Add new coding tool of ext-comp-refs

The tool of ext-comp-refs adds the uni-directional compound reference
prediction. In details, 3 pairs of uni-direcitonal compound references
are added for the comp ref prediction:
(LAST_FRAME, LAST2_FRAME),
(LAST_FRAME, GOLDEN_FRAME), and
(BWDREF_FRAME, ALTREF_FRAME).

This new tool of ext-comp-refs will eventually overwrite
one-side-compound and have the two coding tools to merge to one.

It achieves -0.35 ~ -0.55% coding gains in BDRate, compared against
AV1 baseline with the default experiments on, but without
one-sided-compound. It achieves -0.2% ~ -0.3% coding gains when
one-sided-compound is on. It achieves larger gains on higher
resolution.

Change-Id: Icbdb16e97b96aaebaf2213f5f72d5331e2e358eb
diff --git a/av1/common/blockd.h b/av1/common/blockd.h
index a7a6332..050b9a7 100644
--- a/av1/common/blockd.h
+++ b/av1/common/blockd.h
@@ -502,6 +502,33 @@
   return mbmi->ref_frame[1] > INTRA_FRAME;
 }
 
+#if CONFIG_EXT_COMP_REFS
+static INLINE int has_uni_comp_refs(const MB_MODE_INFO *mbmi) {
+  return has_second_ref(mbmi) && (!((mbmi->ref_frame[0] >= BWDREF_FRAME) ^
+                                    (mbmi->ref_frame[1] >= BWDREF_FRAME)));
+}
+
+static INLINE MV_REFERENCE_FRAME comp_ref0(int ref_idx) {
+  static const MV_REFERENCE_FRAME lut[] = {
+    LAST_FRAME,    // LAST_LAST2_FRAMES,
+    LAST_FRAME,    // LAST_GOLDEN_FRAMES,
+    BWDREF_FRAME,  // BWDREF_ALTREF_FRAMES,
+  };
+  assert(NELEMENTS(lut) == UNIDIR_COMP_REFS);
+  return lut[ref_idx];
+}
+
+static INLINE MV_REFERENCE_FRAME comp_ref1(int ref_idx) {
+  static const MV_REFERENCE_FRAME lut[] = {
+    LAST2_FRAME,   // LAST_LAST2_FRAMES,
+    GOLDEN_FRAME,  // LAST_GOLDEN_FRAMES,
+    ALTREF_FRAME,  // BWDREF_ALTREF_FRAMES,
+  };
+  assert(NELEMENTS(lut) == UNIDIR_COMP_REFS);
+  return lut[ref_idx];
+}
+#endif  // CONFIG_EXT_COMP_REFS
+
 PREDICTION_MODE av1_left_block_mode(const MODE_INFO *cur_mi,
                                     const MODE_INFO *left_mi, int b);
 
diff --git a/av1/common/entropymode.c b/av1/common/entropymode.c
index 433b0d8..b0bf05e 100644
--- a/av1/common/entropymode.c
+++ b/av1/common/entropymode.c
@@ -1392,6 +1392,17 @@
             { AOM_ICDF(41 * 128), AOM_ICDF(32768), 0 } };
 #endif
 
+#if CONFIG_EXT_COMP_REFS
+static const aom_prob default_comp_ref_type_p[COMP_REF_TYPE_CONTEXTS] = {
+  30, 75, 120, 170, 230
+};
+
+static const aom_prob
+    default_uni_comp_ref_p[UNI_COMP_REF_CONTEXTS][UNIDIR_COMP_REFS - 1] = {
+      { 30, 20 }, { 75, 70 }, { 130, 130 }, { 165, 165 }, { 215, 220 }
+    };
+#endif  // CONFIG_EXT_COMP_REFS
+
 #if CONFIG_EXT_REFS
 static const aom_prob default_comp_ref_p[REF_CONTEXTS][FWD_REFS - 1] = {
   // TODO(zoeliu): To adjust the initial prob values.
@@ -4577,6 +4588,10 @@
 #if CONFIG_NEW_MULTISYMBOL
   av1_copy(fc->comp_inter_cdf, default_comp_inter_cdf);
 #endif
+#if CONFIG_EXT_COMP_REFS
+  av1_copy(fc->comp_ref_type_prob, default_comp_ref_type_p);
+  av1_copy(fc->uni_comp_ref_prob, default_uni_comp_ref_p);
+#endif  // CONFIG_EXT_COMP_REFS
   av1_copy(fc->comp_ref_prob, default_comp_ref_p);
 #if CONFIG_LV_MAP
   av1_copy(fc->txb_skip, default_txb_skip);
@@ -4804,6 +4819,17 @@
     fc->comp_inter_prob[i] = av1_mode_mv_merge_probs(pre_fc->comp_inter_prob[i],
                                                      counts->comp_inter[i]);
 
+#if CONFIG_EXT_COMP_REFS
+  for (i = 0; i < COMP_REF_TYPE_CONTEXTS; i++)
+    fc->comp_ref_type_prob[i] = av1_mode_mv_merge_probs(
+        pre_fc->comp_ref_type_prob[i], counts->comp_ref_type[i]);
+
+  for (i = 0; i < UNI_COMP_REF_CONTEXTS; i++)
+    for (j = 0; j < (UNIDIR_COMP_REFS - 1); j++)
+      fc->uni_comp_ref_prob[i][j] = av1_mode_mv_merge_probs(
+          pre_fc->uni_comp_ref_prob[i][j], counts->uni_comp_ref[i][j]);
+#endif  // CONFIG_EXT_COMP_REFS
+
 #if CONFIG_EXT_REFS
   for (i = 0; i < REF_CONTEXTS; i++)
     for (j = 0; j < (FWD_REFS - 1); j++)
diff --git a/av1/common/entropymode.h b/av1/common/entropymode.h
index b16b141..301b3c1 100644
--- a/av1/common/entropymode.h
+++ b/av1/common/entropymode.h
@@ -218,6 +218,10 @@
   aom_cdf_prob comp_inter_cdf[COMP_INTER_CONTEXTS][CDF_SIZE(2)];
   aom_cdf_prob single_ref_cdf[REF_CONTEXTS][SINGLE_REFS - 1][CDF_SIZE(2)];
 #endif
+#if CONFIG_EXT_COMP_REFS
+  aom_prob comp_ref_type_prob[COMP_REF_TYPE_CONTEXTS];
+  aom_prob uni_comp_ref_prob[UNI_COMP_REF_CONTEXTS][UNIDIR_COMP_REFS - 1];
+#endif  // CONFIG_EXT_COMP_REFS
   aom_prob single_ref_prob[REF_CONTEXTS][SINGLE_REFS - 1];
 #if CONFIG_EXT_REFS
   aom_prob comp_ref_prob[REF_CONTEXTS][FWD_REFS - 1];
@@ -390,6 +394,10 @@
 #endif  // CONFIG_MOTION_VAR || CONFIG_WARPED_MOTION
   unsigned int intra_inter[INTRA_INTER_CONTEXTS][2];
   unsigned int comp_inter[COMP_INTER_CONTEXTS][2];
+#if CONFIG_EXT_COMP_REFS
+  unsigned int comp_ref_type[COMP_REF_TYPE_CONTEXTS][2];
+  unsigned int uni_comp_ref[UNI_COMP_REF_CONTEXTS][UNIDIR_COMP_REFS - 1][2];
+#endif  // CONFIG_EXT_COMP_REFS
   unsigned int single_ref[REF_CONTEXTS][SINGLE_REFS - 1][2];
 #if CONFIG_EXT_REFS
   unsigned int comp_ref[REF_CONTEXTS][FWD_REFS - 1][2];
diff --git a/av1/common/enums.h b/av1/common/enums.h
index 2e802de..f008f9d 100644
--- a/av1/common/enums.h
+++ b/av1/common/enums.h
@@ -286,6 +286,18 @@
 #endif  // CONFIG_EXT_REFS
 } AOM_REFFRAME;
 
+#if CONFIG_EXT_COMP_REFS
+#define USE_UNI_COMP_REFS 1
+
+typedef enum {
+  UNIDIR_COMP_REFERENCE = 0,
+  BIDIR_COMP_REFERENCE = 1,
+  COMP_REFERENCE_TYPES = 2,
+} COMP_REFERENCE_TYPE;
+#else  // !CONFIG_EXT_COMP_REFS
+#define USE_UNI_COMP_REFS 0
+#endif  // CONFIG_EXT_COMP_REFS
+
 typedef enum { PLANE_TYPE_Y = 0, PLANE_TYPE_UV = 1, PLANE_TYPES } PLANE_TYPE;
 
 #if CONFIG_CFL
@@ -487,6 +499,12 @@
 #define INTRA_INTER_CONTEXTS 4
 #define COMP_INTER_CONTEXTS 5
 #define REF_CONTEXTS 5
+
+#if CONFIG_EXT_COMP_REFS
+#define COMP_REF_TYPE_CONTEXTS 5
+#define UNI_COMP_REF_CONTEXTS 5
+#endif  // CONFIG_EXT_COMP_REFS
+
 #if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
 #define COMP_INTER_MODE_CONTEXTS 4
 #endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
@@ -526,7 +544,17 @@
 #endif  // CONFIG_EXT_REFS
 
 #define SINGLE_REFS (FWD_REFS + BWD_REFS)
+#if CONFIG_EXT_COMP_REFS
+typedef enum {
+  LAST_LAST2_FRAMES,     // { LAST_FRAME, LAST2_FRAME }
+  LAST_GOLDEN_FRAMES,    // { LAST_FRAME, GOLDEN_FRAME }
+  BWDREF_ALTREF_FRAMES,  // { BWDREF_FRAME, ALTREF_FRAME }
+  UNIDIR_COMP_REFS
+} UNIDIR_COMP_REF;
+#define COMP_REFS (FWD_REFS * BWD_REFS + UNIDIR_COMP_REFS)
+#else  // !CONFIG_EXT_COMP_REFS
 #define COMP_REFS (FWD_REFS * BWD_REFS)
+#endif  // CONFIG_EXT_COMP_REFS
 
 #define MODE_CTX_REF_FRAMES (TOTAL_REFS_PER_FRAME + COMP_REFS)
 
diff --git a/av1/common/mvref_common.h b/av1/common/mvref_common.h
index 209a636..acba436 100644
--- a/av1/common/mvref_common.h
+++ b/av1/common/mvref_common.h
@@ -238,10 +238,41 @@
   return 0;
 }
 
+#if CONFIG_EXT_COMP_REFS
+static INLINE int8_t av1_uni_comp_ref_idx(const MV_REFERENCE_FRAME *const rf) {
+  // Single ref pred
+  if (rf[1] <= INTRA_FRAME) return -1;
+
+  // Bi-directional comp ref pred
+  if ((rf[0] < BWDREF_FRAME) && (rf[1] >= BWDREF_FRAME)) return -1;
+
+  for (int8_t ref_idx = 0; ref_idx < UNIDIR_COMP_REFS; ++ref_idx) {
+    if (rf[0] == comp_ref0(ref_idx) && rf[1] == comp_ref1(ref_idx))
+      return ref_idx;
+  }
+  return -1;
+}
+#endif  // CONFIG_EXT_COMP_REFS
+
 static INLINE int8_t av1_ref_frame_type(const MV_REFERENCE_FRAME *const rf) {
   if (rf[1] > INTRA_FRAME) {
-    return TOTAL_REFS_PER_FRAME + FWD_RF_OFFSET(rf[0]) +
-           BWD_RF_OFFSET(rf[1]) * FWD_REFS;
+#if CONFIG_EXT_COMP_REFS
+    int8_t uni_comp_ref_idx = av1_uni_comp_ref_idx(rf);
+#if !USE_UNI_COMP_REFS
+    // NOTE: uni-directional comp refs disabled
+    assert(uni_comp_ref_idx < 0);
+#endif  // !USE_UNI_COMP_REFS
+    if (uni_comp_ref_idx >= 0) {
+      assert((TOTAL_REFS_PER_FRAME + FWD_REFS * BWD_REFS + uni_comp_ref_idx) <
+             MODE_CTX_REF_FRAMES);
+      return TOTAL_REFS_PER_FRAME + FWD_REFS * BWD_REFS + uni_comp_ref_idx;
+    } else {
+#endif  // CONFIG_EXT_COMP_REFS
+      return TOTAL_REFS_PER_FRAME + FWD_RF_OFFSET(rf[0]) +
+             BWD_RF_OFFSET(rf[1]) * FWD_REFS;
+#if CONFIG_EXT_COMP_REFS
+    }
+#endif  // CONFIG_EXT_COMP_REFS
   }
 
   return rf[0];
@@ -255,9 +286,15 @@
 
   { LAST_FRAME, ALTREF_FRAME },  { LAST2_FRAME, ALTREF_FRAME },
   { LAST3_FRAME, ALTREF_FRAME }, { GOLDEN_FRAME, ALTREF_FRAME }
-#else
+
+  // TODO(zoeliu): Temporarily disable uni-directional comp refs
+#if CONFIG_EXT_COMP_REFS
+  , { LAST_FRAME, LAST2_FRAME }, { LAST_FRAME, GOLDEN_FRAME },
+  { BWDREF_FRAME, ALTREF_FRAME }
+#endif  // CONFIG_EXT_COMP_REFS
+#else  // !CONFIG_EXT_REFS
   { LAST_FRAME, ALTREF_FRAME }, { GOLDEN_FRAME, ALTREF_FRAME }
-#endif
+#endif  // CONFIG_EXT_REFS
 };
 // clang-format on
 
diff --git a/av1/common/pred_common.c b/av1/common/pred_common.c
index bda1fc5..01e7f41 100644
--- a/av1/common/pred_common.c
+++ b/av1/common/pred_common.c
@@ -310,6 +310,280 @@
   return ctx;
 }
 
+#if CONFIG_EXT_COMP_REFS
+#define CHECK_BWDREF_OR_ALTREF(ref_frame) \
+  ((ref_frame) == BWDREF_FRAME || (ref_frame) == ALTREF_FRAME)
+int av1_get_comp_reference_type_context(const AV1_COMMON *cm,
+                                        const MACROBLOCKD *xd) {
+  int pred_context;
+  const MB_MODE_INFO *const above_mbmi = xd->above_mbmi;
+  const MB_MODE_INFO *const left_mbmi = xd->left_mbmi;
+  const int above_in_image = xd->up_available;
+  const int left_in_image = xd->left_available;
+
+  (void)cm;
+
+  if (above_in_image && left_in_image) {  // both edges available
+    const int above_intra = !is_inter_block(above_mbmi);
+    const int left_intra = !is_inter_block(left_mbmi);
+
+    if (above_intra && left_intra) {  // intra/intra
+      pred_context = 2;
+    } else if (above_intra || left_intra) {  // intra/inter
+      const MB_MODE_INFO *inter_mbmi = above_intra ? left_mbmi : above_mbmi;
+
+      if (!has_second_ref(inter_mbmi))  // single pred
+        pred_context = 2;
+      else  // comp pred
+        pred_context = 1 + 2 * has_uni_comp_refs(inter_mbmi);
+    } else {  // inter/inter
+      const int a_sg = !has_second_ref(above_mbmi);
+      const int l_sg = !has_second_ref(left_mbmi);
+      const MV_REFERENCE_FRAME frfa = above_mbmi->ref_frame[0];
+      const MV_REFERENCE_FRAME frfl = left_mbmi->ref_frame[0];
+
+      if (a_sg && l_sg) {  // single/single
+        pred_context = 1 +
+                       2 * (!(CHECK_BWDREF_OR_ALTREF(frfa) ^
+                              CHECK_BWDREF_OR_ALTREF(frfl)));
+      } else if (l_sg || a_sg) {  // single/comp
+        const int uni_rfc =
+            a_sg ? has_uni_comp_refs(left_mbmi) : has_uni_comp_refs(above_mbmi);
+
+        if (!uni_rfc)  // comp bidir
+          pred_context = 1;
+        else  // comp unidir
+          pred_context = 3 + (!(CHECK_BWDREF_OR_ALTREF(frfa) ^
+                                CHECK_BWDREF_OR_ALTREF(frfl)));
+      } else {  // comp/comp
+        const int a_uni_rfc = has_uni_comp_refs(above_mbmi);
+        const int l_uni_rfc = has_uni_comp_refs(left_mbmi);
+
+        if (!a_uni_rfc && !l_uni_rfc)  // bidir/bidir
+          pred_context = 0;
+        else if (!a_uni_rfc || !l_uni_rfc)  // unidir/bidir
+          pred_context = 2;
+        else  // unidir/unidir
+          pred_context =
+              3 + (!((frfa == BWDREF_FRAME) ^ (frfl == BWDREF_FRAME)));
+      }
+    }
+  } else if (above_in_image || left_in_image) {  // one edge available
+    const MB_MODE_INFO *edge_mbmi = above_in_image ? above_mbmi : left_mbmi;
+
+    if (!is_inter_block(edge_mbmi)) {  // intra
+      pred_context = 2;
+    } else {                           // inter
+      if (!has_second_ref(edge_mbmi))  // single pred
+        pred_context = 2;
+      else  // comp pred
+        pred_context = 4 * has_uni_comp_refs(edge_mbmi);
+    }
+  } else {  // no edges available
+    pred_context = 2;
+  }
+
+  assert(pred_context >= 0 && pred_context < COMP_REF_TYPE_CONTEXTS);
+  return pred_context;
+}
+
+// Returns a context number for the given MB prediction signal
+// Signal the uni-directional compound reference frame pair as
+// either (BWDREF, ALTREF), or (LAST, LAST2)/(LAST, GOLDEN),
+// conditioning on the pair is known as uni-directional.
+int av1_get_pred_context_uni_comp_ref_p(const AV1_COMMON *cm,
+                                        const MACROBLOCKD *xd) {
+  int pred_context;
+  const MB_MODE_INFO *const above_mbmi = xd->above_mbmi;
+  const MB_MODE_INFO *const left_mbmi = xd->left_mbmi;
+  const int above_in_image = xd->up_available;
+  const int left_in_image = xd->left_available;
+
+  (void)cm;
+
+  if (above_in_image && left_in_image) {  // both edges available
+    const int above_intra = !is_inter_block(above_mbmi);
+    const int left_intra = !is_inter_block(left_mbmi);
+
+    if (above_intra && left_intra) {  // intra/intra
+      pred_context = 2;
+    } else if (above_intra || left_intra) {  // intra/inter
+      const MB_MODE_INFO *inter_mbmi = above_intra ? left_mbmi : above_mbmi;
+
+      if (!has_second_ref(inter_mbmi)) {  // single pred
+        pred_context =
+            1 + 2 * (!CHECK_BWDREF_OR_ALTREF(inter_mbmi->ref_frame[0]));
+      } else {                              // comp pred
+        if (has_uni_comp_refs(inter_mbmi))  // comp unidir
+          pred_context = 4 * (inter_mbmi->ref_frame[0] != BWDREF_FRAME);
+        else  // comp_bidir
+          pred_context = 2;
+      }
+    } else {  // inter/inter
+      const int a_sg = !has_second_ref(above_mbmi);
+      const int l_sg = !has_second_ref(left_mbmi);
+      const MV_REFERENCE_FRAME frfa = above_mbmi->ref_frame[0];
+      const MV_REFERENCE_FRAME frfl = left_mbmi->ref_frame[0];
+
+      if (CHECK_BWDREF_OR_ALTREF(frfa) && CHECK_BWDREF_OR_ALTREF(frfl)) {
+        pred_context = 0;
+      } else if (a_sg && l_sg) {  // single/single
+        pred_context = 2 + (!CHECK_BWDREF_OR_ALTREF(frfa) &&
+                            !CHECK_BWDREF_OR_ALTREF(frfl));
+      } else if (l_sg || a_sg) {  // single/comp
+        const MV_REFERENCE_FRAME frfc = a_sg ? frfl : frfa;
+        const MV_REFERENCE_FRAME rfs = a_sg ? frfa : frfl;
+        const int uni_rfc =
+            a_sg ? has_uni_comp_refs(left_mbmi) : has_uni_comp_refs(above_mbmi);
+
+        if (uni_rfc && (frfc == BWDREF_FRAME))
+          pred_context = 1;
+        else if (uni_rfc && (frfc == LAST_FRAME))
+          pred_context = 3 + (!CHECK_BWDREF_OR_ALTREF(rfs));
+        else if (CHECK_BWDREF_OR_ALTREF(rfs))
+          pred_context = 2;
+        else
+          pred_context = 3;
+      } else {  // comp/comp
+        const int a_uni_rfc = has_uni_comp_refs(above_mbmi);
+        const int l_uni_rfc = has_uni_comp_refs(left_mbmi);
+
+        if (a_uni_rfc && l_uni_rfc)
+          pred_context = 2 + 2 * (frfa != BWDREF_FRAME && frfl != BWDREF_FRAME);
+        else if (a_uni_rfc || l_uni_rfc)
+          pred_context = 1 + 2 * (frfa != BWDREF_FRAME && frfl != BWDREF_FRAME);
+        else
+          pred_context = 2;
+      }
+    }
+  } else if (above_in_image || left_in_image) {  // one edge available
+    const MB_MODE_INFO *edge_mbmi = above_in_image ? above_mbmi : left_mbmi;
+
+    if (!is_inter_block(edge_mbmi)) {  // intra
+      pred_context = 2;
+    } else {                             // inter
+      if (!has_second_ref(edge_mbmi)) {  // single pred
+        pred_context =
+            1 + 2 * (!CHECK_BWDREF_OR_ALTREF(edge_mbmi->ref_frame[0]));
+      } else {                             // comp pred
+        if (has_uni_comp_refs(edge_mbmi))  // comp unidir
+          pred_context = 4 * (edge_mbmi->ref_frame[0] != BWDREF_FRAME);
+        else  // comp bidir
+          pred_context = 2;
+      }
+    }
+  } else {  // no edges available
+    pred_context = 2;
+  }
+
+  assert(pred_context >= 0 && pred_context < UNI_COMP_REF_CONTEXTS);
+  return pred_context;
+}
+
+// Returns a context number for the given MB prediction signal
+// Signal the uni-directional compound reference frame pair as
+// either (BWDREF, ALTREF), or (LAST, LAST2)/(LAST, GOLDEN),
+// conditioning on the pair is known as uni-directional.
+#define CHECK_LAST2_OR_GOLDEN(ref_frame) \
+  ((ref_frame) == LAST2_FRAME || (ref_frame) == GOLDEN_FRAME)
+int av1_get_pred_context_uni_comp_ref_p1(const AV1_COMMON *cm,
+                                         const MACROBLOCKD *xd) {
+  int pred_context;
+  const MB_MODE_INFO *const above_mbmi = xd->above_mbmi;
+  const MB_MODE_INFO *const left_mbmi = xd->left_mbmi;
+  const int above_in_image = xd->up_available;
+  const int left_in_image = xd->left_available;
+
+  (void)cm;
+
+  if (above_in_image && left_in_image) {  // both edges available
+    const int above_intra = !is_inter_block(above_mbmi);
+    const int left_intra = !is_inter_block(left_mbmi);
+
+    if (above_intra && left_intra) {  // intra/intra
+      pred_context = 2;
+    } else if (above_intra || left_intra) {  // intra/inter
+      const MB_MODE_INFO *inter_mbmi = above_intra ? left_mbmi : above_mbmi;
+
+      if (!has_second_ref(inter_mbmi)) {  // single pred
+        if (CHECK_LAST2_OR_GOLDEN(inter_mbmi->ref_frame[0]))
+          pred_context = 1 + 2 * (inter_mbmi->ref_frame[0] == LAST2_FRAME);
+        else
+          pred_context = 2;
+      } else {  // comp pred
+        if (CHECK_LAST2_OR_GOLDEN(inter_mbmi->ref_frame[1]))
+          pred_context = 4 * (inter_mbmi->ref_frame[1] == LAST2_FRAME);
+        else
+          pred_context = 2;
+      }
+    } else {  // inter/inter
+      const int a_sg = !has_second_ref(above_mbmi);
+      const int l_sg = !has_second_ref(left_mbmi);
+      const MV_REFERENCE_FRAME frfa = above_mbmi->ref_frame[0];
+      const MV_REFERENCE_FRAME frfl = left_mbmi->ref_frame[0];
+      const MV_REFERENCE_FRAME brfa = above_mbmi->ref_frame[1];
+      const MV_REFERENCE_FRAME brfl = left_mbmi->ref_frame[1];
+
+      if (a_sg && l_sg) {  // single/single
+        if (CHECK_LAST2_OR_GOLDEN(frfa) && (frfa == frfl || frfl == LAST_FRAME))
+          pred_context = 1 + 2 * (frfa == LAST2_FRAME);
+        else if (frfa == LAST_FRAME && CHECK_LAST2_OR_GOLDEN(frfl))
+          pred_context = 1 + 2 * (frfl == LAST2_FRAME);
+        else
+          pred_context = 2;
+      } else if (l_sg || a_sg) {  // single/comp
+        const MV_REFERENCE_FRAME brfc = a_sg ? brfl : brfa;
+
+        if (CHECK_LAST2_OR_GOLDEN(brfc))
+          pred_context = 4 * (brfc == LAST2_FRAME);
+        if (CHECK_LAST2_OR_GOLDEN(frfa) && (frfa == frfl || frfl == LAST_FRAME))
+          pred_context = 1 + 2 * (frfa == LAST2_FRAME);
+        else if (frfa == LAST_FRAME && CHECK_LAST2_OR_GOLDEN(frfl))
+          pred_context = 1 + 2 * (frfl == LAST2_FRAME);
+        else
+          pred_context = 2;
+      } else {  // comp/comp
+        if (CHECK_LAST2_OR_GOLDEN(brfa) &&
+            (brfa == brfl || !CHECK_LAST2_OR_GOLDEN(brfl)))
+          pred_context = 4 * (brfa == LAST2_FRAME);
+        else if (!CHECK_LAST2_OR_GOLDEN(brfa) && CHECK_LAST2_OR_GOLDEN(brfl))
+          pred_context = 4 * (brfl == LAST2_FRAME);
+        else if (CHECK_LAST2_OR_GOLDEN(frfa) &&
+                 (frfa == frfl || frfl == LAST_FRAME))
+          pred_context = 1 + 2 * (frfa == LAST2_FRAME);
+        else if (frfa == LAST_FRAME && CHECK_LAST2_OR_GOLDEN(frfl))
+          pred_context = 1 + 2 * (frfl == LAST2_FRAME);
+        else
+          pred_context = 2;
+      }
+    }
+  } else if (above_in_image || left_in_image) {  // one edge available
+    const MB_MODE_INFO *edge_mbmi = above_in_image ? above_mbmi : left_mbmi;
+
+    if (!is_inter_block(edge_mbmi)) {  // intra
+      pred_context = 2;
+    } else {                             // inter
+      if (!has_second_ref(edge_mbmi)) {  // single pred
+        if (CHECK_LAST2_OR_GOLDEN(edge_mbmi->ref_frame[0]))
+          pred_context = 1 + 2 * (edge_mbmi->ref_frame[0] == LAST2_FRAME);
+        else
+          pred_context = 2;
+      } else {  // comp pred
+        if (CHECK_LAST2_OR_GOLDEN(edge_mbmi->ref_frame[1]))
+          pred_context = 4 * (edge_mbmi->ref_frame[1] == LAST2_FRAME);
+        else
+          pred_context = 2;
+      }
+    }
+  } else {  // no edges available
+    pred_context = 2;
+  }
+
+  assert(pred_context >= 0 && pred_context < UNI_COMP_REF_CONTEXTS);
+  return pred_context;
+}
+#endif  // CONFIG_EXT_COMP_REFS
+
 #if CONFIG_EXT_REFS
 
 // TODO(zoeliu): Future work will be conducted to optimize the context design
@@ -327,13 +601,8 @@
 //
 // NOTE(zoeliu): The probability of ref_frame[0] is either
 //               GOLDEN_FRAME or LAST3_FRAME.
-#if CONFIG_ONE_SIDED_COMPOUND
-int av1_get_pred_context_comp_ref_p(UNUSED const AV1_COMMON *cm,
-                                    const MACROBLOCKD *xd) {
-#else
 int av1_get_pred_context_comp_ref_p(const AV1_COMMON *cm,
                                     const MACROBLOCKD *xd) {
-#endif
   int pred_context;
   const MB_MODE_INFO *const above_mbmi = xd->above_mbmi;
   const MB_MODE_INFO *const left_mbmi = xd->left_mbmi;
@@ -344,14 +613,16 @@
 // The mode info data structure has a one element border above and to the
 // left of the entries correpsonding to real macroblocks.
 // The prediction flags in these dummy entries are initialised to 0.
-#if CONFIG_ONE_SIDED_COMPOUND  // No change to bitstream
+#if CONFIG_ONE_SIDED_COMPOUND || CONFIG_EXT_COMP_REFS  // No change to bitstream
   // Code seems to assume that signbias of cm->comp_bwd_ref[0] is always 1
   const int bwd_ref_sign_idx = 1;
 #else
   const int bwd_ref_sign_idx = cm->ref_frame_sign_bias[cm->comp_bwd_ref[0]];
-#endif
+#endif  // CONFIG_ONE_SIDED_COMPOUND || CONFIG_EXT_COMP_REFS
   const int fwd_ref_sign_idx = !bwd_ref_sign_idx;
 
+  (void)cm;
+
   if (above_in_image && left_in_image) {  // both edges available
     const int above_intra = !is_inter_block(above_mbmi);
     const int left_intra = !is_inter_block(left_mbmi);
@@ -403,8 +674,11 @@
         if ((CHECK_LAST_OR_LAST2(frfa) && CHECK_LAST_OR_LAST2(frfl))) {
           pred_context = 4;
         } else {
-          // NOTE(zoeliu): Following assert may be removed once confirmed.
+// NOTE(zoeliu): Following assert may be removed once confirmed.
+#if !USE_UNI_COMP_REFS
+          // TODO(zoeliu): To further study the UNIDIR scenario
           assert(CHECK_GOLDEN_OR_LAST3(frfa) || CHECK_GOLDEN_OR_LAST3(frfl));
+#endif  // !USE_UNI_COMP_REFS
           pred_context = 2;
         }
       }
@@ -437,13 +711,8 @@
 //
 // NOTE(zoeliu): The probability of ref_frame[0] is LAST_FRAME,
 // conditioning on it is either LAST_FRAME or LAST2_FRAME.
-#if CONFIG_ONE_SIDED_COMPOUND
-int av1_get_pred_context_comp_ref_p1(UNUSED const AV1_COMMON *cm,
-                                     const MACROBLOCKD *xd) {
-#else
 int av1_get_pred_context_comp_ref_p1(const AV1_COMMON *cm,
                                      const MACROBLOCKD *xd) {
-#endif
   int pred_context;
   const MB_MODE_INFO *const above_mbmi = xd->above_mbmi;
   const MB_MODE_INFO *const left_mbmi = xd->left_mbmi;
@@ -454,14 +723,16 @@
 // The mode info data structure has a one element border above and to the
 // left of the entries correpsonding to real macroblocks.
 // The prediction flags in these dummy entries are initialised to 0.
-#if CONFIG_ONE_SIDED_COMPOUND  // No change to bitstream
+#if CONFIG_ONE_SIDED_COMPOUND || CONFIG_EXT_COMP_REFS  // No change to bitstream
   // Code seems to assume that signbias of cm->comp_bwd_ref[0] is always 1
   const int bwd_ref_sign_idx = 1;
 #else
   const int bwd_ref_sign_idx = cm->ref_frame_sign_bias[cm->comp_bwd_ref[0]];
-#endif
+#endif  //  CONFIG_ONE_SIDED_COMPOUND || CONFIG_EXT_COMP_REFS
   const int fwd_ref_sign_idx = !bwd_ref_sign_idx;
 
+  (void)cm;
+
   if (above_in_image && left_in_image) {  // both edges available
     const int above_intra = !is_inter_block(above_mbmi);
     const int left_intra = !is_inter_block(left_mbmi);
@@ -548,13 +819,8 @@
 //
 // NOTE(zoeliu): The probability of ref_frame[0] is GOLDEN_FRAME,
 // conditioning on it is either GOLDEN or LAST3.
-#if CONFIG_ONE_SIDED_COMPOUND
-int av1_get_pred_context_comp_ref_p2(UNUSED const AV1_COMMON *cm,
-                                     const MACROBLOCKD *xd) {
-#else
 int av1_get_pred_context_comp_ref_p2(const AV1_COMMON *cm,
                                      const MACROBLOCKD *xd) {
-#endif
   int pred_context;
   const MB_MODE_INFO *const above_mbmi = xd->above_mbmi;
   const MB_MODE_INFO *const left_mbmi = xd->left_mbmi;
@@ -565,14 +831,16 @@
 // The mode info data structure has a one element border above and to the
 // left of the entries correpsonding to real macroblocks.
 // The prediction flags in these dummy entries are initialised to 0.
-#if CONFIG_ONE_SIDED_COMPOUND  // No change to bitstream
+#if CONFIG_ONE_SIDED_COMPOUND || CONFIG_EXT_COMP_REFS  // No change to bitstream
   // Code seems to assume that signbias of cm->comp_bwd_ref[0] is always 1
   const int bwd_ref_sign_idx = 1;
 #else
   const int bwd_ref_sign_idx = cm->ref_frame_sign_bias[cm->comp_bwd_ref[0]];
-#endif
+#endif  // CONFIG_ONE_SIDED_COMPOUND || CONFIG_EXT_COMP_REFS
   const int fwd_ref_sign_idx = !bwd_ref_sign_idx;
 
+  (void)cm;
+
   if (above_in_image && left_in_image) {  // both edges available
     const int above_intra = !is_inter_block(above_mbmi);
     const int left_intra = !is_inter_block(left_mbmi);
@@ -653,13 +921,8 @@
 }
 
 // Returns a context number for the given MB prediction signal
-#if CONFIG_ONE_SIDED_COMPOUND
-int av1_get_pred_context_comp_bwdref_p(UNUSED const AV1_COMMON *cm,
-                                       const MACROBLOCKD *xd) {
-#else
 int av1_get_pred_context_comp_bwdref_p(const AV1_COMMON *cm,
                                        const MACROBLOCKD *xd) {
-#endif
   int pred_context;
   const MB_MODE_INFO *const above_mbmi = xd->above_mbmi;
   const MB_MODE_INFO *const left_mbmi = xd->left_mbmi;
@@ -670,14 +933,16 @@
 // The mode info data structure has a one element border above and to the
 // left of the entries corresponding to real macroblocks.
 // The prediction flags in these dummy entries are initialized to 0.
-#if CONFIG_ONE_SIDED_COMPOUND  // No change to bitstream
+#if CONFIG_ONE_SIDED_COMPOUND || CONFIG_EXT_COMP_REFS  // No change to bitstream
   // Code seems to assume that signbias of cm->comp_bwd_ref[0] is always 1
   const int bwd_ref_sign_idx = 1;
 #else
   const int bwd_ref_sign_idx = cm->ref_frame_sign_bias[cm->comp_bwd_ref[0]];
-#endif
+#endif  // CONFIG_ONE_SIDED_COMPOUND || CONFIG_EXT_COMP_REFS
   const int fwd_ref_sign_idx = !bwd_ref_sign_idx;
 
+  (void)cm;
+
   if (above_in_image && left_in_image) {  // both edges available
     const int above_intra = !is_inter_block(above_mbmi);
     const int left_intra = !is_inter_block(left_mbmi);
@@ -716,8 +981,11 @@
                    a_brf == cm->comp_bwd_ref[1]) {
           pred_context = 1;
         } else {
-          // NOTE: Backward ref should be either BWDREF or ALTREF.
+// NOTE: Backward ref should be either BWDREF or ALTREF.
+#if !USE_UNI_COMP_REFS
+          // TODO(zoeliu): To further study the UNIDIR scenario
           assert(l_brf == a_brf && l_brf != cm->comp_bwd_ref[1]);
+#endif  // !USE_UNI_COMP_REFS
           pred_context = 3;
         }
       } else if (!l_comp && !a_comp) {  // single/single
@@ -729,8 +997,11 @@
         } else if (l_frf == a_frf) {
           pred_context = 3;
         } else {
+#if !USE_UNI_COMP_REFS
+          // TODO(zoeliu): To further study the UNIDIR scenario
           assert(l_frf != a_frf && l_frf != cm->comp_bwd_ref[1] &&
                  a_frf != cm->comp_bwd_ref[1]);
+#endif  // !USE_UNI_COMP_REFS
           pred_context = 4;
         }
       } else {  // comp/single
diff --git a/av1/common/pred_common.h b/av1/common/pred_common.h
index 3764576..9a32815 100644
--- a/av1/common/pred_common.h
+++ b/av1/common/pred_common.h
@@ -108,6 +108,35 @@
 }
 #endif
 
+#if CONFIG_EXT_COMP_REFS
+int av1_get_comp_reference_type_context(const AV1_COMMON *cm,
+                                        const MACROBLOCKD *xd);
+
+static INLINE aom_prob av1_get_comp_reference_type_prob(const AV1_COMMON *cm,
+                                                        const MACROBLOCKD *xd) {
+  return cm->fc
+      ->comp_ref_type_prob[av1_get_comp_reference_type_context(cm, xd)];
+}
+
+int av1_get_pred_context_uni_comp_ref_p(const AV1_COMMON *cm,
+                                        const MACROBLOCKD *xd);
+
+static INLINE aom_prob av1_get_pred_prob_uni_comp_ref_p(const AV1_COMMON *cm,
+                                                        const MACROBLOCKD *xd) {
+  const int pred_context = av1_get_pred_context_uni_comp_ref_p(cm, xd);
+  return cm->fc->uni_comp_ref_prob[pred_context][0];
+}
+
+int av1_get_pred_context_uni_comp_ref_p1(const AV1_COMMON *cm,
+                                         const MACROBLOCKD *xd);
+
+static INLINE aom_prob
+av1_get_pred_prob_uni_comp_ref_p1(const AV1_COMMON *cm, const MACROBLOCKD *xd) {
+  const int pred_context = av1_get_pred_context_uni_comp_ref_p1(cm, xd);
+  return cm->fc->uni_comp_ref_prob[pred_context][1];
+}
+#endif  // CONFIG_EXT_COMP_REFS
+
 int av1_get_pred_context_comp_ref_p(const AV1_COMMON *cm,
                                     const MACROBLOCKD *xd);
 
diff --git a/av1/decoder/decodeframe.c b/av1/decoder/decodeframe.c
index 60847ff..39a0dc0 100644
--- a/av1/decoder/decodeframe.c
+++ b/av1/decoder/decodeframe.c
@@ -95,7 +95,7 @@
                                        struct aom_read_bit_buffer *rb);
 
 static int is_compound_reference_allowed(const AV1_COMMON *cm) {
-#if CONFIG_ONE_SIDED_COMPOUND  // Normative in decoder
+#if CONFIG_ONE_SIDED_COMPOUND || CONFIG_EXT_COMP_REFS  // Normative in decoder
   return !frame_is_intra_only(cm);
 #else
   int i;
@@ -104,7 +104,7 @@
     if (cm->ref_frame_sign_bias[i + 1] != cm->ref_frame_sign_bias[1]) return 1;
 
   return 0;
-#endif
+#endif  // CONFIG_ONE_SIDED_COMPOUND || CONFIG_EXT_COMP_REFS
 }
 
 static void setup_compound_reference_mode(AV1_COMMON *cm) {
@@ -290,6 +290,15 @@
 #endif
 
   if (cm->reference_mode != SINGLE_REFERENCE) {
+#if CONFIG_EXT_COMP_REFS
+    for (i = 0; i < COMP_REF_TYPE_CONTEXTS; ++i)
+      av1_diff_update_prob(r, &fc->comp_ref_type_prob[i], ACCT_STR);
+
+    for (i = 0; i < UNI_COMP_REF_CONTEXTS; ++i)
+      for (j = 0; j < (UNIDIR_COMP_REFS - 1); ++j)
+        av1_diff_update_prob(r, &fc->uni_comp_ref_prob[i][j], ACCT_STR);
+#endif  // CONFIG_EXT_COMP_REFS
+
     for (i = 0; i < REF_CONTEXTS; ++i) {
 #if CONFIG_EXT_REFS
       for (j = 0; j < (FWD_REFS - 1); ++j)
@@ -5213,6 +5222,12 @@
 #endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
   assert(!memcmp(cm->counts.comp_inter, zero_counts.comp_inter,
                  sizeof(cm->counts.comp_inter)));
+#if CONFIG_EXT_COMP_REFS
+  assert(!memcmp(cm->counts.comp_ref_type, zero_counts.comp_ref_type,
+                 sizeof(cm->counts.comp_ref_type)));
+  assert(!memcmp(cm->counts.uni_comp_ref, zero_counts.uni_comp_ref,
+                 sizeof(cm->counts.uni_comp_ref)));
+#endif  // CONFIG_EXT_COMP_REFS
   assert(!memcmp(cm->counts.single_ref, zero_counts.single_ref,
                  sizeof(cm->counts.single_ref)));
   assert(!memcmp(cm->counts.comp_ref, zero_counts.comp_ref,
diff --git a/av1/decoder/decodemv.c b/av1/decoder/decodemv.c
index 246bf4e..a7d6693 100644
--- a/av1/decoder/decodemv.c
+++ b/av1/decoder/decodemv.c
@@ -1288,6 +1288,24 @@
   aom_read(r, av1_get_pred_prob_##pname(cm, xd), ACCT_STR)
 #endif
 
+#if CONFIG_EXT_COMP_REFS
+static REFERENCE_MODE read_comp_reference_type(AV1_COMMON *cm,
+                                               const MACROBLOCKD *xd,
+                                               aom_reader *r) {
+  const int ctx = av1_get_comp_reference_type_context(cm, xd);
+#if USE_UNI_COMP_REFS
+  const COMP_REFERENCE_TYPE comp_ref_type = (COMP_REFERENCE_TYPE)aom_read(
+      r, cm->fc->comp_ref_type_prob[ctx], ACCT_STR);
+#else   // !USE_UNI_COMP_REFS
+  // TODO(zoeliu): Temporarily turn off uni-directional comp refs
+  const COMP_REFERENCE_TYPE comp_ref_type = BIDIR_COMP_REFERENCE;
+#endif  // USE_UNI_COMP_REFS
+  FRAME_COUNTS *counts = xd->counts;
+  if (counts) ++counts->comp_ref_type[ctx][comp_ref_type];
+  return comp_ref_type;  // UNIDIR_COMP_REFERENCE or BIDIR_COMP_REFERENCE
+}
+#endif  // CONFIG_EXT_COMP_REFS
+
 // Read the referncence frame
 static void read_ref_frames(AV1_COMMON *const cm, MACROBLOCKD *const xd,
                             aom_reader *r, int segment_id,
@@ -1303,15 +1321,52 @@
     const REFERENCE_MODE mode = read_block_reference_mode(cm, xd, r);
     // FIXME(rbultje) I'm pretty sure this breaks segmentation ref frame coding
     if (mode == COMPOUND_REFERENCE) {
-#if CONFIG_ONE_SIDED_COMPOUND  // Normative in decoder (for low delay)
+#if CONFIG_EXT_COMP_REFS
+      const COMP_REFERENCE_TYPE comp_ref_type =
+          read_comp_reference_type(cm, xd, r);
+
+#if !USE_UNI_COMP_REFS
+      // TODO(zoeliu): Temporarily turn off uni-directional comp refs
+      assert(comp_ref_type == BIDIR_COMP_REFERENCE);
+#endif  // !USE_UNI_COMP_REFS
+
+      if (comp_ref_type == UNIDIR_COMP_REFERENCE) {
+        const int ctx = av1_get_pred_context_uni_comp_ref_p(cm, xd);
+        const int bit = aom_read(r, fc->uni_comp_ref_prob[ctx][0], ACCT_STR);
+        if (counts) ++counts->uni_comp_ref[ctx][0][bit];
+
+        if (bit) {
+          ref_frame[0] = BWDREF_FRAME;
+          ref_frame[1] = ALTREF_FRAME;
+        } else {
+          const int ctx1 = av1_get_pred_context_uni_comp_ref_p1(cm, xd);
+          const int bit1 =
+              aom_read(r, fc->uni_comp_ref_prob[ctx1][1], ACCT_STR);
+          if (counts) ++counts->uni_comp_ref[ctx1][1][bit1];
+
+          if (bit1) {
+            ref_frame[0] = LAST_FRAME;
+            ref_frame[1] = GOLDEN_FRAME;
+          } else {
+            ref_frame[0] = LAST_FRAME;
+            ref_frame[1] = LAST2_FRAME;
+          }
+        }
+
+        return;
+      }
+#endif  // CONFIG_EXT_COMP_REFS
+
+// Normative in decoder (for low delay)
+#if CONFIG_ONE_SIDED_COMPOUND || CONFIG_EXT_COMP_REFS
       const int idx = 1;
-#else
+#else  // !(CONFIG_ONE_SIDED_COMPOUND || CONFIG_EXT_COMP_REFS)
 #if CONFIG_EXT_REFS
       const int idx = cm->ref_frame_sign_bias[cm->comp_bwd_ref[0]];
-#else
+#else   // !CONFIG_EXT_REFS
       const int idx = cm->ref_frame_sign_bias[cm->comp_fixed_ref];
 #endif  // CONFIG_EXT_REFS
-#endif
+#endif  // CONFIG_ONE_SIDED_COMPOUND || CONFIG_EXT_COMP_REFS
 
       const int ctx = av1_get_pred_context_comp_ref_p(cm, xd);
 #if CONFIG_VAR_REFS
@@ -2042,6 +2097,15 @@
   read_ref_frames(cm, xd, r, mbmi->segment_id, mbmi->ref_frame);
   is_compound = has_second_ref(mbmi);
 
+#if CONFIG_EXT_COMP_REFS
+#if !USE_UNI_COMP_REFS
+  // NOTE: uni-directional comp refs disabled
+  if (is_compound)
+    assert(mbmi->ref_frame[0] < BWDREF_FRAME &&
+           mbmi->ref_frame[1] >= BWDREF_FRAME);
+#endif  // !USE_UNI_COMP_REFS
+#endif  // CONFIG_EXT_COMP_REFS
+
 #if CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
   if (!is_compound)
     is_singleref_comp_mode =
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c
index a615443..0385221 100644
--- a/av1/encoder/bitstream.c
+++ b/av1/encoder/bitstream.c
@@ -1259,6 +1259,30 @@
     }
 
     if (is_compound) {
+#if CONFIG_EXT_COMP_REFS
+      const COMP_REFERENCE_TYPE comp_ref_type = has_uni_comp_refs(mbmi)
+                                                    ? UNIDIR_COMP_REFERENCE
+                                                    : BIDIR_COMP_REFERENCE;
+
+#if USE_UNI_COMP_REFS
+      aom_write(w, comp_ref_type, av1_get_comp_reference_type_prob(cm, xd));
+#else   // !USE_UNI_COMP_REFS
+      // NOTE: uni-directional comp refs disabled
+      assert(comp_ref_type == BIDIR_COMP_REFERENCE);
+#endif  // USE_UNI_COMP_REFS
+
+      if (comp_ref_type == UNIDIR_COMP_REFERENCE) {
+        const int bit = mbmi->ref_frame[0] == BWDREF_FRAME;
+        aom_write(w, bit, av1_get_pred_prob_uni_comp_ref_p(cm, xd));
+        if (!bit) {
+          const int bit1 = mbmi->ref_frame[1] == GOLDEN_FRAME;
+          aom_write(w, bit1, av1_get_pred_prob_uni_comp_ref_p1(cm, xd));
+        }
+
+        return;
+      }
+#endif  // CONFIG_EXT_COMP_REFS
+
 #if CONFIG_EXT_REFS
       const int bit = (mbmi->ref_frame[0] == GOLDEN_FRAME ||
                        mbmi->ref_frame[0] == LAST3_FRAME);
@@ -5099,6 +5123,17 @@
     }
 #endif
     if (cm->reference_mode != SINGLE_REFERENCE) {
+#if CONFIG_EXT_COMP_REFS
+      for (i = 0; i < COMP_REF_TYPE_CONTEXTS; i++)
+        av1_cond_prob_diff_update(header_bc, &fc->comp_ref_type_prob[i],
+                                  counts->comp_ref_type[i], probwt);
+
+      for (i = 0; i < UNI_COMP_REF_CONTEXTS; i++)
+        for (j = 0; j < (UNIDIR_COMP_REFS - 1); j++)
+          av1_cond_prob_diff_update(header_bc, &fc->uni_comp_ref_prob[i][j],
+                                    counts->uni_comp_ref[i][j], probwt);
+#endif  // CONFIG_EXT_COMP_REFS
+
       for (i = 0; i < REF_CONTEXTS; i++) {
 #if CONFIG_EXT_REFS
         for (j = 0; j < (FWD_REFS - 1); j++) {
diff --git a/av1/encoder/encodeframe.c b/av1/encoder/encodeframe.c
index ddecf49..7964e03 100644
--- a/av1/encoder/encodeframe.c
+++ b/av1/encoder/encodeframe.c
@@ -1589,24 +1589,49 @@
         }
 
         if (has_second_ref(mbmi)) {
-#if CONFIG_EXT_REFS
-          const int bit = (ref0 == GOLDEN_FRAME || ref0 == LAST3_FRAME);
+#if CONFIG_EXT_COMP_REFS
+          const COMP_REFERENCE_TYPE comp_ref_type = has_uni_comp_refs(mbmi)
+                                                        ? UNIDIR_COMP_REFERENCE
+                                                        : BIDIR_COMP_REFERENCE;
+#if !USE_UNI_COMP_REFS
+          // TODO(zoeliu): Temporarily turn off uni-directional comp refs
+          assert(comp_ref_type == BIDIR_COMP_REFERENCE);
+#endif  // !USE_UNI_COMP_REFS
+          counts->comp_ref_type[av1_get_comp_reference_type_context(cm, xd)]
+                               [comp_ref_type]++;
 
-          counts->comp_ref[av1_get_pred_context_comp_ref_p(cm, xd)][0][bit]++;
-          if (!bit) {
-            counts->comp_ref[av1_get_pred_context_comp_ref_p1(cm, xd)][1]
-                            [ref0 == LAST_FRAME]++;
+          if (comp_ref_type == UNIDIR_COMP_REFERENCE) {
+            const int bit = (ref0 == BWDREF_FRAME);
+            counts->uni_comp_ref[av1_get_pred_context_uni_comp_ref_p(cm, xd)][0]
+                                [bit]++;
+            if (!bit) {
+              const int bit1 = (ref1 == GOLDEN_FRAME);
+              counts->uni_comp_ref[av1_get_pred_context_uni_comp_ref_p1(cm, xd)]
+                                  [1][bit1]++;
+            }
           } else {
-            counts->comp_ref[av1_get_pred_context_comp_ref_p2(cm, xd)][2]
-                            [ref0 == GOLDEN_FRAME]++;
-          }
+#endif  // CONFIG_EXT_COMP_REFS
+#if CONFIG_EXT_REFS
+            const int bit = (ref0 == GOLDEN_FRAME || ref0 == LAST3_FRAME);
 
-          counts->comp_bwdref[av1_get_pred_context_comp_bwdref_p(cm, xd)][0]
-                             [ref1 == ALTREF_FRAME]++;
+            counts->comp_ref[av1_get_pred_context_comp_ref_p(cm, xd)][0][bit]++;
+            if (!bit) {
+              counts->comp_ref[av1_get_pred_context_comp_ref_p1(cm, xd)][1]
+                              [ref0 == LAST_FRAME]++;
+            } else {
+              counts->comp_ref[av1_get_pred_context_comp_ref_p2(cm, xd)][2]
+                              [ref0 == GOLDEN_FRAME]++;
+            }
+
+            counts->comp_bwdref[av1_get_pred_context_comp_bwdref_p(cm, xd)][0]
+                               [ref1 == ALTREF_FRAME]++;
 #else   // !CONFIG_EXT_REFS
           counts->comp_ref[av1_get_pred_context_comp_ref_p(cm, xd)][0]
                           [ref0 == GOLDEN_FRAME]++;
 #endif  // CONFIG_EXT_REFS
+#if CONFIG_EXT_COMP_REFS
+          }
+#endif  // CONFIG_EXT_COMP_REFS
         } else {
 #if CONFIG_EXT_REFS
           const int bit = (ref0 == ALTREF_FRAME || ref0 == BWDREF_FRAME);
@@ -5062,14 +5087,14 @@
   // side behavior is where the ALT ref buffer has opposite sign bias to
   // the other two.
   if (!frame_is_intra_only(cm)) {
-#if !CONFIG_ONE_SIDED_COMPOUND
+#if !(CONFIG_ONE_SIDED_COMPOUND || CONFIG_EXT_COMP_REFS)
     if ((cm->ref_frame_sign_bias[ALTREF_FRAME] ==
          cm->ref_frame_sign_bias[GOLDEN_FRAME]) ||
         (cm->ref_frame_sign_bias[ALTREF_FRAME] ==
          cm->ref_frame_sign_bias[LAST_FRAME])) {
       cpi->allow_comp_inter_inter = 0;
     } else {
-#endif
+#endif  // !(CONFIG_ONE_SIDED_COMPOUND || CONFIG_EXT_COMP_REFS)
       cpi->allow_comp_inter_inter = 1;
 #if CONFIG_EXT_REFS
       cm->comp_fwd_ref[0] = LAST_FRAME;
@@ -5082,10 +5107,11 @@
     cm->comp_fixed_ref = ALTREF_FRAME;
     cm->comp_var_ref[0] = LAST_FRAME;
     cm->comp_var_ref[1] = GOLDEN_FRAME;
-#endif                          // CONFIG_EXT_REFS
-#if !CONFIG_ONE_SIDED_COMPOUND  // Normative in encoder
+#endif  // CONFIG_EXT_REFS
+#if !(CONFIG_ONE_SIDED_COMPOUND || \
+      CONFIG_EXT_COMP_REFS)  // Normative in encoder
     }
-#endif
+#endif  // !(CONFIG_ONE_SIDED_COMPOUND || CONFIG_EXT_COMP_REFS)
   } else {
     cpi->allow_comp_inter_inter = 0;
   }
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 589dcd9..59894eb 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -282,6 +282,53 @@
 #endif  //  CONFIG_EXT_PARTITION
 }
 
+#ifdef CONFIG_COLLECT_REF_FRAME_STATS
+static void collect_ref_frame_stats() {
+  AV1_COMMON *const cm = &cpi->common;
+  ThreadData *td = &cpi->td;
+  FRAME_COUNTS *const counts = td->counts;
+  int i, j;
+
+  printf(" %d,", cm->frame_context_idx);
+
+  for (i = 0; i < COMP_INTER_CONTEXTS; ++i) {
+    // 0: single ref i.e. !has_second_ref(mbmi)
+    // 1: comp ref i.e. has_second_ref(mbmi)
+    printf(" %u,", counts->comp_inter[i][0]);
+  }
+
+  for (i = 0; i < COMP_REF_TYPE_CONTEXTS; ++i) {
+    // 0: UNIDIR_COMP_REFERENCE
+    // 1: BIDIR_COMP_REFERENCE
+    printf(" %u,", counts->comp_ref_type[i][0]);
+  }
+
+  // {LAST, LAST2}, {LAST, GOLDEN}, and {BWDREF, ALTREF}
+  for (i = 0; i < UNI_COMP_REF_CONTEXTS; ++i) {
+    for (j = 0; j < (UNIDIR_COMP_REFS - 1); ++j)
+      printf(" %u,", counts->uni_comp_ref[i][j][0]);
+  }
+
+  // Single ref stats
+  for (i = 0; i < REF_CONTEXTS; ++i) {
+    for (j = 0; j < (SINGLE_REFS - 1); ++j)
+      printf(" %u,", counts->single_ref[i][j][0]);
+  }
+
+  // Comp ref stats
+  for (i = 0; i < REF_CONTEXTS; ++i) {
+    for (j = 0; j < (FWD_REFS - 1); ++j)
+      printf(" %u,", counts->comp_ref[i][j][0]);
+  }
+  for (i = 0; i < REF_CONTEXTS; ++i) {
+    for (j = 0; j < (BWD_REFS - 1); ++j)
+      printf(" %u,", counts->comp_bwdref[i][j][0]);
+  }
+
+  printf("\n");
+}
+#endif  // CONFIG_COLLECT_REF_FRAME_STATS
+
 static void setup_frame(AV1_COMP *cpi) {
   AV1_COMMON *const cm = &cpi->common;
   // Set up entropy context depending on frame type. The decoder mandates
@@ -4395,7 +4442,12 @@
 
   if (last3_is_last || last3_is_last2 || last3_is_alt) flags &= ~AOM_LAST3_FLAG;
 
+#if CONFIG_EXT_COMP_REFS
+  if (gld_is_last2) flags &= ~AOM_GOLD_FLAG;
+  if (gld_is_last3) flags &= ~AOM_LAST3_FLAG;
+#else
   if (gld_is_last2 || gld_is_last3) flags &= ~AOM_GOLD_FLAG;
+#endif  // CONFIG_EXT_COMP_REFS
 
 #if CONFIG_ONE_SIDED_COMPOUND  // Changes LL & HL bitstream
   /* Allow biprediction between two identical frames (e.g. bwd_is_last = 1) */
diff --git a/av1/encoder/rd.c b/av1/encoder/rd.c
index 02d5bf1..eb68521 100644
--- a/av1/encoder/rd.c
+++ b/av1/encoder/rd.c
@@ -1058,6 +1058,12 @@
   rd->thresh_mult[THR_COMP_NEAREST_NEARESTL2B] += 1000;
   rd->thresh_mult[THR_COMP_NEAREST_NEARESTL3B] += 1000;
   rd->thresh_mult[THR_COMP_NEAREST_NEARESTGB] += 1000;
+
+#if CONFIG_EXT_COMP_REFS
+  rd->thresh_mult[THR_COMP_NEAREST_NEARESTLL2] += 1000;
+  rd->thresh_mult[THR_COMP_NEAREST_NEARESTLG] += 1000;
+  rd->thresh_mult[THR_COMP_NEAREST_NEARESTBA] += 1000;
+#endif  // CONFIG_EXT_COMP_REFS
 #endif  // CONFIG_EXT_REFS
 
 #else  // CONFIG_EXT_INTER
@@ -1073,6 +1079,11 @@
   rd->thresh_mult[THR_COMP_NEARESTL2B] += 1000;
   rd->thresh_mult[THR_COMP_NEARESTL3B] += 1000;
   rd->thresh_mult[THR_COMP_NEARESTGB] += 1000;
+#if CONFIG_EXT_COMP_REFS
+  rd->thresh_mult[THR_COMP_NEARESTLL2] += 1000;
+  rd->thresh_mult[THR_COMP_NEARESTLG] += 1000;
+  rd->thresh_mult[THR_COMP_NEARESTBA] += 1000;
+#endif  // CONFIG_EXT_COMP_REFS
 #endif  // CONFIG_EXT_REFS
 
 #endif  // CONFIG_EXT_INTER
@@ -1145,6 +1156,38 @@
   rd->thresh_mult[THR_COMP_NEW_NEARGB] += 1700;
   rd->thresh_mult[THR_COMP_NEW_NEWGB] += 2000;
   rd->thresh_mult[THR_COMP_ZERO_ZEROGB] += 2500;
+
+#if CONFIG_EXT_COMP_REFS
+  rd->thresh_mult[THR_COMP_NEAREST_NEARLL2] += 1200;
+  rd->thresh_mult[THR_COMP_NEAR_NEARESTLL2] += 1200;
+  rd->thresh_mult[THR_COMP_NEAR_NEARLL2] += 1200;
+  rd->thresh_mult[THR_COMP_NEAREST_NEWLL2] += 1500;
+  rd->thresh_mult[THR_COMP_NEW_NEARESTLL2] += 1500;
+  rd->thresh_mult[THR_COMP_NEAR_NEWLL2] += 1700;
+  rd->thresh_mult[THR_COMP_NEW_NEARLL2] += 1700;
+  rd->thresh_mult[THR_COMP_NEW_NEWLL2] += 2000;
+  rd->thresh_mult[THR_COMP_ZERO_ZEROLL2] += 2500;
+
+  rd->thresh_mult[THR_COMP_NEAREST_NEARLG] += 1200;
+  rd->thresh_mult[THR_COMP_NEAR_NEARESTLG] += 1200;
+  rd->thresh_mult[THR_COMP_NEAR_NEARLG] += 1200;
+  rd->thresh_mult[THR_COMP_NEAREST_NEWLG] += 1500;
+  rd->thresh_mult[THR_COMP_NEW_NEARESTLG] += 1500;
+  rd->thresh_mult[THR_COMP_NEAR_NEWLG] += 1700;
+  rd->thresh_mult[THR_COMP_NEW_NEARLG] += 1700;
+  rd->thresh_mult[THR_COMP_NEW_NEWLG] += 2000;
+  rd->thresh_mult[THR_COMP_ZERO_ZEROLG] += 2500;
+
+  rd->thresh_mult[THR_COMP_NEAREST_NEARBA] += 1200;
+  rd->thresh_mult[THR_COMP_NEAR_NEARESTBA] += 1200;
+  rd->thresh_mult[THR_COMP_NEAR_NEARBA] += 1200;
+  rd->thresh_mult[THR_COMP_NEAREST_NEWBA] += 1500;
+  rd->thresh_mult[THR_COMP_NEW_NEARESTBA] += 1500;
+  rd->thresh_mult[THR_COMP_NEAR_NEWBA] += 1700;
+  rd->thresh_mult[THR_COMP_NEW_NEARBA] += 1700;
+  rd->thresh_mult[THR_COMP_NEW_NEWBA] += 2000;
+  rd->thresh_mult[THR_COMP_ZERO_ZEROBA] += 2500;
+#endif  // CONFIG_EXT_COMP_REFS
 #endif  // CONFIG_EXT_REFS
 
 #else  // CONFIG_EXT_INTER
@@ -1169,6 +1212,15 @@
   rd->thresh_mult[THR_COMP_NEWL3B] += 2000;
   rd->thresh_mult[THR_COMP_NEARGB] += 1500;
   rd->thresh_mult[THR_COMP_NEWGB] += 2000;
+
+#if CONFIG_EXT_COMP_REFS
+  rd->thresh_mult[THR_COMP_NEARLL2] += 1500;
+  rd->thresh_mult[THR_COMP_NEWLL2] += 2000;
+  rd->thresh_mult[THR_COMP_NEARLG] += 1500;
+  rd->thresh_mult[THR_COMP_NEWLG] += 2000;
+  rd->thresh_mult[THR_COMP_NEARBA] += 1500;
+  rd->thresh_mult[THR_COMP_NEWBA] += 2000;
+#endif  // CONFIG_EXT_COMP_REFS
 #endif  // CONFIG_EXT_REFS
 
   rd->thresh_mult[THR_COMP_ZEROLA] += 2500;
@@ -1183,6 +1235,12 @@
   rd->thresh_mult[THR_COMP_ZEROL2B] += 2500;
   rd->thresh_mult[THR_COMP_ZEROL3B] += 2500;
   rd->thresh_mult[THR_COMP_ZEROGB] += 2500;
+
+#if CONFIG_EXT_COMP_REFS
+  rd->thresh_mult[THR_COMP_ZEROLL2] += 2500;
+  rd->thresh_mult[THR_COMP_ZEROLG] += 2500;
+  rd->thresh_mult[THR_COMP_ZEROBA] += 2500;
+#endif  // CONFIG_EXT_COMP_REFS
 #endif  // CONFIG_EXT_REFS
 
 #endif  // CONFIG_EXT_INTER
diff --git a/av1/encoder/rd.h b/av1/encoder/rd.h
index 5901286..c9de1cd 100644
--- a/av1/encoder/rd.h
+++ b/av1/encoder/rd.h
@@ -156,6 +156,11 @@
   THR_COMP_NEAREST_NEARESTL2B,
   THR_COMP_NEAREST_NEARESTL3B,
   THR_COMP_NEAREST_NEARESTGB,
+#if CONFIG_EXT_COMP_REFS
+  THR_COMP_NEAREST_NEARESTLL2,
+  THR_COMP_NEAREST_NEARESTLG,
+  THR_COMP_NEAREST_NEARESTBA,
+#endif  // CONFIG_EXT_COMP_REFS
 #endif  // CONFIG_EXT_REFS
 
 #else  // CONFIG_EXT_INTER
@@ -171,6 +176,11 @@
   THR_COMP_NEARESTL2B,
   THR_COMP_NEARESTL3B,
   THR_COMP_NEARESTGB,
+#if CONFIG_EXT_COMP_REFS
+  THR_COMP_NEARESTLL2,
+  THR_COMP_NEARESTLG,
+  THR_COMP_NEARESTBA,
+#endif  // CONFIG_EXT_COMP_REFS
 #endif  // CONFIG_EXT_REFS
 
 #endif  // CONFIG_EXT_INTER
@@ -253,6 +263,38 @@
   THR_COMP_NEAR_NEWGB,
   THR_COMP_NEW_NEWGB,
   THR_COMP_ZERO_ZEROGB,
+
+#if CONFIG_EXT_COMP_REFS
+  THR_COMP_NEAR_NEARESTLL2,
+  THR_COMP_NEAREST_NEARLL2,
+  THR_COMP_NEAR_NEARLL2,
+  THR_COMP_NEW_NEARESTLL2,
+  THR_COMP_NEAREST_NEWLL2,
+  THR_COMP_NEW_NEARLL2,
+  THR_COMP_NEAR_NEWLL2,
+  THR_COMP_NEW_NEWLL2,
+  THR_COMP_ZERO_ZEROLL2,
+
+  THR_COMP_NEAR_NEARESTLG,
+  THR_COMP_NEAREST_NEARLG,
+  THR_COMP_NEAR_NEARLG,
+  THR_COMP_NEW_NEARESTLG,
+  THR_COMP_NEAREST_NEWLG,
+  THR_COMP_NEW_NEARLG,
+  THR_COMP_NEAR_NEWLG,
+  THR_COMP_NEW_NEWLG,
+  THR_COMP_ZERO_ZEROLG,
+
+  THR_COMP_NEAR_NEARESTBA,
+  THR_COMP_NEAREST_NEARBA,
+  THR_COMP_NEAR_NEARBA,
+  THR_COMP_NEW_NEARESTBA,
+  THR_COMP_NEAREST_NEWBA,
+  THR_COMP_NEW_NEARBA,
+  THR_COMP_NEAR_NEWBA,
+  THR_COMP_NEW_NEWBA,
+  THR_COMP_ZERO_ZEROBA,
+#endif  // CONFIG_EXT_COMP_REFS
 #endif  // CONFIG_EXT_REFS
 
 #else  // CONFIG_EXT_INTER
@@ -277,6 +319,15 @@
   THR_COMP_NEWL3B,
   THR_COMP_NEARGB,
   THR_COMP_NEWGB,
+
+#if CONFIG_EXT_COMP_REFS
+  THR_COMP_NEARLL2,
+  THR_COMP_NEWLL2,
+  THR_COMP_NEARLG,
+  THR_COMP_NEWLG,
+  THR_COMP_NEARBA,
+  THR_COMP_NEWBA,
+#endif  // CONFIG_EXT_COMP_REFS
 #endif  // CONFIG_EXT_REFS
 
   THR_COMP_ZEROLA,
@@ -291,6 +342,12 @@
   THR_COMP_ZEROL2B,
   THR_COMP_ZEROL3B,
   THR_COMP_ZEROGB,
+
+#if CONFIG_EXT_COMP_REFS
+  THR_COMP_ZEROLL2,
+  THR_COMP_ZEROLG,
+  THR_COMP_ZEROBA,
+#endif  // CONFIG_EXT_COMP_REFS
 #endif  // CONFIG_EXT_REFS
 
 #endif  // CONFIG_EXT_INTER
diff --git a/av1/encoder/rdopt.c b/av1/encoder/rdopt.c
index d3a46b1..a1deed6 100644
--- a/av1/encoder/rdopt.c
+++ b/av1/encoder/rdopt.c
@@ -113,8 +113,14 @@
 #endif  // CONFIG_EXT_REFS
 
 #if CONFIG_EXT_REFS
+#if CONFIG_EXT_COMP_REFS
+#define SECOND_REF_FRAME_MASK                                        \
+  ((1 << ALTREF_FRAME) | (1 << BWDREF_FRAME) | (1 << GOLDEN_FRAME) | \
+   (1 << LAST2_FRAME) | 0x01)  // NOLINT
+#else                          // !CONFIG_EXT_COMP_REFS
 #define SECOND_REF_FRAME_MASK ((1 << ALTREF_FRAME) | (1 << BWDREF_FRAME) | 0x01)
-#else
+#endif  // CONFIG_EXT_COMP_REFS
+#else   // !CONFIG_EXT_REFS
 #define SECOND_REF_FRAME_MASK ((1 << ALTREF_FRAME) | 0x01)
 #endif  // CONFIG_EXT_REFS
 
@@ -257,6 +263,12 @@
   { NEAREST_NEARESTMV, { LAST2_FRAME, BWDREF_FRAME } },
   { NEAREST_NEARESTMV, { LAST3_FRAME, BWDREF_FRAME } },
   { NEAREST_NEARESTMV, { GOLDEN_FRAME, BWDREF_FRAME } },
+
+#if CONFIG_EXT_COMP_REFS
+  { NEAREST_NEARESTMV, { LAST_FRAME, LAST2_FRAME } },
+  { NEAREST_NEARESTMV, { LAST_FRAME, GOLDEN_FRAME } },
+  { NEAREST_NEARESTMV, { BWDREF_FRAME, ALTREF_FRAME } },
+#endif  // CONFIG_EXT_COMP_REFS
 #endif  // CONFIG_EXT_REFS
 
 #else  // CONFIG_EXT_INTER
@@ -272,6 +284,12 @@
   { NEARESTMV, { LAST2_FRAME, BWDREF_FRAME } },
   { NEARESTMV, { LAST3_FRAME, BWDREF_FRAME } },
   { NEARESTMV, { GOLDEN_FRAME, BWDREF_FRAME } },
+
+#if CONFIG_EXT_COMP_REFS
+  { NEARESTMV, { LAST_FRAME, LAST2_FRAME } },
+  { NEARESTMV, { LAST_FRAME, GOLDEN_FRAME } },
+  { NEARESTMV, { BWDREF_FRAME, ALTREF_FRAME } },
+#endif  // CONFIG_EXT_COMP_REFS
 #endif  // CONFIG_EXT_REFS
 #endif  // CONFIG_EXT_INTER
 
@@ -352,9 +370,35 @@
   { NEAR_NEWMV, { GOLDEN_FRAME, BWDREF_FRAME } },
   { NEW_NEWMV, { GOLDEN_FRAME, BWDREF_FRAME } },
   { ZERO_ZEROMV, { GOLDEN_FRAME, BWDREF_FRAME } },
+
+#if CONFIG_EXT_COMP_REFS
+  { NEAR_NEARMV, { LAST_FRAME, LAST2_FRAME } },
+  { NEW_NEARESTMV, { LAST_FRAME, LAST2_FRAME } },
+  { NEAREST_NEWMV, { LAST_FRAME, LAST2_FRAME } },
+  { NEW_NEARMV, { LAST_FRAME, LAST2_FRAME } },
+  { NEAR_NEWMV, { LAST_FRAME, LAST2_FRAME } },
+  { NEW_NEWMV, { LAST_FRAME, LAST2_FRAME } },
+  { ZERO_ZEROMV, { LAST_FRAME, LAST2_FRAME } },
+
+  { NEAR_NEARMV, { LAST_FRAME, GOLDEN_FRAME } },
+  { NEW_NEARESTMV, { LAST_FRAME, GOLDEN_FRAME } },
+  { NEAREST_NEWMV, { LAST_FRAME, GOLDEN_FRAME } },
+  { NEW_NEARMV, { LAST_FRAME, GOLDEN_FRAME } },
+  { NEAR_NEWMV, { LAST_FRAME, GOLDEN_FRAME } },
+  { NEW_NEWMV, { LAST_FRAME, GOLDEN_FRAME } },
+  { ZERO_ZEROMV, { LAST_FRAME, GOLDEN_FRAME } },
+
+  { NEAR_NEARMV, { BWDREF_FRAME, ALTREF_FRAME } },
+  { NEW_NEARESTMV, { BWDREF_FRAME, ALTREF_FRAME } },
+  { NEAREST_NEWMV, { BWDREF_FRAME, ALTREF_FRAME } },
+  { NEW_NEARMV, { BWDREF_FRAME, ALTREF_FRAME } },
+  { NEAR_NEWMV, { BWDREF_FRAME, ALTREF_FRAME } },
+  { NEW_NEWMV, { BWDREF_FRAME, ALTREF_FRAME } },
+  { ZERO_ZEROMV, { BWDREF_FRAME, ALTREF_FRAME } },
+#endif  // CONFIG_EXT_COMP_REFS
 #endif  // CONFIG_EXT_REFS
 
-#else  // CONFIG_EXT_INTER
+#else  // !CONFIG_EXT_INTER
 
   { NEARMV, { LAST_FRAME, ALTREF_FRAME } },
   { NEWMV, { LAST_FRAME, ALTREF_FRAME } },
@@ -376,6 +420,15 @@
   { NEWMV, { LAST3_FRAME, BWDREF_FRAME } },
   { NEARMV, { GOLDEN_FRAME, BWDREF_FRAME } },
   { NEWMV, { GOLDEN_FRAME, BWDREF_FRAME } },
+
+#if CONFIG_EXT_COMP_REFS
+  { NEARMV, { LAST_FRAME, LAST2_FRAME } },
+  { NEWMV, { LAST_FRAME, LAST2_FRAME } },
+  { NEARMV, { LAST_FRAME, GOLDEN_FRAME } },
+  { NEWMV, { LAST_FRAME, GOLDEN_FRAME } },
+  { NEARMV, { BWDREF_FRAME, ALTREF_FRAME } },
+  { NEWMV, { BWDREF_FRAME, ALTREF_FRAME } },
+#endif  // CONFIG_EXT_COMP_REFS
 #endif  // CONFIG_EXT_REFS
 
   { ZEROMV, { LAST_FRAME, ALTREF_FRAME } },
@@ -390,6 +443,12 @@
   { ZEROMV, { LAST2_FRAME, BWDREF_FRAME } },
   { ZEROMV, { LAST3_FRAME, BWDREF_FRAME } },
   { ZEROMV, { GOLDEN_FRAME, BWDREF_FRAME } },
+
+#if CONFIG_EXT_COMP_REFS
+  { ZEROMV, { LAST_FRAME, LAST2_FRAME } },
+  { ZEROMV, { LAST_FRAME, GOLDEN_FRAME } },
+  { ZEROMV, { BWDREF_FRAME, ALTREF_FRAME } },
+#endif  // CONFIG_EXT_COMP_REFS
 #endif  // CONFIG_EXT_REFS
 
 #endif  // CONFIG_EXT_INTER
@@ -5566,17 +5625,29 @@
 #endif  // CONFIG_EXT_INTER && CONFIG_COMPOUND_SINGLEREF
 }
 
-static void estimate_ref_frame_costs(const AV1_COMMON *cm,
-                                     const MACROBLOCKD *xd, int segment_id,
-                                     unsigned int *ref_costs_single,
-                                     unsigned int *ref_costs_comp,
-                                     aom_prob *comp_mode_p) {
+static void estimate_ref_frame_costs(
+    const AV1_COMMON *cm, const MACROBLOCKD *xd, int segment_id,
+    unsigned int *ref_costs_single,
+#if CONFIG_EXT_COMP_REFS
+    unsigned int (*ref_costs_comp)[TOTAL_REFS_PER_FRAME],
+#else
+    unsigned int *ref_costs_comp,
+#endif  // CONFIG_EXT_COMP_REFS
+    aom_prob *comp_mode_p) {
   int seg_ref_active =
       segfeature_active(&cm->seg, segment_id, SEG_LVL_REF_FRAME);
   if (seg_ref_active) {
     memset(ref_costs_single, 0,
            TOTAL_REFS_PER_FRAME * sizeof(*ref_costs_single));
+#if CONFIG_EXT_COMP_REFS
+    int ref_frame;
+    for (ref_frame = 0; ref_frame < TOTAL_REFS_PER_FRAME; ++ref_frame)
+      memset(ref_costs_comp[ref_frame], 0,
+             TOTAL_REFS_PER_FRAME * sizeof((*ref_costs_comp)[0]));
+#else
     memset(ref_costs_comp, 0, TOTAL_REFS_PER_FRAME * sizeof(*ref_costs_comp));
+#endif  // CONFIG_EXT_COMP_REFS
+
     *comp_mode_p = 128;
   } else {
     aom_prob intra_inter_p = av1_get_intra_inter_prob(cm, xd);
@@ -5696,6 +5767,57 @@
 
       unsigned int base_cost = av1_cost_bit(intra_inter_p, 1);
 
+#if CONFIG_EXT_COMP_REFS
+      aom_prob comp_ref_type_p = av1_get_comp_reference_type_prob(cm, xd);
+      unsigned int ref_bicomp_costs[TOTAL_REFS_PER_FRAME] = { 0 };
+
+      ref_bicomp_costs[LAST_FRAME] = ref_bicomp_costs[LAST2_FRAME] =
+          ref_bicomp_costs[LAST3_FRAME] = ref_bicomp_costs[GOLDEN_FRAME] =
+#if USE_UNI_COMP_REFS
+              base_cost + av1_cost_bit(comp_ref_type_p, 1);
+#else
+              base_cost;
+#endif  // USE_UNI_COMP_REFS
+      ref_bicomp_costs[BWDREF_FRAME] = ref_bicomp_costs[ALTREF_FRAME] = 0;
+
+      ref_bicomp_costs[LAST_FRAME] += av1_cost_bit(ref_comp_p, 0);
+      ref_bicomp_costs[LAST2_FRAME] += av1_cost_bit(ref_comp_p, 0);
+      ref_bicomp_costs[LAST3_FRAME] += av1_cost_bit(ref_comp_p, 1);
+      ref_bicomp_costs[GOLDEN_FRAME] += av1_cost_bit(ref_comp_p, 1);
+
+      ref_bicomp_costs[LAST_FRAME] += av1_cost_bit(ref_comp_p1, 1);
+      ref_bicomp_costs[LAST2_FRAME] += av1_cost_bit(ref_comp_p1, 0);
+
+      ref_bicomp_costs[LAST3_FRAME] += av1_cost_bit(ref_comp_p2, 0);
+      ref_bicomp_costs[GOLDEN_FRAME] += av1_cost_bit(ref_comp_p2, 1);
+
+      ref_bicomp_costs[BWDREF_FRAME] += av1_cost_bit(bwdref_comp_p, 0);
+      ref_bicomp_costs[ALTREF_FRAME] += av1_cost_bit(bwdref_comp_p, 1);
+
+      int ref0;
+      for (ref0 = LAST_FRAME; ref0 <= GOLDEN_FRAME; ++ref0) {
+        ref_costs_comp[ref0][BWDREF_FRAME] =
+            ref_bicomp_costs[ref0] + ref_bicomp_costs[BWDREF_FRAME];
+        ref_costs_comp[ref0][ALTREF_FRAME] =
+            ref_bicomp_costs[ref0] + ref_bicomp_costs[ALTREF_FRAME];
+      }
+
+      aom_prob uni_comp_ref_p = av1_get_pred_prob_uni_comp_ref_p(cm, xd);
+      aom_prob uni_comp_ref_p1 = av1_get_pred_prob_uni_comp_ref_p1(cm, xd);
+
+      ref_costs_comp[LAST_FRAME][LAST2_FRAME] =
+          base_cost + av1_cost_bit(comp_ref_type_p, 0) +
+          av1_cost_bit(uni_comp_ref_p, 0) + av1_cost_bit(uni_comp_ref_p1, 0);
+      ref_costs_comp[LAST_FRAME][GOLDEN_FRAME] =
+          base_cost + av1_cost_bit(comp_ref_type_p, 0) +
+          av1_cost_bit(uni_comp_ref_p, 0) + av1_cost_bit(uni_comp_ref_p1, 1);
+
+      ref_costs_comp[BWDREF_FRAME][ALTREF_FRAME] =
+          base_cost + av1_cost_bit(comp_ref_type_p, 0) +
+          av1_cost_bit(uni_comp_ref_p, 1);
+
+#else  // !CONFIG_EXT_COMP_REFS
+
       ref_costs_comp[LAST_FRAME] =
 #if CONFIG_EXT_REFS
           ref_costs_comp[LAST2_FRAME] = ref_costs_comp[LAST3_FRAME] =
@@ -5754,7 +5876,18 @@
       ref_costs_comp[LAST_FRAME] += av1_cost_bit(ref_comp_p, 0);
       ref_costs_comp[GOLDEN_FRAME] += av1_cost_bit(ref_comp_p, 1);
 #endif  // CONFIG_EXT_REFS
+#endif  // CONFIG_EXT_COMP_REFS
     } else {
+#if CONFIG_EXT_COMP_REFS
+      int ref0;
+      for (ref0 = LAST_FRAME; ref0 <= GOLDEN_FRAME; ++ref0) {
+        ref_costs_comp[ref0][BWDREF_FRAME] = 512;
+        ref_costs_comp[ref0][ALTREF_FRAME] = 512;
+      }
+      ref_costs_comp[LAST_FRAME][LAST2_FRAME] = 512;
+      ref_costs_comp[LAST_FRAME][GOLDEN_FRAME] = 512;
+      ref_costs_comp[BWDREF_FRAME][ALTREF_FRAME] = 512;
+#else  // !CONFIG_EXT_COMP_REFS
       ref_costs_comp[LAST_FRAME] = 512;
 #if CONFIG_EXT_REFS
       ref_costs_comp[LAST2_FRAME] = 512;
@@ -5763,6 +5896,7 @@
       ref_costs_comp[ALTREF_FRAME] = 512;
 #endif  // CONFIG_EXT_REFS
       ref_costs_comp[GOLDEN_FRAME] = 512;
+#endif  // CONFIG_EXT_COMP_REFS
     }
   }
 }
@@ -9084,7 +9218,11 @@
   int best_mode_skippable = 0;
   int midx, best_mode_index = -1;
   unsigned int ref_costs_single[TOTAL_REFS_PER_FRAME];
+#if CONFIG_EXT_COMP_REFS
+  unsigned int ref_costs_comp[TOTAL_REFS_PER_FRAME][TOTAL_REFS_PER_FRAME];
+#else
   unsigned int ref_costs_comp[TOTAL_REFS_PER_FRAME];
+#endif  // CONFIG_EXT_COMP_REFS
   aom_prob comp_mode_p;
   int64_t best_intra_rd = INT64_MAX;
   unsigned int best_pred_sse = UINT_MAX;
@@ -9304,6 +9442,10 @@
 // Skip checking missing references in both single and compound reference
 // modes. Note that a mode will be skipped iff both reference frames
 // are masked out.
+#if CONFIG_EXT_COMP_REFS
+      ref_frame_skip_mask[0] |= (1 << ref_frame);
+      ref_frame_skip_mask[1] |= SECOND_REF_FRAME_MASK;
+#else  // !CONFIG_EXT_COMP_REFS
 #if CONFIG_EXT_REFS
       if (ref_frame == BWDREF_FRAME || ref_frame == ALTREF_FRAME) {
         ref_frame_skip_mask[0] |= (1 << ref_frame);
@@ -9315,6 +9457,7 @@
 #if CONFIG_EXT_REFS
       }
 #endif  // CONFIG_EXT_REFS
+#endif  // CONFIG_EXT_COMP_REFS
     } else {
       for (i = LAST_FRAME; i <= ALTREF_FRAME; ++i) {
         // Skip fixed mv modes for poor references
@@ -9537,6 +9680,34 @@
         (ref_frame_skip_mask[1] & (1 << AOMMAX(0, second_ref_frame))))
       continue;
 
+#if CONFIG_EXT_COMP_REFS
+// TODO(zoeliu): Following toggle between #if 0/1 and the bug will manifest
+// itself.
+#if 0
+    if (!(cpi->ref_frame_flags & flag_list[ref_frame]) ||
+        (second_ref_frame > INTRA_FRAME &&
+         (!(cpi->ref_frame_flags & flag_list[second_ref_frame]))))
+      printf("Frame=%d, bsize=%d, (mi_row,mi_col)=(%d,%d), ref_frame=%d, "
+             "second_ref_frame=%d\n", cm->current_video_frame, bsize, mi_row,
+             mi_col, ref_frame, second_ref_frame);
+
+    if (!(cpi->ref_frame_flags & flag_list[ref_frame])) continue;
+    if (second_ref_frame > INTRA_FRAME &&
+        (!(cpi->ref_frame_flags & flag_list[second_ref_frame])))
+      continue;
+#endif  // 0
+
+#if !USE_UNI_COMP_REFS
+    // NOTE(zoeliu): Temporarily disable uni-directional comp refs
+    if (second_ref_frame > INTRA_FRAME) {
+      if (!((ref_frame < BWDREF_FRAME) ^ (second_ref_frame < BWDREF_FRAME)))
+        continue;
+    }
+    assert(second_ref_frame <= INTRA_FRAME ||
+           ((ref_frame < BWDREF_FRAME) ^ (second_ref_frame < BWDREF_FRAME)));
+#endif  // !USE_UNI_COMP_REFS
+#endif  // CONFIG_EXT_COMP_REFS
+
     if (mode_skip_mask[ref_frame] & (1 << this_mode)) continue;
 
     // Test best rd so far against threshold for trying this mode.
@@ -10258,10 +10429,14 @@
     // Estimate the reference frame signaling cost and add it
     // to the rolling cost variable.
     if (comp_pred) {
+#if CONFIG_EXT_COMP_REFS
+      rate2 += ref_costs_comp[ref_frame][second_ref_frame];
+#else  // !CONFIG_EXT_COMP_REFS
       rate2 += ref_costs_comp[ref_frame];
 #if CONFIG_EXT_REFS
       rate2 += ref_costs_comp[second_ref_frame];
 #endif  // CONFIG_EXT_REFS
+#endif  // CONFIG_EXT_COMP_REFS
     } else {
       rate2 += ref_costs_single[ref_frame];
     }
@@ -10981,7 +11156,11 @@
   int i;
   int64_t best_pred_diff[REFERENCE_MODES];
   unsigned int ref_costs_single[TOTAL_REFS_PER_FRAME];
+#if CONFIG_EXT_COMP_REFS
+  unsigned int ref_costs_comp[TOTAL_REFS_PER_FRAME][TOTAL_REFS_PER_FRAME];
+#else
   unsigned int ref_costs_comp[TOTAL_REFS_PER_FRAME];
+#endif  // CONFIG_EXT_COMP_REFS
   aom_prob comp_mode_p;
   InterpFilter best_filter = SWITCHABLE;
   int64_t this_rd = INT64_MAX;
diff --git a/configure b/configure
index 0f511ed..f59bf05 100755
--- a/configure
+++ b/configure
@@ -317,6 +317,7 @@
     compound_singleref
     aom_qm
     one_sided_compound
+    ext_comp_refs
     smooth_hv
     var_refs
     rect_intra_pred