#define TCG_CT_CONST_XORI 0x400
#define TCG_CT_CONST_CMPI 0x800
#define TCG_CT_CONST_ADLI 0x1000
+#define TCG_CT_CONST_ZERO 0x2000
/* Several places within the instruction set 0 means "no register"
rather than TCG_REG_R0. */
#endif
static tcg_insn_unit *tb_ret_addr;
-
-/* A list of relevant facilities used by this translator. Some of these
- are required for proper operation, and these are checked at startup. */
-
-#define FACILITY_ZARCH_ACTIVE (1ULL << (63 - 2))
-#define FACILITY_LONG_DISP (1ULL << (63 - 18))
-#define FACILITY_EXT_IMM (1ULL << (63 - 21))
-#define FACILITY_GEN_INST_EXT (1ULL << (63 - 34))
-#define FACILITY_LOAD_ON_COND (1ULL << (63 - 45))
-
-static uint64_t facilities;
+uint64_t s390_facilities;
static void patch_reloc(tcg_insn_unit *code_ptr, int type,
intptr_t value, intptr_t addend)
ct->ct |= TCG_CT_REG;
tcg_regset_set32(ct->u.regs, 0, 0xffff);
break;
- case 'R': /* not R0 */
- ct->ct |= TCG_CT_REG;
- tcg_regset_set32(ct->u.regs, 0, 0xffff);
- tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0);
- break;
case 'L': /* qemu_ld/st constraint */
ct->ct |= TCG_CT_REG;
tcg_regset_set32(ct->u.regs, 0, 0xffff);
case 'C':
ct->ct |= TCG_CT_CONST_CMPI;
break;
+ case 'Z':
+ ct->ct |= TCG_CT_CONST_ZERO;
+ break;
default:
return -1;
}
static int tcg_match_ori(TCGType type, tcg_target_long val)
{
- if (facilities & FACILITY_EXT_IMM) {
+ if (s390_facilities & FACILITY_EXT_IMM) {
if (type == TCG_TYPE_I32) {
/* All 32-bit ORs can be performed with 1 48-bit insn. */
return 1;
if (val == (int16_t)val) {
return 0;
}
- if (facilities & FACILITY_EXT_IMM) {
+ if (s390_facilities & FACILITY_EXT_IMM) {
if (val == (int32_t)val) {
return 0;
}
static int tcg_match_xori(TCGType type, tcg_target_long val)
{
- if ((facilities & FACILITY_EXT_IMM) == 0) {
+ if ((s390_facilities & FACILITY_EXT_IMM) == 0) {
return 0;
}
static int tcg_match_cmpi(TCGType type, tcg_target_long val)
{
- if (facilities & FACILITY_EXT_IMM) {
+ if (s390_facilities & FACILITY_EXT_IMM) {
/* The COMPARE IMMEDIATE instruction is available. */
if (type == TCG_TYPE_I32) {
/* We have a 32-bit immediate and can compare against anything. */
static int tcg_match_add2i(TCGType type, tcg_target_long val)
{
- if (facilities & FACILITY_EXT_IMM) {
+ if (s390_facilities & FACILITY_EXT_IMM) {
if (type == TCG_TYPE_I32) {
return 1;
} else if (val >= -0xffffffffll && val <= 0xffffffffll) {
general-instruction-extensions, then we have MULTIPLY SINGLE
IMMEDIATE with a signed 32-bit, otherwise we have only
MULTIPLY HALFWORD IMMEDIATE, with a signed 16-bit. */
- if (facilities & FACILITY_GEN_INST_EXT) {
+ if (s390_facilities & FACILITY_GEN_INST_EXT) {
return val == (int32_t)val;
} else {
return val == (int16_t)val;
return tcg_match_xori(type, val);
} else if (ct & TCG_CT_CONST_CMPI) {
return tcg_match_cmpi(type, val);
+ } else if (ct & TCG_CT_CONST_ZERO) {
+ return val == 0;
}
return 0;
}
/* Try all 48-bit insns that can load it in one go. */
- if (facilities & FACILITY_EXT_IMM) {
+ if (s390_facilities & FACILITY_EXT_IMM) {
if (sval == (int32_t)sval) {
tcg_out_insn(s, RIL, LGFI, ret, sval);
return;
/* If extended immediates are not present, then we may have to issue
several instructions to load the low 32 bits. */
- if (!(facilities & FACILITY_EXT_IMM)) {
+ if (!(s390_facilities & FACILITY_EXT_IMM)) {
/* A 32-bit unsigned value can be loaded in 2 insns. And given
that the lli_insns loop above did not succeed, we know that
both insns are required. */
/* Insert data into the high 32-bits. */
uval = uval >> 31 >> 1;
- if (facilities & FACILITY_EXT_IMM) {
+ if (s390_facilities & FACILITY_EXT_IMM) {
if (uval < 0x10000) {
tcg_out_insn(s, RI, IIHL, ret, uval);
} else if ((uval & 0xffff) == 0) {
{
intptr_t addr = (intptr_t)abs;
- if ((facilities & FACILITY_GEN_INST_EXT) && !(addr & 1)) {
+ if ((s390_facilities & FACILITY_GEN_INST_EXT) && !(addr & 1)) {
ptrdiff_t disp = tcg_pcrel_diff(s, abs) >> 1;
if (disp == (int32_t)disp) {
if (type == TCG_TYPE_I32) {
static void tgen_ext8s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src)
{
- if (facilities & FACILITY_EXT_IMM) {
+ if (s390_facilities & FACILITY_EXT_IMM) {
tcg_out_insn(s, RRE, LGBR, dest, src);
return;
}
static void tgen_ext8u(TCGContext *s, TCGType type, TCGReg dest, TCGReg src)
{
- if (facilities & FACILITY_EXT_IMM) {
+ if (s390_facilities & FACILITY_EXT_IMM) {
tcg_out_insn(s, RRE, LLGCR, dest, src);
return;
}
static void tgen_ext16s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src)
{
- if (facilities & FACILITY_EXT_IMM) {
+ if (s390_facilities & FACILITY_EXT_IMM) {
tcg_out_insn(s, RRE, LGHR, dest, src);
return;
}
static void tgen_ext16u(TCGContext *s, TCGType type, TCGReg dest, TCGReg src)
{
- if (facilities & FACILITY_EXT_IMM) {
+ if (s390_facilities & FACILITY_EXT_IMM) {
tcg_out_insn(s, RRE, LLGHR, dest, src);
return;
}
tgen_ext32u(s, dest, dest);
return;
}
- if (facilities & FACILITY_EXT_IMM) {
+ if (s390_facilities & FACILITY_EXT_IMM) {
if ((val & valid) == 0xff) {
tgen_ext8u(s, TCG_TYPE_I64, dest, dest);
return;
}
/* Try all 48-bit insns that can perform it in one go. */
- if (facilities & FACILITY_EXT_IMM) {
+ if (s390_facilities & FACILITY_EXT_IMM) {
for (i = 0; i < 2; i++) {
tcg_target_ulong mask = ~(0xffffffffull << i*32);
if (((val | ~valid) & mask) == mask) {
}
}
}
- if ((facilities & FACILITY_GEN_INST_EXT) && risbg_mask(val)) {
+ if ((s390_facilities & FACILITY_GEN_INST_EXT) && risbg_mask(val)) {
tgen_andi_risbg(s, dest, dest, val);
return;
}
return;
}
- if (facilities & FACILITY_EXT_IMM) {
+ if (s390_facilities & FACILITY_EXT_IMM) {
/* Try all 32-bit insns that can perform it in one go. */
for (i = 0; i < 4; i++) {
tcg_target_ulong mask = (0xffffull << i*16);
}
static int tgen_cmp(TCGContext *s, TCGType type, TCGCond c, TCGReg r1,
- TCGArg c2, int c2const)
+ TCGArg c2, bool c2const, bool need_carry)
{
bool is_unsigned = is_unsigned_cond(c);
if (c2const) {
if (c2 == 0) {
+ if (!(is_unsigned && need_carry)) {
+ if (type == TCG_TYPE_I32) {
+ tcg_out_insn(s, RR, LTR, r1, r1);
+ } else {
+ tcg_out_insn(s, RRE, LTGR, r1, r1);
+ }
+ return tcg_cond_to_ltr_cond[c];
+ }
+ /* If we only got here because of load-and-test,
+ and we couldn't use that, then we need to load
+ the constant into a register. */
+ if (!(facilities & FACILITY_EXT_IMM)) {
+ c2 = TCG_TMP0;
+ tcg_out_movi(s, type, c2, 0);
+ goto do_reg;
+ }
+ }
+ if (is_unsigned) {
if (type == TCG_TYPE_I32) {
- tcg_out_insn(s, RR, LTR, r1, r1);
+ tcg_out_insn(s, RIL, CLFI, r1, c2);
} else {
- tcg_out_insn(s, RRE, LTGR, r1, r1);
+ tcg_out_insn(s, RIL, CLGFI, r1, c2);
}
- return tcg_cond_to_ltr_cond[c];
} else {
- 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);
- }
+ if (type == TCG_TYPE_I32) {
+ tcg_out_insn(s, RIL, CFI, r1, c2);
} else {
- if (type == TCG_TYPE_I32) {
- tcg_out_insn(s, RIL, CFI, r1, c2);
- } else {
- tcg_out_insn(s, RIL, CGFI, r1, c2);
- }
+ tcg_out_insn(s, RIL, CGFI, r1, c2);
}
}
} else {
+ do_reg:
if (is_unsigned) {
if (type == TCG_TYPE_I32) {
tcg_out_insn(s, RR, CLR, r1, c2);
do_greater:
/* The result of a compare has CC=2 for GT and CC=3 unused.
ADD LOGICAL WITH CARRY considers (CC & 2) the carry bit. */
- tgen_cmp(s, type, cond, c1, c2, c2const);
+ tgen_cmp(s, type, cond, c1, c2, c2const, true);
tcg_out_movi(s, type, dest, 0);
tcg_out_insn(s, RRE, ALCGR, dest, dest);
return;
break;
}
- cc = tgen_cmp(s, type, cond, c1, c2, c2const);
- if (facilities & FACILITY_LOAD_ON_COND) {
+ cc = tgen_cmp(s, type, cond, c1, c2, c2const, false);
+ if (s390_facilities & FACILITY_LOAD_ON_COND) {
/* Emit: d = 0, t = 1, d = (cc ? t : d). */
tcg_out_movi(s, TCG_TYPE_I64, dest, 0);
tcg_out_movi(s, TCG_TYPE_I64, TCG_TMP0, 1);
TCGReg c1, TCGArg c2, int c2const, TCGReg r3)
{
int cc;
- if (facilities & FACILITY_LOAD_ON_COND) {
- cc = tgen_cmp(s, type, c, c1, c2, c2const);
+ if (s390_facilities & FACILITY_LOAD_ON_COND) {
+ cc = tgen_cmp(s, type, c, c1, c2, c2const, false);
tcg_out_insn(s, RRF, LOCGR, dest, r3, cc);
} else {
c = tcg_invert_cond(c);
- cc = tgen_cmp(s, type, c, c1, c2, c2const);
+ cc = tgen_cmp(s, type, c, c1, c2, c2const, false);
/* Emit: if (cc) goto over; dest = r3; over: */
tcg_out_insn(s, RI, BRC, cc, (4 + 4) >> 1);
}
}
-bool tcg_target_deposit_valid(int ofs, int len)
+static void tgen_deposit(TCGContext *s, TCGReg dest, TCGReg src,
+ int ofs, int len, int z)
{
- return (facilities & FACILITY_GEN_INST_EXT) != 0;
+ int lsb = (63 - ofs);
+ int msb = lsb - (len - 1);
+ tcg_out_risbg(s, dest, src, msb, lsb, ofs, z);
}
-static void tgen_deposit(TCGContext *s, TCGReg dest, TCGReg src,
+static void tgen_extract(TCGContext *s, TCGReg dest, TCGReg src,
int ofs, int len)
{
- int lsb = (63 - ofs);
- int msb = lsb - (len - 1);
- tcg_out_risbg(s, dest, src, msb, lsb, ofs, 0);
+ tcg_out_risbg(s, dest, src, 64 - len, 63, 64 - ofs, 1);
}
static void tgen_gotoi(TCGContext *s, int cc, tcg_insn_unit *dest)
{
int cc;
- if (facilities & FACILITY_GEN_INST_EXT) {
+ if (s390_facilities & FACILITY_GEN_INST_EXT) {
bool is_unsigned = is_unsigned_cond(c);
bool in_range;
S390Opcode opc;
}
}
- cc = tgen_cmp(s, type, c, r1, c2, c2const);
+ cc = tgen_cmp(s, type, c, r1, c2, c2const, false);
tgen_branch(s, cc, l);
}
static TCGReg tcg_out_tlb_read(TCGContext* s, TCGReg addr_reg, TCGMemOp opc,
int mem_index, bool is_ld)
{
- int a_bits = get_alignment_bits(opc);
+ unsigned s_bits = opc & MO_SIZE;
+ unsigned a_bits = get_alignment_bits(opc);
+ unsigned s_mask = (1 << s_bits) - 1;
+ unsigned a_mask = (1 << a_bits) - 1;
int ofs, a_off;
uint64_t tlb_mask;
/* For aligned accesses, we check the first byte and include the alignment
bits within the address. For unaligned access, we check that we don't
cross pages using the address of the last byte of the access. */
- if (a_bits >= 0) {
- /* A byte access or an alignment check required */
- a_off = 0;
- tlb_mask = TARGET_PAGE_MASK | ((1 << a_bits) - 1);
- } else {
- a_off = (1 << (opc & MO_SIZE)) - 1;
- tlb_mask = TARGET_PAGE_MASK;
- }
+ a_off = (a_bits >= s_bits ? 0 : s_mask - a_mask);
+ tlb_mask = (uint64_t)TARGET_PAGE_MASK | a_mask;
- if (facilities & FACILITY_GEN_INST_EXT) {
+ if (s390_facilities & FACILITY_GEN_INST_EXT) {
tcg_out_risbg(s, TCG_REG_R2, addr_reg,
64 - CPU_TLB_BITS - CPU_TLB_ENTRY_BITS,
63 - CPU_TLB_ENTRY_BITS,
tcg_out_insn(s, RI, AHI, a0, a2);
break;
}
- if (facilities & FACILITY_EXT_IMM) {
+ if (s390_facilities & FACILITY_EXT_IMM) {
tcg_out_insn(s, RIL, AFI, a0, a2);
break;
}
tcg_out_insn(s, RI, AGHI, a0, a2);
break;
}
- if (facilities & FACILITY_EXT_IMM) {
+ if (s390_facilities & FACILITY_EXT_IMM) {
if (a2 == (int32_t)a2) {
tcg_out_insn(s, RIL, AGFI, a0, a2);
break;
break;
OP_32_64(deposit):
- tgen_deposit(s, args[0], args[2], args[3], args[4]);
+ a0 = args[0], a1 = args[1], a2 = args[2];
+ if (const_args[1]) {
+ tgen_deposit(s, a0, a2, args[3], args[4], 1);
+ } else {
+ /* Since we can't support "0Z" as a constraint, we allow a1 in
+ any register. Fix things up as if a matching constraint. */
+ if (a0 != a1) {
+ TCGType type = (opc == INDEX_op_deposit_i64);
+ if (a0 == a2) {
+ tcg_out_mov(s, type, TCG_TMP0, a2);
+ a2 = TCG_TMP0;
+ }
+ tcg_out_mov(s, type, a0, a1);
+ }
+ tgen_deposit(s, a0, a2, args[3], args[4], 0);
+ }
+ break;
+
+ OP_32_64(extract):
+ tgen_extract(s, args[0], args[1], args[2], args[3]);
+ break;
+
+ case INDEX_op_mb:
+ /* The host memory model is quite strong, we simply need to
+ serialize the instruction stream. */
+ if (args[0] & TCG_MO_ST_LD) {
+ tcg_out_insn(s, RR, BCR,
+ s390_facilities & FACILITY_FAST_BCR_SER ? 14 : 15, 0);
+ }
break;
case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */
{ INDEX_op_neg_i32, { "r", "r" } },
- { INDEX_op_shl_i32, { "r", "0", "Ri" } },
- { INDEX_op_shr_i32, { "r", "0", "Ri" } },
- { INDEX_op_sar_i32, { "r", "0", "Ri" } },
+ { INDEX_op_shl_i32, { "r", "0", "ri" } },
+ { INDEX_op_shr_i32, { "r", "0", "ri" } },
+ { INDEX_op_sar_i32, { "r", "0", "ri" } },
- { INDEX_op_rotl_i32, { "r", "r", "Ri" } },
- { INDEX_op_rotr_i32, { "r", "r", "Ri" } },
+ { INDEX_op_rotl_i32, { "r", "r", "ri" } },
+ { INDEX_op_rotr_i32, { "r", "r", "ri" } },
{ INDEX_op_ext8s_i32, { "r", "r" } },
{ INDEX_op_ext8u_i32, { "r", "r" } },
{ INDEX_op_brcond_i32, { "r", "rC" } },
{ INDEX_op_setcond_i32, { "r", "r", "rC" } },
{ INDEX_op_movcond_i32, { "r", "r", "rC", "r", "0" } },
- { INDEX_op_deposit_i32, { "r", "0", "r" } },
+ { INDEX_op_deposit_i32, { "r", "rZ", "r" } },
+ { INDEX_op_extract_i32, { "r", "r" } },
{ INDEX_op_qemu_ld_i32, { "r", "L" } },
{ INDEX_op_qemu_ld_i64, { "r", "L" } },
{ INDEX_op_neg_i64, { "r", "r" } },
- { INDEX_op_shl_i64, { "r", "r", "Ri" } },
- { INDEX_op_shr_i64, { "r", "r", "Ri" } },
- { INDEX_op_sar_i64, { "r", "r", "Ri" } },
+ { INDEX_op_shl_i64, { "r", "r", "ri" } },
+ { INDEX_op_shr_i64, { "r", "r", "ri" } },
+ { INDEX_op_sar_i64, { "r", "r", "ri" } },
- { INDEX_op_rotl_i64, { "r", "r", "Ri" } },
- { INDEX_op_rotr_i64, { "r", "r", "Ri" } },
+ { INDEX_op_rotl_i64, { "r", "r", "ri" } },
+ { INDEX_op_rotr_i64, { "r", "r", "ri" } },
{ INDEX_op_ext8s_i64, { "r", "r" } },
{ INDEX_op_ext8u_i64, { "r", "r" } },
{ INDEX_op_setcond_i64, { "r", "r", "rC" } },
{ INDEX_op_movcond_i64, { "r", "r", "rC", "r", "0" } },
{ INDEX_op_deposit_i64, { "r", "0", "r" } },
+ { INDEX_op_extract_i64, { "r", "r" } },
+ { INDEX_op_mb, { } },
{ -1 },
};
-static void query_facilities(void)
+static const TCGTargetOpDef *tcg_target_op_def(TCGOpcode op)
+{
+ int i, n = ARRAY_SIZE(s390_op_defs);
+
+ for (i = 0; i < n; ++i) {
+ if (s390_op_defs[i].op == op) {
+ return &s390_op_defs[i];
+ }
+ }
+ return NULL;
+}
+
+static void query_s390_facilities(void)
{
unsigned long hwcap = qemu_getauxval(AT_HWCAP);
register void *r1 __asm__("1");
/* stfle 0(%r1) */
- r1 = &facilities;
+ r1 = &s390_facilities;
asm volatile(".word 0xb2b0,0x1000"
: "=r"(r0) : "0"(0), "r"(r1) : "memory", "cc");
}
static void tcg_target_init(TCGContext *s)
{
- query_facilities();
+ query_s390_facilities();
tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffff);
tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffff);
/* XXX many insns can't be used with R0, so we better avoid it for now */
tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0);
tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK);
-
- tcg_add_target_add_op_defs(s390_op_defs);
}
#define FRAME_SIZE ((int)(TCG_TARGET_CALL_STACK_OFFSET \