From 5c4e852c6eb4482a9c65c08d17593257346fb1aa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 18 Dec 2018 05:56:56 -0500 Subject: [PATCH] tcg: Add TCG_TARGET_HAS_MEMORY_BSWAP For now, defined universally as true, since we previously required backends to implement swapped memory operations. Future patches may now remove that support where it is onerous. Backports commit e1dcf3529d0797b25bb49a20e94b62eb93e7276a from qemu --- qemu/tcg/aarch64/tcg-target.h | 1 + qemu/tcg/arm/tcg-target.h | 1 + qemu/tcg/i386/tcg-target.h | 2 + qemu/tcg/mips/tcg-target.h | 1 + qemu/tcg/s390/tcg-target.h | 1 + qemu/tcg/sparc/tcg-target.h | 1 + qemu/tcg/tcg-op.c | 119 +++++++++++++++++++++++++++++++++- 7 files changed, 124 insertions(+), 2 deletions(-) diff --git a/qemu/tcg/aarch64/tcg-target.h b/qemu/tcg/aarch64/tcg-target.h index 9aea1d17..f966a4fc 100644 --- a/qemu/tcg/aarch64/tcg-target.h +++ b/qemu/tcg/aarch64/tcg-target.h @@ -137,6 +137,7 @@ typedef enum { #define TCG_TARGET_HAS_mul_vec 1 #define TCG_TARGET_DEFAULT_MO (0) +#define TCG_TARGET_HAS_MEMORY_BSWAP 1 static inline void flush_icache_range(uintptr_t start, uintptr_t stop) { diff --git a/qemu/tcg/arm/tcg-target.h b/qemu/tcg/arm/tcg-target.h index 0b8566b7..8b3ae1fa 100644 --- a/qemu/tcg/arm/tcg-target.h +++ b/qemu/tcg/arm/tcg-target.h @@ -132,6 +132,7 @@ enum { }; #define TCG_TARGET_DEFAULT_MO (0) +#define TCG_TARGET_HAS_MEMORY_BSWAP 1 static inline void flush_icache_range(uintptr_t start, uintptr_t stop) { diff --git a/qemu/tcg/i386/tcg-target.h b/qemu/tcg/i386/tcg-target.h index 54bc92ad..9d6446ac 100644 --- a/qemu/tcg/i386/tcg-target.h +++ b/qemu/tcg/i386/tcg-target.h @@ -253,6 +253,8 @@ static inline void tb_target_set_jmp_target(uintptr_t tc_ptr, #define TCG_TARGET_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) +#define TCG_TARGET_HAS_MEMORY_BSWAP 1 + #ifdef CONFIG_SOFTMMU #define TCG_TARGET_NEED_LDST_LABELS #endif diff --git a/qemu/tcg/mips/tcg-target.h b/qemu/tcg/mips/tcg-target.h index a8222476..5cb86724 100644 --- a/qemu/tcg/mips/tcg-target.h +++ b/qemu/tcg/mips/tcg-target.h @@ -203,6 +203,7 @@ extern bool use_mips32r2_instructions; #endif #define TCG_TARGET_DEFAULT_MO (0) +#define TCG_TARGET_HAS_MEMORY_BSWAP 1 static inline void flush_icache_range(uintptr_t start, uintptr_t stop) { diff --git a/qemu/tcg/s390/tcg-target.h b/qemu/tcg/s390/tcg-target.h index 6f2b06a7..853ed6e7 100644 --- a/qemu/tcg/s390/tcg-target.h +++ b/qemu/tcg/s390/tcg-target.h @@ -135,6 +135,7 @@ extern uint64_t s390_facilities; #define TCG_TARGET_CALL_STACK_OFFSET 160 #define TCG_TARGET_EXTEND_ARGS 1 +#define TCG_TARGET_HAS_MEMORY_BSWAP 1 #define TCG_TARGET_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) diff --git a/qemu/tcg/sparc/tcg-target.h b/qemu/tcg/sparc/tcg-target.h index 3493269c..4944f647 100644 --- a/qemu/tcg/sparc/tcg-target.h +++ b/qemu/tcg/sparc/tcg-target.h @@ -164,6 +164,7 @@ extern bool use_vis3_instructions; #define TCG_AREG0 TCG_REG_I0 #define TCG_TARGET_DEFAULT_MO (0) +#define TCG_TARGET_HAS_MEMORY_BSWAP 1 #ifdef _MSC_VER #include diff --git a/qemu/tcg/tcg-op.c b/qemu/tcg/tcg-op.c index 801b2876..46b408b5 100644 --- a/qemu/tcg/tcg-op.c +++ b/qemu/tcg/tcg-op.c @@ -2723,29 +2723,82 @@ static void tcg_gen_req_mo(TCGContext *s, TCGBar type) void tcg_gen_qemu_ld_i32(struct uc_struct *uc, TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop) { + TCGMemOp orig_memop; TCGContext *tcg_ctx = uc->tcg_ctx; tcg_gen_req_mo(tcg_ctx, TCG_MO_LD_LD | TCG_MO_ST_LD); memop = tcg_canonicalize_memop(memop, 0, 0); + + orig_memop = memop; + if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { + memop &= ~MO_BSWAP; + /* The bswap primitive requires zero-extended input. */ + if ((memop & MO_SSIZE) == MO_SW) { + memop &= ~MO_SIGN; + } + } + gen_ldst_i32(tcg_ctx, INDEX_op_qemu_ld_i32, val, addr, memop, idx); + + if ((orig_memop ^ memop) & MO_BSWAP) { + switch (orig_memop & MO_SIZE) { + case MO_16: + tcg_gen_bswap16_i32(tcg_ctx, val, val); + if (orig_memop & MO_SIGN) { + tcg_gen_ext16s_i32(tcg_ctx, val, val); + } + break; + case MO_32: + tcg_gen_bswap32_i32(tcg_ctx, val, val); + break; + default: + g_assert_not_reached(); + } + } + check_exit_request(tcg_ctx); } void tcg_gen_qemu_st_i32(struct uc_struct *uc, TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop) { + TCGv_i32 swap = NULL; TCGContext *tcg_ctx = uc->tcg_ctx; tcg_gen_req_mo(tcg_ctx, TCG_MO_LD_ST | TCG_MO_ST_ST); memop = tcg_canonicalize_memop(memop, 0, 1); + + if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { + swap = tcg_temp_new_i32(tcg_ctx); + switch (memop & MO_SIZE) { + case MO_16: + tcg_gen_ext16u_i32(tcg_ctx, swap, val); + tcg_gen_bswap16_i32(tcg_ctx, swap, swap); + break; + case MO_32: + tcg_gen_bswap32_i32(tcg_ctx, swap, val); + break; + default: + g_assert_not_reached(); + } + val = swap; + memop &= ~MO_BSWAP; + } + + gen_ldst_i32(tcg_ctx, INDEX_op_qemu_st_i32, val, addr, memop, idx); + + if (swap) { + tcg_temp_free_i32(tcg_ctx, swap); + } + check_exit_request(tcg_ctx); } void tcg_gen_qemu_ld_i64(struct uc_struct *uc, TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop) { TCGContext *tcg_ctx = uc->tcg_ctx; + TCGMemOp orig_memop; - tcg_gen_req_mo(tcg_ctx, TCG_MO_LD_LD | TCG_MO_ST_LD); if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) { tcg_gen_qemu_ld_i32(uc, TCGV_LOW(tcg_ctx, val), addr, idx, memop); if (memop & MO_SIGN) { @@ -2758,24 +2811,86 @@ void tcg_gen_qemu_ld_i64(struct uc_struct *uc, TCGv_i64 val, TCGv addr, TCGArg i return; } + tcg_gen_req_mo(tcg_ctx, TCG_MO_LD_LD | TCG_MO_ST_LD); memop = tcg_canonicalize_memop(memop, 1, 0); + + orig_memop = memop; + if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { + memop &= ~MO_BSWAP; + /* The bswap primitive requires zero-extended input. */ + if ((memop & MO_SIGN) && (memop & MO_SIZE) < MO_64) { + memop &= ~MO_SIGN; + } + } + gen_ldst_i64(tcg_ctx, INDEX_op_qemu_ld_i64, val, addr, memop, idx); + + if ((orig_memop ^ memop) & MO_BSWAP) { + switch (orig_memop & MO_SIZE) { + case MO_16: + tcg_gen_bswap16_i64(tcg_ctx, val, val); + if (orig_memop & MO_SIGN) { + tcg_gen_ext16s_i64(tcg_ctx, val, val); + } + break; + case MO_32: + tcg_gen_bswap32_i64(tcg_ctx, val, val); + if (orig_memop & MO_SIGN) { + tcg_gen_ext32s_i64(tcg_ctx, val, val); + } + break; + case MO_64: + tcg_gen_bswap64_i64(tcg_ctx, val, val); + break; + default: + g_assert_not_reached(); + } + } + check_exit_request(tcg_ctx); } void tcg_gen_qemu_st_i64(struct uc_struct *uc, TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop) { TCGContext *tcg_ctx = uc->tcg_ctx; + TCGv_i64 swap = NULL; - tcg_gen_req_mo(tcg_ctx, TCG_MO_LD_ST | TCG_MO_ST_ST); if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) { tcg_gen_qemu_st_i32(uc, TCGV_LOW(tcg_ctx, val), addr, idx, memop); check_exit_request(tcg_ctx); return; } + tcg_gen_req_mo(tcg_ctx, TCG_MO_LD_ST | TCG_MO_ST_ST); memop = tcg_canonicalize_memop(memop, 1, 1); + + if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { + swap = tcg_temp_new_i64(tcg_ctx); + switch (memop & MO_SIZE) { + case MO_16: + tcg_gen_ext16u_i64(tcg_ctx, swap, val); + tcg_gen_bswap16_i64(tcg_ctx, swap, swap); + break; + case MO_32: + tcg_gen_ext32u_i64(tcg_ctx, swap, val); + tcg_gen_bswap32_i64(tcg_ctx, swap, swap); + break; + case MO_64: + tcg_gen_bswap64_i64(tcg_ctx, swap, val); + break; + default: + g_assert_not_reached(); + } + val = swap; + memop &= ~MO_BSWAP; + } + gen_ldst_i64(tcg_ctx, INDEX_op_qemu_st_i64, val, addr, memop, idx); + + if (swap) { + tcg_temp_free_i64(tcg_ctx, swap); + } + check_exit_request(tcg_ctx); }