} // namespace enco
//
+// Hoist Object
+//
+namespace
+{
+
+bool hoistable(const coco::Shuffle *shuffle)
+{
+ auto range = shuffle->range();
+
+ if (range.size() != shuffle->into()->size())
+ {
+ return false;
+ }
+
+ for (const auto &dst : range)
+ {
+ if (shuffle->at(dst).value() != dst.value())
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace
+
+namespace enco
+{
+
+void hoist_object(enco::Code *code)
+{
+ auto m = code->module();
+
+ for (uint32_t n = 0; n < m->entity()->instr()->size(); ++n)
+ {
+ if (auto shuffle = m->entity()->instr()->at(n)->asShuffle())
+ {
+ if (shuffle->parent() == nullptr)
+ {
+ continue;
+ }
+
+ if (hoistable(shuffle))
+ {
+ auto from = shuffle->from();
+ auto into = shuffle->into();
+
+ into->replaceAllDepsWith(from);
+ }
+ }
+ }
+}
+
+} // namespace enco
+
+//
// Dead Bag Elimination
//
#include <set>
void generate_bypass_shuffle(enco::Code *code);
/**
+ * @brief Update the base bag of each object if possible
+ *
+ * Let us consider the following code:
+ *
+ * %bag_1 = Bag(size: 4)
+ * %bag_2 = Bag(size: 1)
+ *
+ * %obj_1 = ... at %bag_1
+ * %obj_2 = ... at %bag_2
+ *
+ * ...
+ * Shuffle(from: %bag_1, into: %bag_2, [0 -> 0]) <- shuffle
+ * ...
+ *
+ * Note that the content of %bag_2 after shuffle is identical to a part of %bag_1, so
+ * the following code is identical to the above code
+ *
+ * %bag_1 = Bag(size: 4)
+ * %bag_2 = Bag(size: 1)
+ *
+ * %obj_1 = ... at %bag_1
+ * %obj_2 = ... at %bag_1
+ *
+ * ...
+ * Shuffle(from: %bag_1, into: %bag_2, [0 -> 0])
+ * ...
+ *
+ * "hoist_object" optimization rewrites the former code as the latter one.
+ *
+ * NOTE "hoist_object" DOES NOT change any instruction. It just updates the base bag of objects of
+ * interest.
+ */
+void hoist_object(enco::Code *code);
+
+/**
* @brief Eliminate dead bags
*
* A bag is referred to as dead if it is neither input nor output, and has no read. If a bag is