diff --git a/clang/docs/ClangCommandLineReference.rst b/clang/docs/ClangCommandLineReference.rst --- a/clang/docs/ClangCommandLineReference.rst +++ b/clang/docs/ClangCommandLineReference.rst @@ -2285,6 +2285,10 @@ Turn on loop unroller +.. option:: -ffinite-loops, -fno-finite-loops + +Assume that all loops terminate or never assume that they do. This takes precedence over the language standard. + .. option:: -funsafe-math-optimizations, -fno-unsafe-math-optimizations .. option:: -funsigned-bitfields diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -134,6 +134,12 @@ All, // Keep all frame pointers. }; + enum FiniteLoopsKind { + Default, // Follow the language standard + Enforce, // Enforce finite loops + NeverEnforce, // Never enforce finite loops + }; + /// The code model to use (-mcmodel). std::string CodeModel; diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -260,6 +260,7 @@ ///< traced by time profiler CODEGENOPT(UnrollLoops , 1, 0) ///< Control whether loops are unrolled. CODEGENOPT(RerollLoops , 1, 0) ///< Control whether loops are rerolled. +CODEGENOPT(FiniteLoops , 2, 0) ///< Control whether loops are assumed to terminate. CODEGENOPT(NoUseJumpTables , 1, 0) ///< Set when -fno-jump-tables is enabled. CODEGENOPT(UnwindTables , 1, 0) ///< Emit unwind tables. CODEGENOPT(VectorizeLoop , 1, 0) ///< Run loop vectorizer. diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2402,6 +2402,8 @@ defm reroll_loops : BoolFOption<"reroll-loops", "CodeGenOpts.RerollLoops", DefaultsToFalse, ChangedBy, ResetBy>; +def ffinite_loops : Flag<["-"], "ffinite-loops">, Group, HelpText<"Assume that loops terminate">, Flags<[CC1Option]>; +def fno_finite_loops : Flag<["-"], "fno-finite-loops">, Group, HelpText<"Don't assume that loops terminate">, Flags<[CC1Option]>; def ftrigraphs : Flag<["-"], "ftrigraphs">, Group, HelpText<"Process trigraph sequences">, Flags<[CC1Option]>; def fno_trigraphs : Flag<["-"], "fno-trigraphs">, Group, 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 @@ -804,6 +804,16 @@ } else if (LanguageRequiresProgress()) LoopMustProgress = true; + if (CGM.getCodeGenOpts().FiniteLoops == + CodeGenOptions::FiniteLoopsKind::Enforce) { + FnIsMustProgress = true; + LoopMustProgress = true; + } else if (CGM.getCodeGenOpts().FiniteLoops == + CodeGenOptions::FiniteLoopsKind::NeverEnforce) { + FnIsMustProgress = false; + LoopMustProgress = false; + } + const SourceRange &R = S.getSourceRange(); LoopStack.push(LoopHeader.getBlock(), CGM.getContext(), CGM.getCodeGenOpts(), WhileAttrs, SourceLocToDebugLoc(R.getBegin()), @@ -907,6 +917,16 @@ } else if (LanguageRequiresProgress()) LoopMustProgress = true; + if (CGM.getCodeGenOpts().FiniteLoops == + CodeGenOptions::FiniteLoopsKind::Enforce) { + FnIsMustProgress = true; + LoopMustProgress = true; + } else if (CGM.getCodeGenOpts().FiniteLoops == + CodeGenOptions::FiniteLoopsKind::NeverEnforce) { + FnIsMustProgress = false; + LoopMustProgress = false; + } + const SourceRange &R = S.getSourceRange(); LoopStack.push(LoopBody, CGM.getContext(), CGM.getCodeGenOpts(), DoAttrs, SourceLocToDebugLoc(R.getBegin()), @@ -959,6 +979,14 @@ LoopMustProgress = true; } + if (CGM.getCodeGenOpts().FiniteLoops == + CodeGenOptions::FiniteLoopsKind::Enforce) { + LoopMustProgress = true; + } else if (CGM.getCodeGenOpts().FiniteLoops == + CodeGenOptions::FiniteLoopsKind::NeverEnforce) { + LoopMustProgress = false; + } + const SourceRange &R = S.getSourceRange(); LoopStack.push(CondBlock, CGM.getContext(), CGM.getCodeGenOpts(), ForAttrs, SourceLocToDebugLoc(R.getBegin()), @@ -1017,6 +1045,14 @@ } incrementProfileCounter(&S); + if (CGM.getCodeGenOpts().FiniteLoops == + CodeGenOptions::FiniteLoopsKind::Enforce) { + FnIsMustProgress = true; + } else if (CGM.getCodeGenOpts().FiniteLoops == + CodeGenOptions::FiniteLoopsKind::NeverEnforce) { + FnIsMustProgress = false; + } + { // Create a separate cleanup scope for the body, in case it is not // a compound statement. diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5587,8 +5587,9 @@ Args.AddLastArg(CmdArgs, options::OPT_fwritable_strings); Args.AddLastArg(CmdArgs, options::OPT_funroll_loops, options::OPT_fno_unroll_loops); - Args.AddLastArg(CmdArgs, options::OPT_pthread); + Args.AddLastArg(CmdArgs, options::OPT_ffinite_loops, + options::OPT_fno_finite_loops); if (Args.hasFlag(options::OPT_mspeculative_load_hardening, options::OPT_mno_speculative_load_hardening, false)) diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -981,6 +981,11 @@ Opts.UnrollLoops = Args.hasFlag(OPT_funroll_loops, OPT_fno_unroll_loops, (Opts.OptimizationLevel > 1)); + Opts.FiniteLoops = Args.hasArg(OPT_ffinite_loops) + ? CodeGenOptions::FiniteLoopsKind::Enforce + : (Args.hasArg(OPT_fno_finite_loops) + ? CodeGenOptions::FiniteLoopsKind::NeverEnforce + : CodeGenOptions::FiniteLoopsKind::Default); Opts.DebugNameTable = static_cast( Args.hasArg(OPT_ggnu_pubnames) diff --git a/clang/test/CodeGen/finite-loops.c b/clang/test/CodeGen/finite-loops.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/finite-loops.c @@ -0,0 +1,99 @@ +// RUN: %clang_cc1 -triple x86_64 -S -emit-llvm -std=c99 -o - %s | FileCheck %s -check-prefix=CHECK-C99-LOOPS +// RUN: %clang_cc1 -triple x86_64 -S -emit-llvm -std=c99 -ffinite-loops -o - %s | FileCheck %s -check-prefix=CHECK-FINITE-LOOPS +// RUN: %clang_cc1 -triple x86_64 -S -emit-llvm -std=c99 -fno-finite-loops -o - %s | FileCheck %s -check-prefix=CHECK-INFINITE-LOOPS +// RUN: %clang_cc1 -triple x86_64 -S -emit-llvm -std=c11 -o - %s | FileCheck %s -check-prefix=CHECK-C11-LOOPS +// RUN: %clang_cc1 -triple x86_64 -S -emit-llvm -std=c11 -ffinite-loops -o - %s | FileCheck %s -check-prefix=CHECK-FINITE-LOOPS +// RUN: %clang_cc1 -triple x86_64 -S -emit-llvm -std=c11 -fno-finite-loops -o - %s | FileCheck %s -check-prefix=CHECK-INFINITE-LOOPS + +// CHECK-C99-LOOPS: Function Attrs: noinline nounwind optnone +// CHECK-C99-LOOPS-LABEL: @f( +// CHECK-C99-LOOPS-NOT: {{.}} !llvm.loop ! +// +// CHECK-FINITE-LOOPS: Function Attrs: noinline nounwind optnone mustprogress +// CHECK-FINITE-LOOPS-LABEL: @f( +// CHECK-FINITE-LOOPS: {{.}} !llvm.loop ! +// CHECK-FINITE-LOOPS: {{.}} !llvm.loop ! +// +// CHECK-INFINITE-LOOPS: Function Attrs: noinline nounwind optnone +// CHECK-INFINITE-LOOPS-LABEL: @f( +// CHECK-INFINITE-LOOPS-NOT: {{.}} !llvm.loop ! +// +// CHECK-C11-LOOPS: Function Attrs: noinline nounwind optnone +// CHECK-C11-LOOPS-LABEL: @f( +// CHECK-C11-LOOPS: {{.}} !llvm.loop ! +// CHECK-C11-LOOPS-NOT: {{.}} !llvm.loop ! +// +int f(int a, int b) { + for (; a != b; ) { + if (a == b) + return 1; + } + + for (;;) { + if (a != b) + return 1; + } + return 0; +} + +// CHECK-C99-LOOPS: Function Attrs: noinline nounwind optnone +// CHECK-C99-LOOPS-LABEL: @dw( +// CHECK-C99-LOOPS-NOT: {{.}} !llvm.loop ! +// +// CHECK-FINITE-LOOPS: Function Attrs: noinline nounwind optnone mustprogress +// CHECK-FINITE-LOOPS-LABEL: @dw( +// CHECK-FINITE-LOOPS: {{.}} !llvm.loop ! +// CHECK-FINITE-LOOPS: {{.}} !llvm.loop ! +// +// CHECK-INFINITE-LOOPS: Function Attrs: noinline nounwind optnone +// CHECK-INFINITE-LOOPS-LABEL: @dw( +// CHECK-INFINITE-LOOPS-NOT: {{.}} !llvm.loop ! +// +// CHECK-C11-LOOPS: Function Attrs: noinline nounwind optnone +// CHECK-C11-LOOPS-LABEL: @dw( +// CHECK-C11-LOOPS: {{.}} !llvm.loop ! +// CHECK-C11-LOOPS-NOT: {{.}} !llvm.loop ! +// +int dw(int a, int b) { + do { + if (a == b) + return 1; + } while (a != b); + + do { + if (a != b) + return 1; + } while (1); + return 0; +} + +// CHECK-C99-LOOPS: Function Attrs: noinline nounwind optnone +// CHECK-C99-LOOPS-LABEL: @w( +// CHECK-C99-LOOPS-NOT: {{.}} !llvm.loop ! +// +// CHECK-FINITE-LOOPS: Function Attrs: noinline nounwind optnone mustprogress +// CHECK-FINITE-LOOPS-LABEL: @w( +// CHECK-FINITE-LOOPS: {{.}} !llvm.loop ! +// CHECK-FINITE-LOOPS: {{.}} !llvm.loop ! +// +// CHECK-INFINITE-LOOPS: Function Attrs: noinline nounwind optnone +// CHECK-INFINITE-LOOPS-LABEL: @w( +// CHECK-INFINITE-LOOPS-NOT: {{.}} !llvm.loop ! +// +// CHECK-C11-LOOPS: Function Attrs: noinline nounwind optnone +// CHECK-C11-LOOPS-LABEL: @w( +// CHECK-C11-LOOPS: {{.}} !llvm.loop ! +// CHECK-C11-LOOPS-NOT: {{.}} !llvm.loop ! +// +int w(int a, int b) { + while(a != b) { + if (a == b) return 2; + } + + while(1) { + if (a != b) + return 1; + } + return 0; +} + diff --git a/clang/test/CodeGenCXX/finite-loops.cpp b/clang/test/CodeGenCXX/finite-loops.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/finite-loops.cpp @@ -0,0 +1,99 @@ +// RUN: %clang_cc1 -triple x86_64 -S -emit-llvm -std=c++03 -o - %s | FileCheck %s -check-prefix=CHECK-CPP03-LOOPS +// RUN: %clang_cc1 -triple x86_64 -S -emit-llvm -std=c++03 -ffinite-loops -o - %s | FileCheck %s -check-prefix=CHECK-FINITE-LOOPS +// RUN: %clang_cc1 -triple x86_64 -S -emit-llvm -std=c++03 -fno-finite-loops -o - %s | FileCheck %s -check-prefix=CHECK-INFINITE-LOOPS +// RUN: %clang_cc1 -triple x86_64 -S -emit-llvm -std=c++11 -o - %s | FileCheck %s -check-prefix=CHECK-CPP11-LOOPS +// RUN: %clang_cc1 -triple x86_64 -S -emit-llvm -std=c++11 -ffinite-loops -o - %s | FileCheck %s -check-prefix=CHECK-FINITE-LOOPS +// RUN: %clang_cc1 -triple x86_64 -S -emit-llvm -std=c++11 -fno-finite-loops -o - %s | FileCheck %s -check-prefix=CHECK-INFINITE-LOOPS + +// CHECK-CPP03-LOOPS: Function Attrs: noinline nounwind optnone +// CHECK-CPP03-LOOPS-LABEL: @_Z1fii( +// CHECK-CPP03-LOOPS-NOT: {{.*}} !llvm.loop ! +// +// CHECK-FINITE-LOOPS: Function Attrs: noinline nounwind optnone mustprogress +// CHECK-FINITE-LOOPS-LABEL: @_Z1fii( +// CHECK-FINITE-LOOPS: {{.*}} !llvm.loop ! +// CHECK-FINITE-LOOPS: {{.*}} !llvm.loop ! +// +// CHECK-INFINITE-LOOPS: Function Attrs: noinline nounwind optnone +// CHECK-INFINITE-LOOPS-LABEL: @_Z1fii( +// CHECK-INFINITE-LOOPS-NOT: {{.*}} !llvm.loop ! +// +// CHECK-CPP11-LOOPS: Function Attrs: noinline nounwind optnone +// CHECK-CPP11-LOOPS-LABEL: @_Z1fii( +// CHECK-CPP11-LOOPS: {{.*}} !llvm.loop ! +// CHECK-CPP11-LOOPS: {{.*}} !llvm.loop ! +// +int f(int a, int b) { + for (; a != b; ) { + if (a == b) + return 1; + } + + for (;;) { + if (a != b) + return 1; + } + return 0; +} + +// CHECK-CPP03-LOOPS: Function Attrs: noinline nounwind optnone +// CHECK-CPP03-LOOPS-LABEL: @_Z2dwii( +// CHECK-CPP03-LOOPS-NOT: {{.*}} !llvm.loop ! +// +// CHECK-FINITE-LOOPS: Function Attrs: noinline nounwind optnone mustprogress +// CHECK-FINITE-LOOPS-LABEL: @_Z2dwii( +// CHECK-FINITE-LOOPS: {{.*}} !llvm.loop ! +// CHECK-FINITE-LOOPS: {{.*}} !llvm.loop ! +// +// CHECK-INFINITE-LOOPS: Function Attrs: noinline nounwind optnone +// CHECK-INFINITE-LOOPS-LABEL: @_Z2dwii( +// CHECK-INFINITE-LOOPS-NOT: {{.*}} !llvm.loop ! +// +// CHECK-CPP11-LOOPS: Function Attrs: noinline nounwind optnone +// CHECK-CPP11-LOOPS-LABEL: @_Z2dwii( +// CHECK-CPP11-LOOPS: {{.*}} !llvm.loop ! +// CHECK-CPP11-LOOPS: {{.*}} !llvm.loop ! +// +int dw(int a, int b) { + do { + if (a == b) + return 1; + } while (a != b); + + do { + if (a != b) + return 1; + } while (1); + return 0; +} + +// CHECK-CPP03-LOOPS: Function Attrs: noinline nounwind optnone +// CHECK-CPP03-LOOPS-LABEL: @_Z1wii( +// CHECK-CPP03-LOOPS-NOT: {{.*}} !llvm.loop ! +// +// CHECK-FINITE-LOOPS: Function Attrs: noinline nounwind optnone mustprogress +// CHECK-FINITE-LOOPS-LABEL: @_Z1wii( +// CHECK-FINITE-LOOPS: {{.*}} !llvm.loop ! +// CHECK-FINITE-LOOPS: {{.*}} !llvm.loop ! +// +// CHECK-INFINITE-LOOPS: Function Attrs: noinline nounwind optnone +// CHECK-INFINITE-LOOPS-LABEL: @_Z1wii( +// CHECK-INFINITE-LOOPS-NOT: {{.*}} !llvm.loop ! +// +// CHECK-CPP11-LOOPS: Function Attrs: noinline nounwind optnone +// CHECK-CPP11-LOOPS-LABEL: @_Z1wii( +// CHECK-CPP11-LOOPS: {{.*}} !llvm.loop ! +// CHECK-CPP11-LOOPS: {{.*}} !llvm.loop ! +// +int w(int a, int b) { + while(a != b) { + if (a == b) return 2; + } + + while(1) { + if (a != b) + return 1; + } + return 0; +} + diff --git a/clang/test/Driver/clang_f_opts.c b/clang/test/Driver/clang_f_opts.c --- a/clang/test/Driver/clang_f_opts.c +++ b/clang/test/Driver/clang_f_opts.c @@ -52,6 +52,15 @@ // CHECK-REROLL-LOOPS: "-freroll-loops" // CHECK-NO-REROLL-LOOPS-NOT: "-freroll-loops" +// RUN: %clang -### -S -ffinite-loops %s 2>&1 | FileCheck -check-prefix=CHECK-FINITE-LOOPS %s +// RUN: %clang -### -S -fno-finite-loops %s 2>&1 | FileCheck -check-prefix=CHECK-NO-FINITE-LOOPS %s +// RUN: %clang -### -S -fno-finite-loops -ffinite-loops %s 2>&1 | FileCheck -check-prefix=CHECK-FINITE-LOOPS %s +// RUN: %clang -### -S -ffinite-loops -fno-finite-loops %s 2>&1 | FileCheck -check-prefix=CHECK-NO-FINITE-LOOPS %s +// CHECK-FINITE-LOOPS: "-ffinite-loops" +// CHECK-FINITE-LOOPS-NOT: "-fno-finite-loops" +// CHECK-NO-FINITE-LOOPS: "-fno-finite-loops" +// CHECK-NO-FINITE-LOOPS-NOT: "-ffinite-loops" + // RUN: %clang -### -S -fprofile-sample-accurate %s 2>&1 | FileCheck -check-prefix=CHECK-PROFILE-SAMPLE-ACCURATE %s // CHECK-PROFILE-SAMPLE-ACCURATE: "-fprofile-sample-accurate" @@ -292,6 +301,7 @@ // RUN: -malign-functions=100 \ // RUN: -malign-loops=100 \ // RUN: -malign-jumps=100 \ +// RUN: -ffinite-loops -fno-finite-loops \ // RUN: %s 2>&1 | FileCheck --check-prefix=IGNORE %s // IGNORE-NOT: error: unknown argument