target/sparc: Split out fp ldst functions with asi precomputed

Take the operation size from the MemOp instead of a
separate parameter.

Tested-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Acked-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2023-10-05 01:37:06 -07:00
parent 5458fd3153
commit 3259b9e2fd

View File

@ -2320,35 +2320,41 @@ static void gen_ldstub_asi(DisasContext *dc, DisasASI *da, TCGv dst, TCGv addr)
} }
} }
static void __attribute__((unused)) static void gen_ldf_asi0(DisasContext *dc, DisasASI *da, MemOp orig_size,
gen_ldf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) TCGv addr, int rd)
{ {
DisasASI da = get_asi(dc, insn, (size == 4 ? MO_TEUL : MO_TEUQ)); MemOp memop = da->memop;
MemOp size = memop & MO_SIZE;
TCGv_i32 d32; TCGv_i32 d32;
TCGv_i64 d64; TCGv_i64 d64;
switch (da.type) { /* TODO: Use 128-bit load/store below. */
if (size == MO_128) {
memop = (memop & ~MO_SIZE) | MO_64;
}
switch (da->type) {
case GET_ASI_EXCP: case GET_ASI_EXCP:
break; break;
case GET_ASI_DIRECT: case GET_ASI_DIRECT:
gen_address_mask(dc, addr); memop |= MO_ALIGN_4;
switch (size) { switch (size) {
case 4: case MO_32:
d32 = gen_dest_fpr_F(dc); d32 = gen_dest_fpr_F(dc);
tcg_gen_qemu_ld_i32(d32, addr, da.mem_idx, da.memop | MO_ALIGN); tcg_gen_qemu_ld_i32(d32, addr, da->mem_idx, memop);
gen_store_fpr_F(dc, rd, d32); gen_store_fpr_F(dc, rd, d32);
break; break;
case 8:
tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2], addr, da.mem_idx, case MO_64:
da.memop | MO_ALIGN_4); tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2], addr, da->mem_idx, memop);
break; break;
case 16:
case MO_128:
d64 = tcg_temp_new_i64(); d64 = tcg_temp_new_i64();
tcg_gen_qemu_ld_i64(d64, addr, da.mem_idx, da.memop | MO_ALIGN_4); tcg_gen_qemu_ld_i64(d64, addr, da->mem_idx, memop);
tcg_gen_addi_tl(addr, addr, 8); tcg_gen_addi_tl(addr, addr, 8);
tcg_gen_qemu_ld_i64(cpu_fpr[rd/2+1], addr, da.mem_idx, tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2 + 1], addr, da->mem_idx, memop);
da.memop | MO_ALIGN_4);
tcg_gen_mov_i64(cpu_fpr[rd / 2], d64); tcg_gen_mov_i64(cpu_fpr[rd / 2], d64);
break; break;
default: default:
@ -2358,24 +2364,19 @@ gen_ldf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd)
case GET_ASI_BLOCK: case GET_ASI_BLOCK:
/* Valid for lddfa on aligned registers only. */ /* Valid for lddfa on aligned registers only. */
if (size == 8 && (rd & 7) == 0) { if (orig_size == MO_64 && (rd & 7) == 0) {
MemOp memop;
TCGv eight; TCGv eight;
int i; int i;
gen_address_mask(dc, addr);
/* The first operation checks required alignment. */ /* The first operation checks required alignment. */
memop = da.memop | MO_ALIGN_64;
eight = tcg_constant_tl(8); eight = tcg_constant_tl(8);
for (i = 0; ; ++i) { for (i = 0; ; ++i) {
tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2 + i], addr, tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2 + i], addr, da->mem_idx,
da.mem_idx, memop); memop | (i == 0 ? MO_ALIGN_64 : 0));
if (i == 7) { if (i == 7) {
break; break;
} }
tcg_gen_add_tl(addr, addr, eight); tcg_gen_add_tl(addr, addr, eight);
memop = da.memop;
} }
} else { } else {
gen_exception(dc, TT_ILL_INSN); gen_exception(dc, TT_ILL_INSN);
@ -2384,10 +2385,9 @@ gen_ldf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd)
case GET_ASI_SHORT: case GET_ASI_SHORT:
/* Valid for lddfa only. */ /* Valid for lddfa only. */
if (size == 8) { if (orig_size == MO_64) {
gen_address_mask(dc, addr); tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2], addr, da->mem_idx,
tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2], addr, da.mem_idx, memop | MO_ALIGN);
da.memop | MO_ALIGN);
} else { } else {
gen_exception(dc, TT_ILL_INSN); gen_exception(dc, TT_ILL_INSN);
} }
@ -2395,8 +2395,8 @@ gen_ldf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd)
default: default:
{ {
TCGv_i32 r_asi = tcg_constant_i32(da.asi); TCGv_i32 r_asi = tcg_constant_i32(da->asi);
TCGv_i32 r_mop = tcg_constant_i32(da.memop | MO_ALIGN); TCGv_i32 r_mop = tcg_constant_i32(memop | MO_ALIGN);
save_state(dc); save_state(dc);
/* According to the table in the UA2011 manual, the only /* According to the table in the UA2011 manual, the only
@ -2404,21 +2404,23 @@ gen_ldf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd)
the NO_FAULT asis. We still need a helper for these, the NO_FAULT asis. We still need a helper for these,
but we can just use the integer asi helper for them. */ but we can just use the integer asi helper for them. */
switch (size) { switch (size) {
case 4: case MO_32:
d64 = tcg_temp_new_i64(); d64 = tcg_temp_new_i64();
gen_helper_ld_asi(d64, tcg_env, addr, r_asi, r_mop); gen_helper_ld_asi(d64, tcg_env, addr, r_asi, r_mop);
d32 = gen_dest_fpr_F(dc); d32 = gen_dest_fpr_F(dc);
tcg_gen_extrl_i64_i32(d32, d64); tcg_gen_extrl_i64_i32(d32, d64);
gen_store_fpr_F(dc, rd, d32); gen_store_fpr_F(dc, rd, d32);
break; break;
case 8: case MO_64:
gen_helper_ld_asi(cpu_fpr[rd / 2], tcg_env, addr, r_asi, r_mop); gen_helper_ld_asi(cpu_fpr[rd / 2], tcg_env, addr,
r_asi, r_mop);
break; break;
case 16: case MO_128:
d64 = tcg_temp_new_i64(); d64 = tcg_temp_new_i64();
gen_helper_ld_asi(d64, tcg_env, addr, r_asi, r_mop); gen_helper_ld_asi(d64, tcg_env, addr, r_asi, r_mop);
tcg_gen_addi_tl(addr, addr, 8); tcg_gen_addi_tl(addr, addr, 8);
gen_helper_ld_asi(cpu_fpr[rd/2+1], tcg_env, addr, r_asi, r_mop); gen_helper_ld_asi(cpu_fpr[rd / 2 + 1], tcg_env, addr,
r_asi, r_mop);
tcg_gen_mov_i64(cpu_fpr[rd / 2], d64); tcg_gen_mov_i64(cpu_fpr[rd / 2], d64);
break; break;
default: default:
@ -2430,36 +2432,52 @@ gen_ldf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd)
} }
static void __attribute__((unused)) static void __attribute__((unused))
gen_stf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd) gen_ldf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd)
{ {
DisasASI da = get_asi(dc, insn, (size == 4 ? MO_TEUL : MO_TEUQ)); MemOp sz = ctz32(size);
DisasASI da = get_asi(dc, insn, MO_TE | sz);
gen_address_mask(dc, addr);
gen_ldf_asi0(dc, &da, sz, addr, rd);
}
static void gen_stf_asi0(DisasContext *dc, DisasASI *da, MemOp orig_size,
TCGv addr, int rd)
{
MemOp memop = da->memop;
MemOp size = memop & MO_SIZE;
TCGv_i32 d32; TCGv_i32 d32;
switch (da.type) { /* TODO: Use 128-bit load/store below. */
if (size == MO_128) {
memop = (memop & ~MO_SIZE) | MO_64;
}
switch (da->type) {
case GET_ASI_EXCP: case GET_ASI_EXCP:
break; break;
case GET_ASI_DIRECT: case GET_ASI_DIRECT:
gen_address_mask(dc, addr); memop |= MO_ALIGN_4;
switch (size) { switch (size) {
case 4: case MO_32:
d32 = gen_load_fpr_F(dc, rd); d32 = gen_load_fpr_F(dc, rd);
tcg_gen_qemu_st_i32(d32, addr, da.mem_idx, da.memop | MO_ALIGN); tcg_gen_qemu_st_i32(d32, addr, da->mem_idx, memop | MO_ALIGN);
break; break;
case 8: case MO_64:
tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da.mem_idx, tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da->mem_idx,
da.memop | MO_ALIGN_4); memop | MO_ALIGN_4);
break; break;
case 16: case MO_128:
/* Only 4-byte alignment required. However, it is legal for the /* Only 4-byte alignment required. However, it is legal for the
cpu to signal the alignment fault, and the OS trap handler is cpu to signal the alignment fault, and the OS trap handler is
required to fix it up. Requiring 16-byte alignment here avoids required to fix it up. Requiring 16-byte alignment here avoids
having to probe the second page before performing the first having to probe the second page before performing the first
write. */ write. */
tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da.mem_idx, tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da->mem_idx,
da.memop | MO_ALIGN_16); memop | MO_ALIGN_16);
tcg_gen_addi_tl(addr, addr, 8); tcg_gen_addi_tl(addr, addr, 8);
tcg_gen_qemu_st_i64(cpu_fpr[rd/2+1], addr, da.mem_idx, da.memop); tcg_gen_qemu_st_i64(cpu_fpr[rd / 2 + 1], addr, da->mem_idx, memop);
break; break;
default: default:
g_assert_not_reached(); g_assert_not_reached();
@ -2468,24 +2486,19 @@ gen_stf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd)
case GET_ASI_BLOCK: case GET_ASI_BLOCK:
/* Valid for stdfa on aligned registers only. */ /* Valid for stdfa on aligned registers only. */
if (size == 8 && (rd & 7) == 0) { if (orig_size == MO_64 && (rd & 7) == 0) {
MemOp memop;
TCGv eight; TCGv eight;
int i; int i;
gen_address_mask(dc, addr);
/* The first operation checks required alignment. */ /* The first operation checks required alignment. */
memop = da.memop | MO_ALIGN_64;
eight = tcg_constant_tl(8); eight = tcg_constant_tl(8);
for (i = 0; ; ++i) { for (i = 0; ; ++i) {
tcg_gen_qemu_st_i64(cpu_fpr[rd / 2 + i], addr, tcg_gen_qemu_st_i64(cpu_fpr[rd / 2 + i], addr, da->mem_idx,
da.mem_idx, memop); memop | (i == 0 ? MO_ALIGN_64 : 0));
if (i == 7) { if (i == 7) {
break; break;
} }
tcg_gen_add_tl(addr, addr, eight); tcg_gen_add_tl(addr, addr, eight);
memop = da.memop;
} }
} else { } else {
gen_exception(dc, TT_ILL_INSN); gen_exception(dc, TT_ILL_INSN);
@ -2494,10 +2507,9 @@ gen_stf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd)
case GET_ASI_SHORT: case GET_ASI_SHORT:
/* Valid for stdfa only. */ /* Valid for stdfa only. */
if (size == 8) { if (orig_size == MO_64) {
gen_address_mask(dc, addr); tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da->mem_idx,
tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da.mem_idx, memop | MO_ALIGN);
da.memop | MO_ALIGN);
} else { } else {
gen_exception(dc, TT_ILL_INSN); gen_exception(dc, TT_ILL_INSN);
} }
@ -2512,6 +2524,16 @@ gen_stf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd)
} }
} }
static void __attribute__((unused))
gen_stf_asi(DisasContext *dc, TCGv addr, int insn, int size, int rd)
{
MemOp sz = ctz32(size);
DisasASI da = get_asi(dc, insn, MO_TE | sz);
gen_address_mask(dc, addr);
gen_stf_asi0(dc, &da, sz, addr, rd);
}
static void gen_ldda_asi(DisasContext *dc, DisasASI *da, TCGv addr, int rd) static void gen_ldda_asi(DisasContext *dc, DisasASI *da, TCGv addr, int rd)
{ {
TCGv hi = gen_dest_gpr(dc, rd); TCGv hi = gen_dest_gpr(dc, rd);