[tint][ir][ToProgram] Emit 'else if' instead of 'else { if'

Bug: tint:1902
Change-Id: If49a7fd71d72cd562fc49957a5715ea7eefc8b4d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/133140
Kokoro: Ben Clayton <bclayton@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/ir/to_program.cc b/src/tint/ir/to_program.cc
index a59146e..972f526 100644
--- a/src/tint/ir/to_program.cc
+++ b/src/tint/ir/to_program.cc
@@ -116,10 +116,27 @@
     const ast::IfStatement* If(const ir::If* i) {
         auto* cond = Expr(i->condition);
         auto* t = FlowNodeGraph(i->true_.target, i->merge.target);
+        if (!t) {
+            return nullptr;
+        }
         if (!IsEmpty(i->false_.target, i->merge.target)) {
-            // TODO(crbug.com/tint/1902): Merge if else
-            auto* f = FlowNodeGraph(i->false_.target, i->merge.target);
-            return b.If(cond, t, b.Else(f));
+            // If the else target is an if flow node with the same merge target as this if, then
+            // emit an 'else if' instead of a block statement for the else.
+            if (auto* else_if = As<ir::If>(NextNonEmptyNode(i->false_.target));
+                else_if &&
+                NextNonEmptyNode(i->merge.target) == NextNonEmptyNode(else_if->merge.target)) {
+                auto* f = If(else_if);
+                if (!f) {
+                    return nullptr;
+                }
+                return b.If(cond, t, b.Else(f));
+            } else {
+                auto* f = FlowNodeGraph(i->false_.target, i->merge.target);
+                if (!f) {
+                    return nullptr;
+                }
+                return b.If(cond, t, b.Else(f));
+            }
         }
         return b.If(cond, t);
     }
@@ -139,6 +156,21 @@
         return true;
     }
 
+    /// @return the next flow node that isn't an empty block
+    const ir::FlowNode* NextNonEmptyNode(const ir::FlowNode* node) {
+        while (node) {
+            if (auto* block = node->As<ir::Block>()) {
+                if (block->instructions.Length() > 0) {
+                    return node;
+                }
+                node = block->branch.target;
+            } else {
+                return node;
+            }
+        }
+        return nullptr;
+    }
+
     const ast::Statement* Stmt(const ir::Instruction* inst) {
         return Switch(
             inst,                                            //
diff --git a/src/tint/ir/to_program_roundtrip_test.cc b/src/tint/ir/to_program_roundtrip_test.cc
index e62646c..dd04ab3 100644
--- a/src/tint/ir/to_program_roundtrip_test.cc
+++ b/src/tint/ir/to_program_roundtrip_test.cc
@@ -152,28 +152,6 @@
     b();
   }
 }
-)",
-         R"(
-fn a() {
-}
-
-fn b() {
-}
-
-fn c() {
-}
-
-fn f() {
-  var cond_a : bool = true;
-  var cond_b : bool = true;
-  if (cond_a) {
-    a();
-  } else {
-    if (cond_b) {
-      b();
-    }
-  }
-}
 )");
 }
 }  // namespace