diff --git a/tcg/s390/tcg-target.c b/tcg/s390/tcg-target.c
index b150d1a..21ad1a3 100644
--- a/tcg/s390/tcg-target.c
+++ b/tcg/s390/tcg-target.c
@@ -90,17 +90,22 @@ typedef enum S390Opcode {
     RS_SRL      = 0x88,
 
     RXY_CG      = 0xe320,
+    RXY_LB      = 0xe376,
     RXY_LG      = 0xe304,
     RXY_LGB     = 0xe377,
     RXY_LGF     = 0xe314,
     RXY_LGH     = 0xe315,
+    RXY_LHY     = 0xe378,
+    RXY_LLC     = 0xe394,
     RXY_LLGC    = 0xe390,
     RXY_LLGF    = 0xe316,
     RXY_LLGH    = 0xe391,
+    RXY_LLH     = 0xe395,
     RXY_LMG     = 0xeb04,
     RXY_LRV     = 0xe31e,
     RXY_LRVG    = 0xe30f,
     RXY_LRVH    = 0xe31f,
+    RXY_LY      = 0xe358,
     RXY_STCY    = 0xe372,
     RXY_STG     = 0xe324,
     RXY_STHY    = 0xe370,
@@ -108,7 +113,10 @@ typedef enum S390Opcode {
     RXY_STRV    = 0xe33e,
     RXY_STRVG   = 0xe32f,
     RXY_STRVH   = 0xe33f,
+    RXY_STY     = 0xe350,
 
+    RX_L        = 0x58,
+    RX_LH       = 0x48,
     RX_ST       = 0x50,
     RX_STC      = 0x42,
     RX_STH      = 0x40,
@@ -362,22 +370,52 @@ static inline void tcg_out_movi(TCGContext *s, TCGType type,
     }
 }
 
-/* load data without address translation or endianness conversion */
-static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg data,
-                       TCGReg base, tcg_target_long ofs)
+
+/* Emit a load/store type instruction.  Inputs are:
+   DATA:     The register to be loaded or stored.
+   BASE+OFS: The effective address.
+   OPC_RX:   If the operation has an RX format opcode (e.g. STC), otherwise 0.
+   OPC_RXY:  The RXY format opcode for the operation (e.g. STCY).  */
+
+static void tcg_out_ldst(TCGContext *s, S390Opcode opc_rx, S390Opcode opc_rxy,
+                         TCGReg data, TCGReg base, tcg_target_long ofs)
 {
-    S390Opcode op;
+    TCGReg index = 0;
+
+    if (ofs < -0x80000 || ofs >= 0x80000) {
+        /* Combine the low 16 bits of the offset with the actual load insn;
+           the high 48 bits must come from an immediate load.  */
+        index = TCG_REG_R13;
+        tcg_out_movi(s, TCG_TYPE_PTR, index, ofs & ~0xffff);
+        ofs &= 0xffff;
+    }
 
-    op = (type == TCG_TYPE_I32) ? RXY_LLGF : RXY_LG;
+    if (opc_rx && ofs >= 0 && ofs < 0x1000) {
+        tcg_out_insn_RX(s, opc_rx, data, base, index, ofs);
+    } else {
+        tcg_out_insn_RXY(s, opc_rxy, data, base, index, ofs);
+    }
+}
 
-    if (ofs < -0x80000 || ofs > 0x7ffff) {
-        /* load the displacement */
-        tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R13, ofs);
-        /* load the data */
-        tcg_out_insn_RXY(s, op, data, base, TCG_REG_R13, 0);
+
+/* load data without address translation or endianness conversion */
+static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg data,
+                              TCGReg base, tcg_target_long ofs)
+{
+    if (type == TCG_TYPE_I32) {
+        tcg_out_ldst(s, RX_L, RXY_LY, data, base, ofs);
     } else {
-        /* load the data */
-        tcg_out_insn_RXY(s, op, data, base, 0, ofs);
+        tcg_out_ldst(s, 0, RXY_LG, data, base, ofs);
+    }
+}
+
+static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg data,
+                              TCGReg base, tcg_target_long ofs)
+{
+    if (type == TCG_TYPE_I32) {
+        tcg_out_ldst(s, RX_ST, RXY_STY, data, base, ofs);
+    } else {
+        tcg_out_ldst(s, 0, RXY_STG, data, base, ofs);
     }
 }
 
@@ -693,28 +731,6 @@ static void tcg_out_qemu_st(TCGContext* s, const TCGArg* args, int opc)
     tcg_finish_qemu_ldst(s, label2_ptr);
 }
 
-static inline void tcg_out_st(TCGContext *s, TCGType type, int arg,
-                              int arg1, tcg_target_long arg2)
-{
-    dprintf("tcg_out_st arg 0x%x arg1 0x%x arg2 0x%lx\n", arg, arg1, arg2);
-
-    if (type == TCG_TYPE_I32) {
-        if (((long)arg2) < -0x800 || ((long)arg2) > 0x7ff) {
-            tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R13, arg2);
-            tcg_out_insn(s, RRE, AGR, 13, arg1);
-            tcg_out_insn(s, RX, ST, arg, TCG_REG_R13, 0, 0);
-        } else {
-            tcg_out_insn(s, RX, ST, arg, arg1, 0, arg2);
-        }
-    }
-    else {
-        if (((long)arg2) < -0x80000 || ((long)arg2) > 0x7ffff) {
-            tcg_abort();
-        }
-        tcg_out_insn(s, RXY, STG, arg, arg1, 0, arg2);
-    }
-}
-
 static inline void tcg_out_op(TCGContext *s, TCGOpcode opc,
                 const TCGArg *args, const int *const_args)
 {
@@ -780,51 +796,41 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc,
         break;
 
     case INDEX_op_ld8u_i32:
+        tcg_out_ldst(s, 0, RXY_LLC, args[0], args[1], args[2]);
+        break;
     case INDEX_op_ld8u_i64:
-        if ((long)args[2] > -0x80000 && (long)args[2] < 0x7ffff) {
-            tcg_out_insn(s, RXY, LLGC, args[0], args[1], 0, args[2]);
-        } else {
-            /* XXX displacement too large, have to calculate address manually */
-            tcg_abort();
-        }
+        tcg_out_ldst(s, 0, RXY_LLGC, args[0], args[1], args[2]);
         break;
 
     case INDEX_op_ld8s_i32:
-        /* XXX */
-        tcg_abort();
+        tcg_out_ldst(s, 0, RXY_LB, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld8s_i64:
+        tcg_out_ldst(s, 0, RXY_LGB, args[0], args[1], args[2]);
         break;
 
     case INDEX_op_ld16u_i32:
-        if ((long)args[2] > -0x80000 && (long)args[2] < 0x7ffff) {
-            tcg_out_insn(s, RXY, LLGH, args[0], args[1], 0, args[2]);
-        } else {
-            /* XXX displacement too large, have to calculate address manually */
-            tcg_abort();
-        }
+        tcg_out_ldst(s, 0, RXY_LLH, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld16u_i64:
+        tcg_out_ldst(s, 0, RXY_LLGH, args[0], args[1], args[2]);
         break;
 
     case INDEX_op_ld16s_i32:
-        /* XXX */
-        tcg_abort();
+        tcg_out_ldst(s, RX_LH, RXY_LHY, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld16s_i64:
+        tcg_out_ldst(s, 0, RXY_LGH, args[0], args[1], args[2]);
         break;
 
     case INDEX_op_ld_i32:
-    case INDEX_op_ld32u_i64:
         tcg_out_ld(s, TCG_TYPE_I32, args[0], args[1], args[2]);
         break;
-
+    case INDEX_op_ld32u_i64:
+        tcg_out_ldst(s, 0, RXY_LLGF, args[0], args[1], args[2]);
+        break;
     case INDEX_op_ld32s_i64:
-        if (args[2] < -0x80000 || args[2] > 0x7ffff) {
-            /* load the displacement */
-            tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R13, args[2]);
-            /* add the address */
-            tcg_out_insn(s, RRE, AGR, TCG_REG_R13, args[1]);
-            /* load the data (sign-extended) */
-            tcg_out_insn(s, RXY, LGF, args[0], TCG_REG_R13, 0, 0);
-        } else {
-            /* load the data (sign-extended) */
-            tcg_out_insn(s, RXY, LGF, args[0], args[1], 0, args[2]);
-        }
+        tcg_out_ldst(s, 0, RXY_LGF, args[0], args[1], args[2]);
         break;
 
     case INDEX_op_ld_i64:
@@ -833,28 +839,12 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc,
 
     case INDEX_op_st8_i32:
     case INDEX_op_st8_i64:
-        if (((long)args[2]) >= -0x800 && ((long)args[2]) < 0x800) {
-            tcg_out_insn(s, RX, STC, args[0], args[1], 0, args[2]);
-        } else if (((long)args[2]) >= -0x80000 && ((long)args[2]) < 0x80000) {
-            /* FIXME: requires long displacement facility */
-            tcg_out_insn(s, RXY, STCY, args[0], args[1], 0, args[2]);
-            tcg_abort();
-        } else {
-            tcg_abort();
-        }
+        tcg_out_ldst(s, RX_STC, RXY_STCY, args[0], args[1], args[2]);
         break;
 
     case INDEX_op_st16_i32:
     case INDEX_op_st16_i64:
-        if (((long)args[2]) >= -0x800 && ((long)args[2]) < 0x800) {
-            tcg_out_insn(s, RX, STH, args[0], args[1], 0, args[2]);
-        } else if (((long)args[2]) >= -0x80000 && ((long)args[2]) < 0x80000) {
-            /* FIXME: requires long displacement facility */
-            tcg_out_insn(s, RXY, STHY, args[0], args[1], 0, args[2]);
-            tcg_abort();
-        } else {
-            tcg_abort();
-        }
+        tcg_out_ldst(s, RX_STH, RXY_STHY, args[0], args[1], args[2]);
         break;
 
     case INDEX_op_st_i32:
