diff --git a/clang/lib/CodeGen/CGLoopInfo.h b/clang/lib/CodeGen/CGLoopInfo.h --- a/clang/lib/CodeGen/CGLoopInfo.h +++ b/clang/lib/CodeGen/CGLoopInfo.h @@ -29,6 +29,7 @@ namespace clang { class Attr; class ASTContext; +class CodeGenOptions; namespace CodeGen { /// Attributes that may be specified on loops. @@ -202,6 +203,7 @@ /// Begin a new structured loop. Stage attributes from the Attrs list. /// The staged attributes are applied to the loop and then cleared. void push(llvm::BasicBlock *Header, clang::ASTContext &Ctx, + const clang::CodeGenOptions &CGOpts, llvm::ArrayRef Attrs, const llvm::DebugLoc &StartLoc, const llvm::DebugLoc &EndLoc); diff --git a/clang/lib/CodeGen/CGLoopInfo.cpp b/clang/lib/CodeGen/CGLoopInfo.cpp --- a/clang/lib/CodeGen/CGLoopInfo.cpp +++ b/clang/lib/CodeGen/CGLoopInfo.cpp @@ -10,6 +10,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/Expr.h" +#include "clang/Basic/CodeGenOptions.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/CFG.h" #include "llvm/IR/Constants.h" @@ -573,6 +574,7 @@ } void LoopInfoStack::push(BasicBlock *Header, clang::ASTContext &Ctx, + const clang::CodeGenOptions &CGOpts, ArrayRef Attrs, const llvm::DebugLoc &StartLoc, const llvm::DebugLoc &EndLoc) { @@ -753,6 +755,14 @@ } } + if (CGOpts.OptimizationLevel > 0) + // Disable unrolling for the loop, if unrolling is disabled (via + // -fno-unroll-loops) and no pragmas override the decision. + if (!CGOpts.UnrollLoops && + (StagedAttrs.UnrollEnable == LoopAttributes::Unspecified && + StagedAttrs.UnrollCount == 0)) + setUnrollState(LoopAttributes::Disable); + /// Stage the attributes. push(Header, StartLoc, EndLoc); } diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -728,8 +728,8 @@ EmitBlock(LoopHeader.getBlock()); const SourceRange &R = S.getSourceRange(); - LoopStack.push(LoopHeader.getBlock(), CGM.getContext(), WhileAttrs, - SourceLocToDebugLoc(R.getBegin()), + LoopStack.push(LoopHeader.getBlock(), CGM.getContext(), CGM.getCodeGenOpts(), + WhileAttrs, SourceLocToDebugLoc(R.getBegin()), SourceLocToDebugLoc(R.getEnd())); // Create an exit block for when the condition fails, which will @@ -830,7 +830,7 @@ EmitBlock(LoopCond.getBlock()); const SourceRange &R = S.getSourceRange(); - LoopStack.push(LoopBody, CGM.getContext(), DoAttrs, + LoopStack.push(LoopBody, CGM.getContext(), CGM.getCodeGenOpts(), DoAttrs, SourceLocToDebugLoc(R.getBegin()), SourceLocToDebugLoc(R.getEnd())); @@ -888,7 +888,7 @@ EmitBlock(CondBlock); const SourceRange &R = S.getSourceRange(); - LoopStack.push(CondBlock, CGM.getContext(), ForAttrs, + LoopStack.push(CondBlock, CGM.getContext(), CGM.getCodeGenOpts(), ForAttrs, SourceLocToDebugLoc(R.getBegin()), SourceLocToDebugLoc(R.getEnd())); @@ -989,7 +989,7 @@ EmitBlock(CondBlock); const SourceRange &R = S.getSourceRange(); - LoopStack.push(CondBlock, CGM.getContext(), ForAttrs, + LoopStack.push(CondBlock, CGM.getContext(), CGM.getCodeGenOpts(), ForAttrs, SourceLocToDebugLoc(R.getBegin()), SourceLocToDebugLoc(R.getEnd())); diff --git a/clang/test/CodeGenCXX/fno-unroll-loops-metadata.cpp b/clang/test/CodeGenCXX/fno-unroll-loops-metadata.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/fno-unroll-loops-metadata.cpp @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin -std=c++11 -emit-llvm -o - %s -O0 -disable-llvm-optzns -fno-unroll-loops | FileCheck --check-prefix=NO_UNROLL_MD %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -std=c++11 -emit-llvm -o - %s -O1 -disable-llvm-optzns -fno-unroll-loops | FileCheck --check-prefix=UNROLL_DISABLED_MD %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -std=c++11 -emit-llvm -o - %s -O2 -disable-llvm-optzns -fno-unroll-loops | FileCheck --check-prefix=UNROLL_DISABLED_MD %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -std=c++11 -emit-llvm -o - %s -O3 -disable-llvm-optzns -fno-unroll-loops | FileCheck --check-prefix=UNROLL_DISABLED_MD %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -std=c++11 -emit-llvm -o - %s -O3 -disable-llvm-optzns | FileCheck --check-prefix=NO_UNROLL_MD %s + +// NO_UNROLL_MD-NOT: llvm.loop + +// Verify unroll.disable metadata is added to while loop with -fno-unroll-loops +// and optlevel > 0. +void while_test(int *List, int Length) { + // UNROLL_DISABLED_MD: define {{.*}} @_Z10while_test + int i = 0; + + while (i < Length) { + // UNROLL_DISABLED_MD: br label {{.*}}, !llvm.loop ![[LOOP_1:.*]] + List[i] = i * 2; + i++; + } +} + +// Verify unroll.disable metadata is added to do-while loop with +// -fno-unroll-loops and optlevel > 0. +void do_test(int *List, int Length) { + // UNROLL_DISABLED_MD: define {{.*}} @_Z7do_test + int i = 0; + + do { + // UNROLL_DISABLED_MD: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_2:.*]] + List[i] = i * 2; + i++; + } while (i < Length); +} + +// Verify unroll.disable metadata is added to while loop with -fno-unroll-loops +// and optlevel > 0. +void for_test(int *List, int Length) { + // UNROLL_DISABLED_MD: define {{.*}} @_Z8for_test + for (int i = 0; i < Length; i++) { + // UNROLL_DISABLED_MD: br label {{.*}}, !llvm.loop ![[LOOP_3:.*]] + List[i] = i * 2; + } +} + +// UNROLL_DISABLED_MD: ![[LOOP_1]] = distinct !{![[LOOP_1]], ![[UNROLL_DISABLE:.*]]} +// UNROLL_DISABLED_MD: ![[UNROLL_DISABLE]] = !{!"llvm.loop.unroll.disable"} +// UNROLL_DISABLED_MD: ![[LOOP_2]] = distinct !{![[LOOP_2:.*]], ![[UNROLL_DISABLE:.*]]} +// UNROLL_DISABLED_MD: ![[LOOP_3]] = distinct !{![[LOOP_3]], ![[UNROLL_DISABLE:.*]]} diff --git a/clang/test/CodeGenCXX/pragma-unroll.cpp b/clang/test/CodeGenCXX/pragma-unroll.cpp --- a/clang/test/CodeGenCXX/pragma-unroll.cpp +++ b/clang/test/CodeGenCXX/pragma-unroll.cpp @@ -1,5 +1,8 @@ // RUN: %clang_cc1 -triple x86_64-apple-darwin -std=c++11 -emit-llvm -o - %s | FileCheck %s +// Check that passing -fno-unroll-loops does not impact the decision made using pragmas. +// RUN: %clang_cc1 -triple x86_64-apple-darwin -std=c++11 -emit-llvm -o - -O1 -disable-llvm-optzns -fno-unroll-loops %s | FileCheck %s + // Verify while loop is recognized after unroll pragma. void while_test(int *List, int Length) { // CHECK: define {{.*}} @_Z10while_test