Generate performance warnings in HLSL translation

Generate performance warnings for some code that undergoes heavy
emulation when translated to HLSL:
1. Dynamic indexing of vectors and matrices.
2. Non-empty fall-through cases in switch/case.

The warnings are generated only when code is translated to HLSL.
Generating them in the parsing stage would add too much maintenance
burden.

Improves switch statement fall-through handling in cases where an
empty fall-through case follows a non-empty one so that extra
performance warnings are not generated.

BUG=angleproject:1116

Change-Id: I7c85d78fe7c4f8e6042bda72ceaaf6e37dadfe6c
Reviewed-on: https://chromium-review.googlesource.com/732986
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/compiler/translator/RemoveSwitchFallThrough.cpp b/src/compiler/translator/RemoveSwitchFallThrough.cpp
index 10da1df..ab2045d 100644
--- a/src/compiler/translator/RemoveSwitchFallThrough.cpp
+++ b/src/compiler/translator/RemoveSwitchFallThrough.cpp
@@ -10,6 +10,7 @@
 
 #include "compiler/translator/RemoveSwitchFallThrough.h"
 
+#include "compiler/translator/Diagnostics.h"
 #include "compiler/translator/IntermTraverse.h"
 
 namespace sh
@@ -21,10 +22,12 @@
 class RemoveSwitchFallThroughTraverser : public TIntermTraverser
 {
   public:
-    static TIntermBlock *removeFallThrough(TIntermBlock *statementList);
+    static TIntermBlock *removeFallThrough(TIntermBlock *statementList,
+                                           PerformanceDiagnostics *perfDiagnostics);
 
   private:
-    RemoveSwitchFallThroughTraverser(TIntermBlock *statementList);
+    RemoveSwitchFallThroughTraverser(TIntermBlock *statementList,
+                                     PerformanceDiagnostics *perfDiagnostics);
 
     void visitSymbol(TIntermSymbol *node) override;
     void visitConstantUnion(TIntermConstantUnion *node) override;
@@ -49,11 +52,14 @@
     bool mLastStatementWasBreak;
     TIntermBlock *mPreviousCase;
     std::vector<TIntermBlock *> mCasesSharingBreak;
+    PerformanceDiagnostics *mPerfDiagnostics;
 };
 
-TIntermBlock *RemoveSwitchFallThroughTraverser::removeFallThrough(TIntermBlock *statementList)
+TIntermBlock *RemoveSwitchFallThroughTraverser::removeFallThrough(
+    TIntermBlock *statementList,
+    PerformanceDiagnostics *perfDiagnostics)
 {
-    RemoveSwitchFallThroughTraverser rm(statementList);
+    RemoveSwitchFallThroughTraverser rm(statementList, perfDiagnostics);
     ASSERT(statementList);
     statementList->traverse(&rm);
     ASSERT(rm.mPreviousCase || statementList->getSequence()->empty());
@@ -69,11 +75,14 @@
     return rm.mStatementListOut;
 }
 
-RemoveSwitchFallThroughTraverser::RemoveSwitchFallThroughTraverser(TIntermBlock *statementList)
+RemoveSwitchFallThroughTraverser::RemoveSwitchFallThroughTraverser(
+    TIntermBlock *statementList,
+    PerformanceDiagnostics *perfDiagnostics)
     : TIntermTraverser(true, false, false),
       mStatementList(statementList),
       mLastStatementWasBreak(false),
-      mPreviousCase(nullptr)
+      mPreviousCase(nullptr),
+      mPerfDiagnostics(perfDiagnostics)
 {
     mStatementListOut = new TIntermBlock();
 }
@@ -158,14 +167,10 @@
         mCasesSharingBreak.push_back(mPreviousCase);
     if (mLastStatementWasBreak)
     {
-        bool labelsWithNoStatements = true;
         for (size_t i = 0; i < mCasesSharingBreak.size(); ++i)
         {
-            if (mCasesSharingBreak.at(i)->getSequence()->size() > 1)
-            {
-                labelsWithNoStatements = false;
-            }
-            if (labelsWithNoStatements)
+            ASSERT(!mCasesSharingBreak.at(i)->getSequence()->empty());
+            if (mCasesSharingBreak.at(i)->getSequence()->size() == 1)
             {
                 // Fall-through is allowed in case the label has no statements.
                 outputSequence(mCasesSharingBreak.at(i)->getSequence(), 0);
@@ -173,6 +178,13 @@
             else
             {
                 // Include all the statements that this case can fall through under the same label.
+                if (mCasesSharingBreak.size() > i + 1u)
+                {
+                    mPerfDiagnostics->warning(mCasesSharingBreak.at(i)->getLine(),
+                                              "Performance: non-empty fall-through cases in "
+                                              "switch statements generate extra code.",
+                                              "switch");
+                }
                 for (size_t j = i; j < mCasesSharingBreak.size(); ++j)
                 {
                     size_t startIndex =
@@ -192,6 +204,7 @@
     handlePreviousCase();
     mPreviousCase = new TIntermBlock();
     mPreviousCase->getSequence()->push_back(node);
+    mPreviousCase->setLine(node->getLine());
     // Don't traverse the condition of the case statement
     return false;
 }
@@ -248,9 +261,10 @@
 
 }  // anonymous namespace
 
-TIntermBlock *RemoveSwitchFallThrough(TIntermBlock *statementList)
+TIntermBlock *RemoveSwitchFallThrough(TIntermBlock *statementList,
+                                      PerformanceDiagnostics *perfDiagnostics)
 {
-    return RemoveSwitchFallThroughTraverser::removeFallThrough(statementList);
+    return RemoveSwitchFallThroughTraverser::removeFallThrough(statementList, perfDiagnostics);
 }
 
 }  // namespace sh