addHRegUse(u, HRmWrite, insn->variant.move.dst);
break;
+ case S390_INSN_MEMCPY:
+ s390_amode_get_reg_usage(u, insn->variant.memcpy.src);
+ s390_amode_get_reg_usage(u, insn->variant.memcpy.dst);
+ break;
+
case S390_INSN_COND_MOVE:
s390_opnd_RMI_get_reg_usage(u, insn->variant.cond_move.src);
addHRegUse(u, HRmWrite, insn->variant.cond_move.dst);
insn->variant.move.src = lookupHRegRemap(m, insn->variant.move.src);
break;
+ case S390_INSN_MEMCPY:
+ s390_amode_map_regs(m, insn->variant.memcpy.dst);
+ s390_amode_map_regs(m, insn->variant.memcpy.src);
+ break;
+
case S390_INSN_COND_MOVE:
insn->variant.cond_move.dst = lookupHRegRemap(m, insn->variant.cond_move.dst);
s390_opnd_RMI_map_regs(m, &insn->variant.cond_move.src);
}
+static UChar *
+s390_emit_MVC(UChar *p, UInt l, UChar b1, UShort d1, UChar b2, UShort d2)
+{
+ if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM))
+ s390_disasm(ENC3(MNM, UDLB, UDXB), "mvc", d1, l, b1, d2, 0, b2);
+
+ return emit_SSa(p, 0xd20000000000ULL, l, b1, d1, b2, d2);
+}
+
+
static UChar *
s390_emit_MVI(UChar *p, UChar i2, UChar b1, UShort d1)
{
}
+s390_insn *
+s390_insn_memcpy(UChar size, s390_amode *dst, s390_amode *src)
+{
+ s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
+
+ /* This insn will be mapped to MVC which requires base register
+ plus 12-bit displacement */
+ vassert(src->tag == S390_AMODE_B12);
+ vassert(dst->tag == S390_AMODE_B12);
+
+ insn->tag = S390_INSN_MEMCPY;
+ insn->size = size;
+ insn->variant.memcpy.src = src;
+ insn->variant.memcpy.dst = dst;
+
+ vassert(size == 1 || size == 2 || size == 4 || size == 8);
+
+ return insn;
+}
+
+
s390_insn *
s390_insn_cond_move(UChar size, s390_cc_t cond, HReg dst, s390_opnd_RMI src)
{
insn->variant.move.src);
break;
+ case S390_INSN_MEMCPY:
+ s390_sprintf(buf, "%M %A,%A", "v-memcpy", insn->variant.memcpy.dst,
+ insn->variant.memcpy.src);
+ break;
+
case S390_INSN_COND_MOVE:
s390_sprintf(buf, "%M if (%C) %R,%O", "v-move",
insn->variant.cond_move.cond, insn->variant.cond_move.dst,
}
+static UChar *
+s390_insn_memcpy_emit(UChar *buf, const s390_insn *insn)
+{
+ s390_amode *dst = insn->variant.memcpy.dst;
+ s390_amode *src = insn->variant.memcpy.src;
+
+ return s390_emit_MVC(buf, insn->size - 1, hregNumber(dst->b), dst->d,
+ hregNumber(src->b), src->d);
+}
+
+
static UChar *
s390_insn_load_immediate_emit(UChar *buf, const s390_insn *insn)
{
end = s390_insn_move_emit(buf, insn);
break;
+ case S390_INSN_MEMCPY:
+ end = s390_insn_memcpy_emit(buf, insn);
+ break;
+
case S390_INSN_COND_MOVE:
end = s390_insn_cond_move_emit(buf, insn);
break;
S390_INSN_LOAD, /* load register from memory */
S390_INSN_STORE, /* store register to memory */
S390_INSN_MOVE, /* from register to register */
+ S390_INSN_MEMCPY, /* from memory to memory */
S390_INSN_COND_MOVE, /* conditonal "move" to register */
S390_INSN_LOAD_IMMEDIATE,
S390_INSN_ALU,
HReg dst;
HReg src;
} move;
+ struct {
+ s390_amode *dst;
+ s390_amode *src;
+ } memcpy;
struct {
s390_cc_t cond;
HReg dst;
s390_insn *s390_insn_load(UChar size, HReg dst, s390_amode *src);
s390_insn *s390_insn_store(UChar size, s390_amode *dst, HReg src);
s390_insn *s390_insn_move(UChar size, HReg dst, HReg src);
+s390_insn *s390_insn_memcpy(UChar size, s390_amode *dst, s390_amode *src);
s390_insn *s390_insn_cond_move(UChar size, s390_cc_t cond, HReg dst,
s390_opnd_RMI src);
s390_insn *s390_insn_load_immediate(UChar size, HReg dst, ULong val);
addInstr(env, s390_insn_mimm(sizeofIRType(tyd), am, value));
return;
}
+ /* Check whether we can use a memcpy here. Currently, the restriction
+ is that both amodes need to be B12, so MVC can be emitted.
+ We do not consider a store whose data expression is a load because
+ we don't want to deal with overlapping locations. */
+ /* store(get) never overlaps*/
+ if (am->tag == S390_AMODE_B12 &&
+ stmt->Ist.Store.data->tag == Iex_Get) {
+ UInt offset = stmt->Ist.Store.data->Iex.Get.offset;
+ s390_amode *from = s390_amode_for_guest_state(offset);
+ addInstr(env, s390_insn_memcpy(sizeofIRType(tyd), am, from));
+ return;
+ }
+ /* General case: compile data into a register */
src = s390_isel_int_expr(env, stmt->Ist.Store.data);
break;
addInstr(env, s390_insn_mimm(sizeofIRType(tyd), am, value));
return;
}
+ /* Check whether we can use a memcpy here. Currently, the restriction
+ is that both amodes need to be B12, so MVC can be emitted. */
+ /* put(load) never overlaps */
+ if (am->tag == S390_AMODE_B12 &&
+ stmt->Ist.Put.data->tag == Iex_Load) {
+ if (stmt->Ist.Put.data->Iex.Load.end != Iend_BE) goto stmt_fail;
+ IRExpr *data = stmt->Ist.Put.data->Iex.Load.addr;
+ s390_amode *from = s390_isel_amode(env, data);
+ UInt size = sizeofIRType(tyd);
+
+ if (from->tag == S390_AMODE_B12) {
+ /* Source can be compiled into a B12 amode. */
+ addInstr(env, s390_insn_memcpy(size, am, from));
+ return;
+ }
+
+ src = newVRegI(env);
+ addInstr(env, s390_insn_load(size, src, from));
+ break;
+ }
+ /* put(get) */
+ if (am->tag == S390_AMODE_B12 &&
+ stmt->Ist.Put.data->tag == Iex_Get) {
+ UInt put_offset = am->d;
+ UInt get_offset = stmt->Ist.Put.data->Iex.Get.offset;
+ UInt size = sizeofIRType(tyd);
+ /* don't memcpy in case of overlap */
+ if (put_offset + size <= get_offset ||
+ get_offset + size <= put_offset) {
+ s390_amode *from = s390_amode_for_guest_state(get_offset);
+ addInstr(env, s390_insn_memcpy(size, am, from));
+ return;
+ }
+ goto no_memcpy_put;
+ }
+ /* General case: compile data into a register */
+no_memcpy_put:
src = s390_isel_int_expr(env, stmt->Ist.Put.data);
break;