--- /dev/null
+#ifndef __COCO_IR_COPY_H__
+#define __COCO_IR_COPY_H__
+
+#include "coco/IR/Instr.h"
+
+#include "coco/IR/Object.h"
+#include "coco/IR/Def.h"
+#include "coco/IR/Use.h"
+
+namespace coco
+{
+
+/**
+ * @brief Index-wise element transfer between two objects
+ *
+ * Given two objects "src" and "dst" of the same kind/shape, "copy(src, dst)"
+ * denotes index-wise element transfers.
+ *
+ * For example, the following pseudo-code describes "copy(src, dat)"
+ * when both src and dst are a feature map of the shape B x C x H x W:
+ *
+ * for each valid index b, ch, row, col:
+ * load the "src->at(b, ch, row, col)"-th element from bag(src)
+ * store it as the "dst->at(b, ch, row, col)"-th element of bag(dst)
+ *
+ * In principle, "copy" is unnecessary as it is always possible to rewrite "copy"
+ * as a "shuffle". However, "shuffle"-based optimization is too heavy as it requires
+ * much of iterations.
+ */
+class Copy final : public Instr, public Object::Producer, public Object::Consumer
+{
+public:
+ Copy() : _from{this}, _into{this}
+ {
+ // DO NOTHING
+ }
+
+public:
+ Copy *asCopy(void) override { return this; }
+ const Copy *asCopy(void) const override { return this; }
+
+public:
+ std::set<Bag *> reads(void) const override;
+ std::set<Bag *> updates(void) const override;
+
+public:
+ Instr *loc(void) override { return this; }
+
+public:
+ Object *from(void) const { return _from.value(); }
+ void from(Object *o) { _from.value(o); }
+
+public:
+ Object *into(void) const { return _into.value(); }
+ void into(Object *o) { _into.value(o); }
+
+private:
+ Use _from;
+ Def _into;
+};
+
+} // namespace coco
+
+#endif // __COCO_IR_COPY_H__