From b549051e33f5cb6b1adc60e64cbd2fc2bf0a7365 Mon Sep 17 00:00:00 2001
From: Lenny Truong <leonardtruong@fb.com>
Date: Thu, 13 Feb 2020 17:06:24 -0800
Subject: [PATCH 1/6] Special handling of wire inlining to preserve
 intermediates

---
 CMakeLists.txt                           |   4 +
 include/coreir/passes/analysis/verilog.h |   3 +
 src/passes/analysis/verilog.cpp          | 102 +++++++++++++++--------
 tests/gtest/golds/arr_tuple.v            |  11 +++
 tests/gtest/golds/bit_wire.v             |   6 ++
 tests/gtest/golds/bits_instance.v        |   9 ++
 tests/gtest/golds/bits_wire.v            |   6 ++
 tests/gtest/golds/fanout.v               |   7 ++
 tests/gtest/golds/nested_array_wire.v    |  10 +++
 tests/gtest/golds/tuple.v                |   7 ++
 tests/gtest/srcs/arr_tuple.json          |  37 ++++++++
 tests/gtest/srcs/bit_wire.json           |  23 +++++
 tests/gtest/srcs/bits_instance.json      |  34 ++++++++
 tests/gtest/srcs/bits_wire.json          |  24 ++++++
 tests/gtest/srcs/fanout.json             |  25 ++++++
 tests/gtest/srcs/fanout.v                |  25 ++++++
 tests/gtest/srcs/nested_array_wire.json  |  72 ++++++++++++++++
 tests/gtest/srcs/tuple.json              |  34 ++++++++
 tests/gtest/test_inline_wires.cpp        |  93 +++++++++++++++++++++
 19 files changed, 498 insertions(+), 34 deletions(-)
 create mode 100644 tests/gtest/golds/arr_tuple.v
 create mode 100644 tests/gtest/golds/bit_wire.v
 create mode 100644 tests/gtest/golds/bits_instance.v
 create mode 100644 tests/gtest/golds/bits_wire.v
 create mode 100644 tests/gtest/golds/fanout.v
 create mode 100644 tests/gtest/golds/nested_array_wire.v
 create mode 100644 tests/gtest/golds/tuple.v
 create mode 100644 tests/gtest/srcs/arr_tuple.json
 create mode 100644 tests/gtest/srcs/bit_wire.json
 create mode 100644 tests/gtest/srcs/bits_instance.json
 create mode 100644 tests/gtest/srcs/bits_wire.json
 create mode 100644 tests/gtest/srcs/fanout.json
 create mode 100644 tests/gtest/srcs/fanout.v
 create mode 100644 tests/gtest/srcs/nested_array_wire.json
 create mode 100644 tests/gtest/srcs/tuple.json
 create mode 100644 tests/gtest/test_inline_wires.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 48686f1b8..9864793b1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -74,3 +74,7 @@ enable_testing()
 add_executable(test_verilog tests/gtest/test_verilog.cpp)
 target_link_libraries(test_verilog gtest_main coreir coreir-commonlib)
 add_test(NAME test_verilog COMMAND test_verilog WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/gtest)
+
+add_executable(test_inline_wires tests/gtest/test_inline_wires.cpp)
+target_link_libraries(test_inline_wires gtest_main coreir coreir-commonlib)
+add_test(NAME test_inline_wires COMMAND test_inline_wires WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/gtest)
diff --git a/include/coreir/passes/analysis/verilog.h b/include/coreir/passes/analysis/verilog.h
index 06f56d985..ce91f5784 100644
--- a/include/coreir/passes/analysis/verilog.h
+++ b/include/coreir/passes/analysis/verilog.h
@@ -28,6 +28,9 @@ class Verilog : public InstanceGraphPass {
   // modules. These parametrized modules have been instanced to create coreir
   // modules, but we only need to compile the verilog definition once
   std::set<Generator *> verilog_generators_seen;
+  
+  // Keep track of wire primitive instances, we do not inline these
+  std::set<std::string> wires;
 
   void compileModule(Module *module);
 
diff --git a/src/passes/analysis/verilog.cpp b/src/passes/analysis/verilog.cpp
index 2f5a552ac..d519d39c3 100644
--- a/src/passes/analysis/verilog.cpp
+++ b/src/passes/analysis/verilog.cpp
@@ -151,11 +151,16 @@ bool can_inline_unary_op(CoreIR::Module *module, bool _inline) {
 
 std::unique_ptr<vAST::StructuralStatement> inline_unary_op(
     std::pair<std::string, CoreIR::Instance *> instance,
-    std::map<std::string, std::unique_ptr<vAST::Expression>> verilog_connections
-        ) {
+    std::map<std::string, std::unique_ptr<vAST::Expression>>
+        verilog_connections,
+    bool is_wire) {
     UnaryOpReplacer transformer(std::move(verilog_connections["in"]));
+    std::string wire_name = instance.first;
+    if (!is_wire) {
+        wire_name += "_out";
+    }
     return std::make_unique<vAST::ContinuousAssign>(
-        std::make_unique<vAST::Identifier>(instance.first + "_out"),
+        std::make_unique<vAST::Identifier>(wire_name),
         transformer.visit(get_primitive_expr(instance.second)));
 }
 
@@ -321,33 +326,49 @@ process_decl(std::unique_ptr<vAST::Identifier> id, Type *type) {
   return std::move(id);
 }
 
+void make_wire_decl(
+    std::string name, Type *type,
+    std::vector<std::variant<std::unique_ptr<vAST::StructuralStatement>,
+                             std::unique_ptr<vAST::Declaration>>>
+        &wire_declarations) {
+    std::unique_ptr<vAST::Identifier> id =
+        std::make_unique<vAST::Identifier>(name);
+    // Can't find a simple way to "convert" a variant type to a
+    // superset, so we just manually unpack it to call the Wire
+    // constructor
+    std::visit(
+        [&](auto &&arg) -> void {
+            wire_declarations.push_back(
+                std::make_unique<vAST::Wire>(std::move(arg)));
+        },
+        process_decl(std::move(id), type));
+}
+
 // Given a map of instances, return a vector of containing declarations for all
 // the output ports of each instance.  We return a vector of a variant type for
 // compatibility with the module AST node constructor, even though we only ever
 // create Wire nodes.
 std::vector<std::variant<std::unique_ptr<vAST::StructuralStatement>,
                          std::unique_ptr<vAST::Declaration>>>
-declare_connections(std::map<std::string, Instance *> instances) {
+declare_connections(std::map<std::string, Instance *> instances, bool _inline) {
   std::vector<std::variant<std::unique_ptr<vAST::StructuralStatement>,
                            std::unique_ptr<vAST::Declaration>>>
       wire_declarations;
   for (auto instance : instances) {
+    if (instance.second->getModuleRef()->getName() == "wire" && _inline) {
+        // Emit inline wire
+        Type *type =
+            cast<RecordType>(instance.second->getModuleRef()->getType())
+                ->getRecord().at("in");
+        make_wire_decl(instance.first, type, wire_declarations);
+        continue;
+    }
     for (auto port :
          cast<RecordType>(instance.second->getModuleRef()->getType())
              ->getRecord()) {
       if (!port.second->isInput()) {
-        std::unique_ptr<vAST::Identifier> id =
-            std::make_unique<vAST::Identifier>(instance.first + "_" +
-                                               port.first);
-        // Can't find a simple way to "convert" a variant type to a
-        // superset, so we just manually unpack it to call the Wire
-        // constructor
-        std::visit(
-            [&](auto &&arg) -> void {
-              wire_declarations.push_back(
-                  std::make_unique<vAST::Wire>(std::move(arg)));
-            },
-            process_decl(std::move(id), port.second));
+        make_wire_decl(instance.first + "_" + port.first, port.second,
+                       wire_declarations);
       }
     }
   }
@@ -575,11 +596,18 @@ build_connection_map(CoreIR::ModuleDef *definition,
 
 // Join select path fields by "_" (ignoring intial self if present)
 std::variant<std::unique_ptr<vAST::Identifier>, std::unique_ptr<vAST::Index>>
-convert_to_verilog_connection(Wireable *value) {
+convert_to_verilog_connection(Wireable *value, bool _inline) {
   SelectPath select_path = value->getSelectPath();
   if (select_path.front() == "self") {
     select_path.pop_front();
   }
+  Wireable *parent = value->getTopParent();
+  if (_inline && parent->getKind() == Wireable::WK_Instance &&
+      cast<Instance>(parent)->getModuleRef()->getName() == "wire") {
+    // Use instance name as wire name
+    select_path.pop_front();
+    select_path[0] = parent->toString();
+  }
   std::string connection_name = "";
   for (uint i = 0; i < select_path.size(); i++) {
     auto item = select_path[i];
@@ -618,12 +646,12 @@ std::unique_ptr<vAST::Concat>
 convert_non_bulk_connection_to_concat(std::vector<ConnMapEntry> entries,
         std::vector<std::variant<std::unique_ptr<vAST::StructuralStatement>,
                                  std::unique_ptr<vAST::Declaration>>> &body,
-        std::string debug_prefix) {
+        std::string debug_prefix, bool _inline) {
   std::vector<std::unique_ptr<vAST::Expression>> args;
   args.resize(entries.size());
   for (auto entry : entries) {
     std::unique_ptr<vAST::Expression> verilog_conn =
-        convert_to_expression(convert_to_verilog_connection(entry.source));
+        convert_to_expression(convert_to_verilog_connection(entry.source, _inline));
     process_connection_debug_metadata(entry, verilog_conn->toString(), body,
             debug_prefix + "[" + std::to_string(entry.index) + "]");
     args[entry.index] = std::move(verilog_conn);
@@ -638,7 +666,7 @@ void assign_module_outputs(
     RecordType *record_type,
     std::vector<std::variant<std::unique_ptr<vAST::StructuralStatement>,
                              std::unique_ptr<vAST::Declaration>>> &body,
-    std::map<ConnMapKey, std::vector<ConnMapEntry>> connection_map) {
+    std::map<ConnMapKey, std::vector<ConnMapEntry>> connection_map, bool _inline) {
   for (auto port : record_type->getRecord()) {
     if (port.second->isInput()) {
       continue;
@@ -648,12 +676,12 @@ void assign_module_outputs(
       continue;
     } else if (entries.size() > 1) {
       std::unique_ptr<vAST::Concat> concat =
-          convert_non_bulk_connection_to_concat(entries, body, port.first);
+          convert_non_bulk_connection_to_concat(entries, body, port.first, _inline);
       body.push_back(std::make_unique<vAST::ContinuousAssign>(
           std::make_unique<vAST::Identifier>(port.first), std::move(concat)));
     } else {
       std::unique_ptr<vAST::Expression> verilog_conn =
-          convert_to_expression(convert_to_verilog_connection(entries[0].source));
+          convert_to_expression(convert_to_verilog_connection(entries[0].source, _inline));
       process_connection_debug_metadata(entries[0], verilog_conn->toString(), body,
               port.first);
       // Regular (possibly bulk) connection
@@ -669,13 +697,14 @@ void assign_module_outputs(
 void assign_inouts(
     std::vector<Connection> connections,
     std::vector<std::variant<std::unique_ptr<vAST::StructuralStatement>,
-                             std::unique_ptr<vAST::Declaration>>> &body) {
+                             std::unique_ptr<vAST::Declaration>>> &body,
+    bool _inline) {
   for (auto connection : connections) {
     if (connection.first->getType()->isInOut() ||
         connection.second->getType()->isInOut()) {
       body.push_back(std::make_unique<vAST::ContinuousAssign>(
-              convert_to_assign_target(convert_to_verilog_connection(connection.first)),
-              convert_to_expression(convert_to_verilog_connection(connection.second))
+              convert_to_assign_target(convert_to_verilog_connection(connection.first, _inline)),
+              convert_to_expression(convert_to_verilog_connection(connection.second, _inline))
        ));
     };
   };
@@ -688,12 +717,12 @@ std::vector<std::variant<std::unique_ptr<vAST::StructuralStatement>,
                          std::unique_ptr<vAST::Declaration>>>
 compile_module_body(RecordType *module_type,
                     CoreIR::ModuleDef *definition,
-                    bool _inline) {
+                    bool _inline, std::set<std::string> &wires) {
   std::map<std::string, Instance *> instances = definition->getInstances();
 
   std::vector<std::variant<std::unique_ptr<vAST::StructuralStatement>,
                            std::unique_ptr<vAST::Declaration>>>
-      body = declare_connections(instances);
+      body = declare_connections(instances, _inline);
 
   std::map<ConnMapKey, std::vector<ConnMapEntry>> connection_map =
       build_connection_map(definition, instances);
@@ -741,13 +770,13 @@ compile_module_body(RecordType *module_type,
       } else if (entries.size() > 1) {
         std::unique_ptr<vAST::Concat> concat =
             convert_non_bulk_connection_to_concat(entries, body, 
-                instance_name + "." + port.first);
+                instance_name + "." + port.first, _inline);
         verilog_connections.insert(
             std::make_pair(port.first, std::move(concat)));
         // Otherwise we just use the entry in the connection map
       } else {
         std::unique_ptr<vAST::Expression> verilog_conn =
-            convert_to_expression(convert_to_verilog_connection(entries[0].source));
+            convert_to_expression(convert_to_verilog_connection(entries[0].source, _inline));
         process_connection_debug_metadata(entries[0], verilog_conn->toString(), body,
                 instance_name + "." + port.first);
         verilog_connections.insert(std::make_pair(port.first, std::move(verilog_conn)));
@@ -776,7 +805,12 @@ compile_module_body(RecordType *module_type,
     if (can_inline_binary_op(instance_module, _inline)) {
         statement = inline_binary_op(instance, std::move(verilog_connections));
     } else if (can_inline_unary_op(instance_module, _inline)) {
-        statement = inline_unary_op(instance, std::move(verilog_connections));
+        bool is_wire = instance_module->getName() == "wire";
+        if (is_wire) {
+            wires.insert(instance.first);
+        }
+        statement = inline_unary_op(instance, std::move(verilog_connections),
+                                    is_wire);
     } else if (can_inline_mux_op(instance_module, _inline)) {
         statement = inline_mux_op(instance, std::move(verilog_connections));
     } else if (can_inline_const_op(instance_module, _inline)) {
@@ -809,8 +843,8 @@ compile_module_body(RecordType *module_type,
     body.push_back(std::move(statement));
   }
   // Wire the outputs of the module and inout connections
-  assign_module_outputs(module_type, body, connection_map);
-  assign_inouts(definition->getSortedConnections(), body);
+  assign_module_outputs(module_type, body, connection_map, _inline);
+  assign_inouts(definition->getSortedConnections(), body, _inline);
   return body;
 }
 
@@ -899,7 +933,7 @@ void Passes::Verilog::compileModule(Module *module) {
       body;
   if (module->hasDef()) {
       body = compile_module_body(module->getType(), definition,
-                                 this->_inline);
+                                 this->_inline, this->wires);
   }
 
   if (module->getMetaData().count("filename") > 0) { 
@@ -929,7 +963,7 @@ void Passes::Verilog::compileModule(Module *module) {
                                      std::move(parameters));
 
   if (this->_inline) {
-      vAST::AssignInliner transformer;
+      vAST::AssignInliner transformer(this->wires);
       verilog_module = transformer.visit(std::move(verilog_module));
   }
   modules.push_back(std::make_pair(name, std::move(verilog_module)));
diff --git a/tests/gtest/golds/arr_tuple.v b/tests/gtest/golds/arr_tuple.v
new file mode 100644
index 000000000..57aeed117
--- /dev/null
+++ b/tests/gtest/golds/arr_tuple.v
@@ -0,0 +1,11 @@
+// Module `Foo` defined externally
+module Main (input z_0_x, output z_0_y, input z_1_x, output z_1_y);
+wire Foo_inst0_z_0_y;
+wire a_x;
+wire a_y;
+Foo Foo_inst0(.z_0_x(a_y), .z_0_y(Foo_inst0_z_0_y), .z_1_x(z_0_x), .z_1_y(z_1_y));
+assign a_x = Foo_inst0_z_0_y;
+assign a_y = z_0_x;
+assign z_0_y = a_x;
+endmodule
+
diff --git a/tests/gtest/golds/bit_wire.v b/tests/gtest/golds/bit_wire.v
new file mode 100644
index 000000000..74f4b9e43
--- /dev/null
+++ b/tests/gtest/golds/bit_wire.v
@@ -0,0 +1,6 @@
+module Main (input I, output O);
+wire x;
+assign x = I;
+assign O = x;
+endmodule
+
diff --git a/tests/gtest/golds/bits_instance.v b/tests/gtest/golds/bits_instance.v
new file mode 100644
index 000000000..a88ece5cc
--- /dev/null
+++ b/tests/gtest/golds/bits_instance.v
@@ -0,0 +1,9 @@
+// Module `Foo` defined externally
+module Main (input [4:0] I, output [4:0] O);
+wire [4:0] Foo_inst0_O;
+wire [4:0] x;
+Foo Foo_inst0(.I(I), .O(Foo_inst0_O));
+assign x = Foo_inst0_O;
+assign O = x;
+endmodule
+
diff --git a/tests/gtest/golds/bits_wire.v b/tests/gtest/golds/bits_wire.v
new file mode 100644
index 000000000..133f73bca
--- /dev/null
+++ b/tests/gtest/golds/bits_wire.v
@@ -0,0 +1,6 @@
+module Main (input [4:0] I, output [4:0] O);
+wire [4:0] x;
+assign x = I;
+assign O = x;
+endmodule
+
diff --git a/tests/gtest/golds/fanout.v b/tests/gtest/golds/fanout.v
new file mode 100644
index 000000000..1d6249ca0
--- /dev/null
+++ b/tests/gtest/golds/fanout.v
@@ -0,0 +1,7 @@
+module Main (input I, output O0, output O1);
+wire x;
+assign x = I;
+assign O0 = x;
+assign O1 = x;
+endmodule
+
diff --git a/tests/gtest/golds/nested_array_wire.v b/tests/gtest/golds/nested_array_wire.v
new file mode 100644
index 000000000..e8fdb040b
--- /dev/null
+++ b/tests/gtest/golds/nested_array_wire.v
@@ -0,0 +1,10 @@
+module Main (input [4:0] I_0, input [4:0] I_1, input [4:0] I_2, input [4:0] I_3, input [4:0] I_4, output [4:0] O_0, output [4:0] O_1, output [4:0] O_2, output [4:0] O_3, output [4:0] O_4);
+wire [24:0] x;
+assign x = {I_4[4],I_4[3],I_4[2],I_4[1],I_4[0],I_3[4],I_3[3],I_3[2],I_3[1],I_3[0],I_2[4],I_2[3],I_2[2],I_2[1],I_2[0],I_1[4],I_1[3],I_1[2],I_1[1],I_1[0],I_0[4],I_0[3],I_0[2],I_0[1],I_0[0]};
+assign O_0 = {x[4],x[3],x[2],x[1],x[0]};
+assign O_1 = {x[9],x[8],x[7],x[6],x[5]};
+assign O_2 = {x[14],x[13],x[12],x[11],x[10]};
+assign O_3 = {x[19],x[18],x[17],x[16],x[15]};
+assign O_4 = {x[24],x[23],x[22],x[21],x[20]};
+endmodule
+
diff --git a/tests/gtest/golds/tuple.v b/tests/gtest/golds/tuple.v
new file mode 100644
index 000000000..793efe59d
--- /dev/null
+++ b/tests/gtest/golds/tuple.v
@@ -0,0 +1,7 @@
+module Main (input [4:0] I__0, input I__1, output [4:0] O__0, output O__1);
+wire [5:0] x;
+assign x = {I__1,I__0[4],I__0[3],I__0[2],I__0[1],I__0[0]};
+assign O__0 = {x[4],x[3],x[2],x[1],x[0]};
+assign O__1 = x[5];
+endmodule
+
diff --git a/tests/gtest/srcs/arr_tuple.json b/tests/gtest/srcs/arr_tuple.json
new file mode 100644
index 000000000..aff4d1d65
--- /dev/null
+++ b/tests/gtest/srcs/arr_tuple.json
@@ -0,0 +1,37 @@
+{"top":"global.Main",
+"namespaces":{
+  "global":{
+    "modules":{
+      "Foo":{
+        "type":["Record",[
+          ["z",["Array",2,["Record",[["x","BitIn"],["y","Bit"]]]]]
+        ]]
+      },
+      "Main":{
+        "type":["Record",[
+          ["z",["Array",2,["Record",[["x","BitIn"],["y","Bit"]]]]]
+        ]],
+        "instances":{
+          "Foo_inst0":{
+            "modref":"global.Foo"
+          },
+          "a_x":{
+            "modref":"corebit.wire"
+          },
+          "a_y":{
+            "modref":"corebit.wire"
+          }
+        },
+        "connections":[
+          ["a_y.out","Foo_inst0.z.0.x"],
+          ["a_x.in","Foo_inst0.z.0.y"],
+          ["self.z.0.x","Foo_inst0.z.1.x"],
+          ["self.z.1.y","Foo_inst0.z.1.y"],
+          ["self.z.0.y","a_x.out"],
+          ["self.z.0.x","a_y.in"]
+        ]
+      }
+    }
+  }
+}
+}
diff --git a/tests/gtest/srcs/bit_wire.json b/tests/gtest/srcs/bit_wire.json
new file mode 100644
index 000000000..6ef52e97f
--- /dev/null
+++ b/tests/gtest/srcs/bit_wire.json
@@ -0,0 +1,23 @@
+{"top":"global.Main",
+"namespaces":{
+  "global":{
+    "modules":{
+      "Main":{
+        "type":["Record",[
+          ["I","BitIn"],
+          ["O","Bit"]
+        ]],
+        "instances":{
+          "x":{
+            "modref":"corebit.wire"
+          }
+        },
+        "connections":[
+          ["x.in","self.I"],
+          ["x.out","self.O"]
+        ]
+      }
+    }
+  }
+}
+}
diff --git a/tests/gtest/srcs/bits_instance.json b/tests/gtest/srcs/bits_instance.json
new file mode 100644
index 000000000..4b08abe2f
--- /dev/null
+++ b/tests/gtest/srcs/bits_instance.json
@@ -0,0 +1,34 @@
+{"top":"global.Main",
+"namespaces":{
+  "global":{
+    "modules":{
+      "Foo":{
+        "type":["Record",[
+          ["I",["Array",5,"BitIn"]],
+          ["O",["Array",5,"Bit"]]
+        ]]
+      },
+      "Main":{
+        "type":["Record",[
+          ["I",["Array",5,"BitIn"]],
+          ["O",["Array",5,"Bit"]]
+        ]],
+        "instances":{
+          "Foo_inst0":{
+            "modref":"global.Foo"
+          },
+          "x":{
+            "genref":"coreir.wire",
+            "genargs":{"width":["Int",5]}
+          }
+        },
+        "connections":[
+          ["self.I","Foo_inst0.I"],
+          ["x.in","Foo_inst0.O"],
+          ["x.out","self.O"]
+        ]
+      }
+    }
+  }
+}
+}
diff --git a/tests/gtest/srcs/bits_wire.json b/tests/gtest/srcs/bits_wire.json
new file mode 100644
index 000000000..cb1eebe24
--- /dev/null
+++ b/tests/gtest/srcs/bits_wire.json
@@ -0,0 +1,24 @@
+{"top":"global.Main",
+"namespaces":{
+  "global":{
+    "modules":{
+      "Main":{
+        "type":["Record",[
+          ["I",["Array",5,"BitIn"]],
+          ["O",["Array",5,"Bit"]]
+        ]],
+        "instances":{
+          "x":{
+            "genref":"coreir.wire",
+            "genargs":{"width":["Int",5]}
+          }
+        },
+        "connections":[
+          ["x.in","self.I"],
+          ["x.out","self.O"]
+        ]
+      }
+    }
+  }
+}
+}
diff --git a/tests/gtest/srcs/fanout.json b/tests/gtest/srcs/fanout.json
new file mode 100644
index 000000000..3519dae23
--- /dev/null
+++ b/tests/gtest/srcs/fanout.json
@@ -0,0 +1,25 @@
+{"top":"global.Main",
+"namespaces":{
+  "global":{
+    "modules":{
+      "Main":{
+        "type":["Record",[
+          ["I","BitIn"],
+          ["O0","Bit"],
+          ["O1","Bit"]
+        ]],
+        "instances":{
+          "x":{
+            "modref":"corebit.wire"
+          }
+        },
+        "connections":[
+          ["x.in","self.I"],
+          ["x.out","self.O0"],
+          ["x.out","self.O1"]
+        ]
+      }
+    }
+  }
+}
+}
diff --git a/tests/gtest/srcs/fanout.v b/tests/gtest/srcs/fanout.v
new file mode 100644
index 000000000..3519dae23
--- /dev/null
+++ b/tests/gtest/srcs/fanout.v
@@ -0,0 +1,25 @@
+{"top":"global.Main",
+"namespaces":{
+  "global":{
+    "modules":{
+      "Main":{
+        "type":["Record",[
+          ["I","BitIn"],
+          ["O0","Bit"],
+          ["O1","Bit"]
+        ]],
+        "instances":{
+          "x":{
+            "modref":"corebit.wire"
+          }
+        },
+        "connections":[
+          ["x.in","self.I"],
+          ["x.out","self.O0"],
+          ["x.out","self.O1"]
+        ]
+      }
+    }
+  }
+}
+}
diff --git a/tests/gtest/srcs/nested_array_wire.json b/tests/gtest/srcs/nested_array_wire.json
new file mode 100644
index 000000000..36e2bccee
--- /dev/null
+++ b/tests/gtest/srcs/nested_array_wire.json
@@ -0,0 +1,72 @@
+{"top":"global.Main",
+"namespaces":{
+  "global":{
+    "modules":{
+      "Main":{
+        "type":["Record",[
+          ["I",["Array",5,["Array",5,"BitIn"]]],
+          ["O",["Array",5,["Array",5,"Bit"]]]
+        ]],
+        "instances":{
+          "x":{
+            "genref":"coreir.wire",
+            "genargs":{"width":["Int",25]}
+          }
+        },
+        "connections":[
+          ["x.in.0","self.I.0.0"],
+          ["x.in.1","self.I.0.1"],
+          ["x.in.2","self.I.0.2"],
+          ["x.in.3","self.I.0.3"],
+          ["x.in.4","self.I.0.4"],
+          ["x.in.5","self.I.1.0"],
+          ["x.in.6","self.I.1.1"],
+          ["x.in.7","self.I.1.2"],
+          ["x.in.8","self.I.1.3"],
+          ["x.in.9","self.I.1.4"],
+          ["x.in.10","self.I.2.0"],
+          ["x.in.11","self.I.2.1"],
+          ["x.in.12","self.I.2.2"],
+          ["x.in.13","self.I.2.3"],
+          ["x.in.14","self.I.2.4"],
+          ["x.in.15","self.I.3.0"],
+          ["x.in.16","self.I.3.1"],
+          ["x.in.17","self.I.3.2"],
+          ["x.in.18","self.I.3.3"],
+          ["x.in.19","self.I.3.4"],
+          ["x.in.20","self.I.4.0"],
+          ["x.in.21","self.I.4.1"],
+          ["x.in.22","self.I.4.2"],
+          ["x.in.23","self.I.4.3"],
+          ["x.in.24","self.I.4.4"],
+          ["x.out.0","self.O.0.0"],
+          ["x.out.1","self.O.0.1"],
+          ["x.out.2","self.O.0.2"],
+          ["x.out.3","self.O.0.3"],
+          ["x.out.4","self.O.0.4"],
+          ["x.out.5","self.O.1.0"],
+          ["x.out.6","self.O.1.1"],
+          ["x.out.7","self.O.1.2"],
+          ["x.out.8","self.O.1.3"],
+          ["x.out.9","self.O.1.4"],
+          ["x.out.10","self.O.2.0"],
+          ["x.out.11","self.O.2.1"],
+          ["x.out.12","self.O.2.2"],
+          ["x.out.13","self.O.2.3"],
+          ["x.out.14","self.O.2.4"],
+          ["x.out.15","self.O.3.0"],
+          ["x.out.16","self.O.3.1"],
+          ["x.out.17","self.O.3.2"],
+          ["x.out.18","self.O.3.3"],
+          ["x.out.19","self.O.3.4"],
+          ["x.out.20","self.O.4.0"],
+          ["x.out.21","self.O.4.1"],
+          ["x.out.22","self.O.4.2"],
+          ["x.out.23","self.O.4.3"],
+          ["x.out.24","self.O.4.4"]
+        ]
+      }
+    }
+  }
+}
+}
diff --git a/tests/gtest/srcs/tuple.json b/tests/gtest/srcs/tuple.json
new file mode 100644
index 000000000..ae8bd507e
--- /dev/null
+++ b/tests/gtest/srcs/tuple.json
@@ -0,0 +1,34 @@
+{"top":"global.Main",
+"namespaces":{
+  "global":{
+    "modules":{
+      "Main":{
+        "type":["Record",[
+          ["I",["Record",[["_0",["Array",5,"BitIn"]],["_1","BitIn"]]]],
+          ["O",["Record",[["_0",["Array",5,"Bit"]],["_1","Bit"]]]]
+        ]],
+        "instances":{
+          "x":{
+            "genref":"coreir.wire",
+            "genargs":{"width":["Int",6]}
+          }
+        },
+        "connections":[
+          ["x.in.0","self.I._0.0"],
+          ["x.in.1","self.I._0.1"],
+          ["x.in.2","self.I._0.2"],
+          ["x.in.3","self.I._0.3"],
+          ["x.in.4","self.I._0.4"],
+          ["x.in.5","self.I._1"],
+          ["x.out.0","self.O._0.0"],
+          ["x.out.1","self.O._0.1"],
+          ["x.out.2","self.O._0.2"],
+          ["x.out.3","self.O._0.3"],
+          ["x.out.4","self.O._0.4"],
+          ["x.out.5","self.O._1"]
+        ]
+      }
+    }
+  }
+}
+}
diff --git a/tests/gtest/test_inline_wires.cpp b/tests/gtest/test_inline_wires.cpp
new file mode 100644
index 000000000..fda51f2d0
--- /dev/null
+++ b/tests/gtest/test_inline_wires.cpp
@@ -0,0 +1,93 @@
+#include <gtest/gtest.h>
+#include "assert_pass.h"
+#include "coreir.h"
+#include "coreir/definitions/coreVerilog.hpp"
+#include "coreir/definitions/corebitVerilog.hpp"
+
+using namespace CoreIR;
+
+namespace {
+
+void load_file(Context *context, std::string file_name) {
+    Module *top;
+    bool result = loadFromFile(context, file_name, &top);
+    ASSERT_TRUE(result);
+    ASSERT_NE(top, nullptr);
+    context->setTop(top->getRefName());
+}
+
+void check_verilog(Context *context, std::string gold_file) {
+    context->runPasses({"flattentypes", "verilog --inline"}, {});
+    assertPassEq<Passes::Verilog>(context, gold_file);
+}
+
+TEST(VerilogInlineWireTest, TestBitWire) {
+    Context *c = newContext();
+    CoreIRLoadVerilog_corebit(c);
+    load_file(c, "srcs/bit_wire.json");
+
+    check_verilog(c, "golds/bit_wire.v");
+    deleteContext(c);
+}
+
+TEST(VerilogInlineWireTest, TestBitsWire) {
+    Context *c = newContext();
+    CoreIRLoadVerilog_coreir(c);
+    load_file(c, "srcs/bits_wire.json");
+
+    check_verilog(c, "golds/bits_wire.v");
+    deleteContext(c);
+}
+
+TEST(VerilogInlineWireTest, TestNestedArraysWire) {
+    Context *c = newContext();
+    CoreIRLoadVerilog_coreir(c);
+    load_file(c, "srcs/nested_array_wire.json");
+
+    check_verilog(c, "golds/nested_array_wire.v");
+    deleteContext(c);
+}
+
+TEST(VerilogInlineWireTest, TestTupleWire) {
+    Context *c = newContext();
+    CoreIRLoadVerilog_coreir(c);
+    load_file(c, "srcs/tuple.json");
+
+    check_verilog(c, "golds/tuple.v");
+    deleteContext(c);
+}
+
+TEST(VerilogInlineWireTest, TestArrTupleWire) {
+    Context *c = newContext();
+    CoreIRLoadVerilog_corebit(c);
+    CoreIRLoadVerilog_coreir(c);
+    load_file(c, "srcs/arr_tuple.json");
+
+    check_verilog(c, "golds/arr_tuple.v");
+    deleteContext(c);
+}
+
+TEST(VerilogInlineWireTest, TestFanOut) {
+    Context *c = newContext();
+    CoreIRLoadVerilog_corebit(c);
+    load_file(c, "srcs/fanout.json");
+
+    check_verilog(c, "golds/fanout.v");
+    deleteContext(c);
+}
+
+TEST(VerilogInlineWireTest, TestInstance) {
+    Context *c = newContext();
+    CoreIRLoadVerilog_coreir(c);
+    load_file(c, "srcs/bits_instance.json");
+
+    check_verilog(c, "golds/bits_instance.v");
+    deleteContext(c);
+}
+
+}  // namespace
+
+int main(int argc, char **argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}

From 3f972f4457be8a60753b401cd4c2f97eff393122 Mon Sep 17 00:00:00 2001
From: Lenny Truong <leonardtruong@fb.com>
Date: Thu, 13 Feb 2020 17:14:09 -0800
Subject: [PATCH 2/6] Check verilogAST branch

---
 CMakeLists.txt.in | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CMakeLists.txt.in b/CMakeLists.txt.in
index 98d3d225e..13b3d6802 100644
--- a/CMakeLists.txt.in
+++ b/CMakeLists.txt.in
@@ -7,7 +7,7 @@ project(verilogAST-download NONE)
 include(ExternalProject)
 ExternalProject_Add(verilogAST
   GIT_REPOSITORY    https://github.com/leonardt/verilogAST-cpp.git
-  GIT_TAG           master
+  GIT_TAG           inline-wire-blacklist
   SOURCE_DIR        "${CMAKE_CURRENT_BINARY_DIR}/verilogAST-src"
   BINARY_DIR        "${CMAKE_CURRENT_BINARY_DIR}/verilogAST-build"
   CONFIGURE_COMMAND ""

From 0d73963f253dc0e3e9989f7901da1f3f617d257c Mon Sep 17 00:00:00 2001
From: Lenny Truong <leonardtruong@fb.com>
Date: Fri, 21 Feb 2020 15:42:33 -0800
Subject: [PATCH 3/6] Add todo

---
 src/passes/analysis/verilog.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/passes/analysis/verilog.cpp b/src/passes/analysis/verilog.cpp
index 3ba157b86..092369758 100644
--- a/src/passes/analysis/verilog.cpp
+++ b/src/passes/analysis/verilog.cpp
@@ -840,6 +840,8 @@ compile_module_body(RecordType *module_type,
     body.push_back(std::move(statement));
   }
   // Wire the outputs of the module and inout connections
+  // TODO: Make these object methods so we don't have to pass things aorund so
+  // much (e.g. _inline flag)
   assign_module_outputs(module_type, body, connection_map, _inline);
   assign_inouts(definition->getSortedConnections(), body, _inline);
   return body;

From a0b59f910ca29e516f43e11654998827e3b91dc7 Mon Sep 17 00:00:00 2001
From: Lenny Truong <leonardtruong@fb.com>
Date: Fri, 21 Feb 2020 15:52:55 -0800
Subject: [PATCH 4/6] Fix copy elision

---
 src/passes/analysis/verilog.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/passes/analysis/verilog.cpp b/src/passes/analysis/verilog.cpp
index 092369758..1e3c6e6d8 100644
--- a/src/passes/analysis/verilog.cpp
+++ b/src/passes/analysis/verilog.cpp
@@ -151,7 +151,7 @@ std::unique_ptr<vAST::StructuralStatement> inline_unary_op(
     std::pair<std::string, CoreIR::Instance *> instance,
     std::unique_ptr<vAST::Connections> verilog_connections,
     bool is_wire) {
-    UnaryOpReplacer transformer(std::move(verilog_connections->at("in")));
+    UnaryOpReplacer transformer(verilog_connections->at("in"));
     std::string wire_name = instance.first;
     if (!is_wire) {
         wire_name += "_out";

From c25251ec47299a77395b42eb0eaac332e900fa90 Mon Sep 17 00:00:00 2001
From: Lenny Truong <leonardtruong@fb.com>
Date: Mon, 24 Feb 2020 15:39:31 -0800
Subject: [PATCH 5/6] Update golds for linebreaks

---
 tests/gtest/golds/arr_tuple.v         | 14 ++++++++++++--
 tests/gtest/golds/bit_wire.v          |  5 ++++-
 tests/gtest/golds/bits_instance.v     | 10 ++++++++--
 tests/gtest/golds/bits_wire.v         |  5 ++++-
 tests/gtest/golds/fanout.v            |  6 +++++-
 tests/gtest/golds/nested_array_wire.v | 13 ++++++++++++-
 tests/gtest/golds/tuple.v             |  7 ++++++-
 7 files changed, 51 insertions(+), 9 deletions(-)

diff --git a/tests/gtest/golds/arr_tuple.v b/tests/gtest/golds/arr_tuple.v
index 57aeed117..e8f80c4b3 100644
--- a/tests/gtest/golds/arr_tuple.v
+++ b/tests/gtest/golds/arr_tuple.v
@@ -1,9 +1,19 @@
 // Module `Foo` defined externally
-module Main (input z_0_x, output z_0_y, input z_1_x, output z_1_y);
+module Main (
+    input z_0_x,
+    output z_0_y,
+    input z_1_x,
+    output z_1_y
+);
 wire Foo_inst0_z_0_y;
 wire a_x;
 wire a_y;
-Foo Foo_inst0(.z_0_x(a_y), .z_0_y(Foo_inst0_z_0_y), .z_1_x(z_0_x), .z_1_y(z_1_y));
+Foo Foo_inst0 (
+    .z_0_x(a_y),
+    .z_0_y(Foo_inst0_z_0_y),
+    .z_1_x(z_0_x),
+    .z_1_y(z_1_y)
+);
 assign a_x = Foo_inst0_z_0_y;
 assign a_y = z_0_x;
 assign z_0_y = a_x;
diff --git a/tests/gtest/golds/bit_wire.v b/tests/gtest/golds/bit_wire.v
index 74f4b9e43..f4d32abd7 100644
--- a/tests/gtest/golds/bit_wire.v
+++ b/tests/gtest/golds/bit_wire.v
@@ -1,4 +1,7 @@
-module Main (input I, output O);
+module Main (
+    input I,
+    output O
+);
 wire x;
 assign x = I;
 assign O = x;
diff --git a/tests/gtest/golds/bits_instance.v b/tests/gtest/golds/bits_instance.v
index a88ece5cc..d7d5a5b1c 100644
--- a/tests/gtest/golds/bits_instance.v
+++ b/tests/gtest/golds/bits_instance.v
@@ -1,8 +1,14 @@
 // Module `Foo` defined externally
-module Main (input [4:0] I, output [4:0] O);
+module Main (
+    input [4:0] I,
+    output [4:0] O
+);
 wire [4:0] Foo_inst0_O;
 wire [4:0] x;
-Foo Foo_inst0(.I(I), .O(Foo_inst0_O));
+Foo Foo_inst0 (
+    .I(I),
+    .O(Foo_inst0_O)
+);
 assign x = Foo_inst0_O;
 assign O = x;
 endmodule
diff --git a/tests/gtest/golds/bits_wire.v b/tests/gtest/golds/bits_wire.v
index 133f73bca..42a9c2abe 100644
--- a/tests/gtest/golds/bits_wire.v
+++ b/tests/gtest/golds/bits_wire.v
@@ -1,4 +1,7 @@
-module Main (input [4:0] I, output [4:0] O);
+module Main (
+    input [4:0] I,
+    output [4:0] O
+);
 wire [4:0] x;
 assign x = I;
 assign O = x;
diff --git a/tests/gtest/golds/fanout.v b/tests/gtest/golds/fanout.v
index 1d6249ca0..fd96f4892 100644
--- a/tests/gtest/golds/fanout.v
+++ b/tests/gtest/golds/fanout.v
@@ -1,4 +1,8 @@
-module Main (input I, output O0, output O1);
+module Main (
+    input I,
+    output O0,
+    output O1
+);
 wire x;
 assign x = I;
 assign O0 = x;
diff --git a/tests/gtest/golds/nested_array_wire.v b/tests/gtest/golds/nested_array_wire.v
index e8fdb040b..9d3372f6f 100644
--- a/tests/gtest/golds/nested_array_wire.v
+++ b/tests/gtest/golds/nested_array_wire.v
@@ -1,4 +1,15 @@
-module Main (input [4:0] I_0, input [4:0] I_1, input [4:0] I_2, input [4:0] I_3, input [4:0] I_4, output [4:0] O_0, output [4:0] O_1, output [4:0] O_2, output [4:0] O_3, output [4:0] O_4);
+module Main (
+    input [4:0] I_0,
+    input [4:0] I_1,
+    input [4:0] I_2,
+    input [4:0] I_3,
+    input [4:0] I_4,
+    output [4:0] O_0,
+    output [4:0] O_1,
+    output [4:0] O_2,
+    output [4:0] O_3,
+    output [4:0] O_4
+);
 wire [24:0] x;
 assign x = {I_4[4],I_4[3],I_4[2],I_4[1],I_4[0],I_3[4],I_3[3],I_3[2],I_3[1],I_3[0],I_2[4],I_2[3],I_2[2],I_2[1],I_2[0],I_1[4],I_1[3],I_1[2],I_1[1],I_1[0],I_0[4],I_0[3],I_0[2],I_0[1],I_0[0]};
 assign O_0 = {x[4],x[3],x[2],x[1],x[0]};
diff --git a/tests/gtest/golds/tuple.v b/tests/gtest/golds/tuple.v
index 793efe59d..c224e7ef8 100644
--- a/tests/gtest/golds/tuple.v
+++ b/tests/gtest/golds/tuple.v
@@ -1,4 +1,9 @@
-module Main (input [4:0] I__0, input I__1, output [4:0] O__0, output O__1);
+module Main (
+    input [4:0] I__0,
+    input I__1,
+    output [4:0] O__0,
+    output O__1
+);
 wire [5:0] x;
 assign x = {I__1,I__0[4],I__0[3],I__0[2],I__0[1],I__0[0]};
 assign O__0 = {x[4],x[3],x[2],x[1],x[0]};

From c2445b6790c2e237f4989d64546de8c66d3734f7 Mon Sep 17 00:00:00 2001
From: Lenny Truong <leonardtruong@fb.com>
Date: Mon, 24 Feb 2020 16:04:18 -0800
Subject: [PATCH 6/6] Use master branch

---
 CMakeLists.txt.in | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CMakeLists.txt.in b/CMakeLists.txt.in
index 13b3d6802..98d3d225e 100644
--- a/CMakeLists.txt.in
+++ b/CMakeLists.txt.in
@@ -7,7 +7,7 @@ project(verilogAST-download NONE)
 include(ExternalProject)
 ExternalProject_Add(verilogAST
   GIT_REPOSITORY    https://github.com/leonardt/verilogAST-cpp.git
-  GIT_TAG           inline-wire-blacklist
+  GIT_TAG           master
   SOURCE_DIR        "${CMAKE_CURRENT_BINARY_DIR}/verilogAST-src"
   BINARY_DIR        "${CMAKE_CURRENT_BINARY_DIR}/verilogAST-build"
   CONFIGURE_COMMAND ""