diff --git a/tcg/s390/tcg-target.c b/tcg/s390/tcg-target.c
index 5af8bc9..4e3fb8b 100644
--- a/tcg/s390/tcg-target.c
+++ b/tcg/s390/tcg-target.c
@@ -114,6 +114,15 @@ typedef enum S390Opcode {
     RI_OILH     = 0xa50a,
     RI_OILL     = 0xa50b,
 
+    RIE_CGIJ    = 0xec7c,
+    RIE_CGRJ    = 0xec64,
+    RIE_CIJ     = 0xec7e,
+    RIE_CLGRJ   = 0xec65,
+    RIE_CLIJ    = 0xec7f,
+    RIE_CLGIJ   = 0xec7d,
+    RIE_CLRJ    = 0xec77,
+    RIE_CRJ     = 0xec76,
+
     RRE_AGR     = 0xb908,
     RRE_CGR     = 0xb920,
     RRE_CLGR    = 0xb921,
@@ -1100,6 +1109,7 @@ static void tgen64_xori(TCGContext *s, TCGReg dest, tcg_target_ulong val)
 static int tgen_cmp(TCGContext *s, TCGType type, TCGCond c, TCGReg r1,
                     TCGArg c2, int c2const)
 {
+    _Bool is_unsigned = (c > TCG_COND_GT);
     if (c2const) {
         if (c2 == 0) {
             if (type == TCG_TYPE_I32) {
@@ -1109,15 +1119,13 @@ static int tgen_cmp(TCGContext *s, TCGType type, TCGCond c, TCGReg r1,
             }
             return tcg_cond_to_ltr_cond[c];
         } else {
-            if (c > TCG_COND_GT) {
-                /* unsigned */
+            if (is_unsigned) {
                 if (type == TCG_TYPE_I32) {
                     tcg_out_insn(s, RIL, CLFI, r1, c2);
                 } else {
                     tcg_out_insn(s, RIL, CLGFI, r1, c2);
                 }
             } else {
-                /* signed */
                 if (type == TCG_TYPE_I32) {
                     tcg_out_insn(s, RIL, CFI, r1, c2);
                 } else {
@@ -1126,15 +1134,13 @@ static int tgen_cmp(TCGContext *s, TCGType type, TCGCond c, TCGReg r1,
             }
         }
     } else {
-        if (c > TCG_COND_GT) {
-            /* unsigned */
+        if (is_unsigned) {
             if (type == TCG_TYPE_I32) {
                 tcg_out_insn(s, RR, CLR, r1, c2);
             } else {
                 tcg_out_insn(s, RRE, CLGR, r1, c2);
             }
         } else {
-            /* signed */
             if (type == TCG_TYPE_I32) {
                 tcg_out_insn(s, RR, CR, r1, c2);
             } else {
@@ -1185,10 +1191,92 @@ static void tgen_branch(TCGContext *s, int cc, int labelno)
     }
 }
 
+static void tgen_compare_branch(TCGContext *s, S390Opcode opc, int cc,
+                                TCGReg r1, TCGReg r2, int labelno)
+{
+    TCGLabel* l = &s->labels[labelno];
+    tcg_target_long off;
+
+    if (l->has_value) {
+        off = (l->u.value - (tcg_target_long)s->code_ptr) >> 1;
+    } else {
+        /* We need to keep the offset unchanged for retranslation.  */
+        off = ((int16_t *)s->code_ptr)[1];
+        tcg_out_reloc(s, s->code_ptr + 2, R_390_PC16DBL, labelno, -2);
+    }
+
+    tcg_out16(s, (opc & 0xff00) | (r1 << 4) | r2);
+    tcg_out16(s, off);
+    tcg_out16(s, cc << 12 | (opc & 0xff));
+}
+
+static void tgen_compare_imm_branch(TCGContext *s, S390Opcode opc, int cc,
+                                    TCGReg r1, int i2, int labelno)
+{
+    TCGLabel* l = &s->labels[labelno];
+    tcg_target_long off;
+
+    if (l->has_value) {
+        off = (l->u.value - (tcg_target_long)s->code_ptr) >> 1;
+    } else {
+        /* We need to keep the offset unchanged for retranslation.  */
+        off = ((int16_t *)s->code_ptr)[1];
+        tcg_out_reloc(s, s->code_ptr + 2, R_390_PC16DBL, labelno, -2);
+    }
+
+    tcg_out16(s, (opc & 0xff00) | (r1 << 4) | cc);
+    tcg_out16(s, off);
+    tcg_out16(s, (i2 << 8) | (opc & 0xff));
+}
+
 static void tgen_brcond(TCGContext *s, TCGType type, TCGCond c,
                         TCGReg r1, TCGArg c2, int c2const, int labelno)
 {
-    int cc = tgen_cmp(s, type, c, r1, c2, c2const);
+    int cc;
+
+    if (facilities & FACILITY_GEN_INST_EXT) {
+        _Bool is_unsigned = (c > TCG_COND_GT);
+        _Bool in_range;
+        S390Opcode opc;
+
+        cc = tcg_cond_to_s390_cond[c];
+
+        if (!c2const) {
+            opc = (type == TCG_TYPE_I32
+                   ? (is_unsigned ? RIE_CLRJ : RIE_CRJ)
+                   : (is_unsigned ? RIE_CLGRJ : RIE_CGRJ));
+            tgen_compare_branch(s, opc, cc, r1, c2, labelno);
+            return;
+        }
+
+        /* COMPARE IMMEDIATE AND BRANCH RELATIVE has an 8-bit immediate field.
+           If the immediate we've been given does not fit that range, we'll
+           fall back to separate compare and branch instructions using the
+           larger comparison range afforded by COMPARE IMMEDIATE.  */
+        if (type == TCG_TYPE_I32) {
+            if (is_unsigned) {
+                opc = RIE_CLIJ;
+                in_range = (uint32_t)c2 == (uint8_t)c2;
+            } else {
+                opc = RIE_CIJ;
+                in_range = (int32_t)c2 == (int8_t)c2;
+            }
+        } else {
+            if (is_unsigned) {
+                opc = RIE_CLGIJ;
+                in_range = (uint64_t)c2 == (uint8_t)c2;
+            } else {
+                opc = RIE_CGIJ;
+                in_range = (int64_t)c2 == (int8_t)c2;
+            }
+        }
+        if (in_range) {
+            tgen_compare_imm_branch(s, opc, cc, r1, c2, labelno);
+            return;
+        }
+    }
+
+    cc = tgen_cmp(s, type, c, r1, c2, c2const);
     tgen_branch(s, cc, labelno);
 }
 
