target/riscv: Support raising misaligned exceptions for vector loads/stores
When the Zicclsm extension is not enabled, raise misaligned load/store exceptions for misaligned accesses from vector load/store instructions. We will skip the host fast-path and fall back to the slow TLB-path to raise misaligned load/store exceptions for the misaligned accesses when Zicclsm extension is disabled. Signed-off-by: Frank Chang <frank.chang@sifive.com> Reviewed-by: Max Chou <max.chou@sifive.com> Acked-by: Alistair Francis <alistair.francis@wdc.com> Message-ID: <20260810055618.1175500-6-frank.chang@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Frank Chang committed
Aug 10, 2026 at 13:56 UTC
460549ba1088ee9ac5f69c0f5c42c96ebb4d1355
2 files changed
+65
-18
target/riscv/tcg/insn_trans/trans_rvv.c.inc
+15
-3
@@ -1191,26 +1191,38 @@ static bool ldst_whole_trans(uint32_t vd, uint32_t rs1, uint32_t nf,
1191
* Use the helper function if either:
1192
* - vstart is not 0.
1193
*/
1194
-
1194
bool use_helper_fn = !s->vstart_eq_zero;
1195
1196
if (!use_helper_fn) {
1197
uint32_t size = s->cfg_ptr->vlenb * nf;
1198
TCGv_i64 t8 = tcg_temp_new_i64();
1199
MemOp atomicity = MO_ATOM_NONE;
1200
+ MemOp alignment = MO_UNALN;
1201
+
1202
+ /*
1203
+ * If Zicclsm is disabled, require alignment based on element size.
1204
+ * Use MO_ALIGN_* based on log2_esz (0 = MO_UNALN, 1 = MO_ALIGN_2, etc).
1205
+ */
1206
+ if (!s->cfg_ptr->ext_zicclsm) {
1207
+ alignment = log2_esz << MO_ASHIFT;
1208
+ }
1209
+
1210
if (log2_esz == 0) {
1211
atomicity = MO_ATOM_NONE;
1212
} else {
1213
atomicity = MO_ATOM_IFALIGN_PAIR;
1214
}
1215
+
1216
for (int i = 0; i < size; i += 8) {
1217
TCGv addr = get_address(s, rs1, i);
1218
if (is_load) {
1209
- tcg_gen_qemu_ld_i64(t8, addr, s->mem_idx, MO_LEUQ | atomicity);
1219
+ tcg_gen_qemu_ld_i64(t8, addr, s->mem_idx,
1220
+ MO_LEUQ | atomicity | alignment);
1221
tcg_gen_st_i64(t8, tcg_env, vreg_ofs(s, vd) + i);
1222
} else {
1223
tcg_gen_ld_i64(t8, tcg_env, vreg_ofs(s, vd) + i);
1213
- tcg_gen_qemu_st_i64(t8, addr, s->mem_idx, MO_LEUQ | atomicity);
1224
+ tcg_gen_qemu_st_i64(t8, addr, s->mem_idx,
1225
+ MO_LEUQ | atomicity | alignment);
1226
}
1227
if (i == size - 8) {
1228
tcg_gen_movi_i32(cpu_vstart, 0);
target/riscv/tcg/vector_helper.c
+50
-15
@@ -199,20 +199,34 @@ static inline void vext_set_elem_mask(void *v0, int index,
199
((uint64_t *)v0)[idx] = deposit64(old, pos, 1, value);
200
}
201
202
+static inline MemOpIdx vext_make_memop_idx(CPURISCVState *env, size_t size)
203
+{
204
+ int mmu_idx = riscv_env_mmu_index(env, false);
205
+ MemOp memop = size_memop(size) | mo_endian_env(env);
206
+
207
+ if (!riscv_cpu_cfg(env)->ext_zicclsm) {
208
+ memop |= MO_ALIGN;
209
+ }
210
+
211
+ return make_memop_idx(memop, mmu_idx);
212
+}
213
+
214
/* elements operations for load and store */
215
typedef void vext_ldst_elem_fn_tlb(CPURISCVState *env, abi_ptr addr,
216
uint32_t idx, void *vd, uintptr_t retaddr);
217
typedef void vext_ldst_elem_fn_host(void *vd, uint32_t idx, void *host);
218
207
-#define GEN_VEXT_LD_ELEM(NAME, ETYPE, H, LDSUF) \
219
+#define GEN_VEXT_TLB_LD_ELEM(NAME, ETYPE, H, LDSUF) \
220
static inline QEMU_ALWAYS_INLINE \
221
void NAME##_tlb(CPURISCVState *env, abi_ptr addr, \
222
uint32_t idx, void *vd, uintptr_t retaddr) \
223
{ \
224
ETYPE *cur = ((ETYPE *)vd + H(idx)); \
213
- *cur = cpu_##LDSUF##_data_ra(env, addr, retaddr); \
225
+ MemOpIdx oi = vext_make_memop_idx(env, sizeof(ETYPE)); \
226
+ *cur = cpu_##LDSUF##_mmu(env, addr, oi, retaddr); \
227
} \
215
- \
228
+
229
+#define GEN_VEXT_HOST_LD_ELEM(NAME, ETYPE, H, LDSUF) \
230
static inline QEMU_ALWAYS_INLINE \
231
void NAME##_host(void *vd, uint32_t idx, void *host) \
232
{ \
@@ -220,20 +234,27 @@ void NAME##_host(void *vd, uint32_t idx, void *host) \
234
*cur = (ETYPE)LDSUF##_p(host); \
235
}
236
223
-GEN_VEXT_LD_ELEM(lde_b, uint8_t, H1, ldub)
224
-GEN_VEXT_LD_ELEM(lde_h, uint16_t, H2, lduw_le)
225
-GEN_VEXT_LD_ELEM(lde_w, uint32_t, H4, ldl_le)
226
-GEN_VEXT_LD_ELEM(lde_d, uint64_t, H8, ldq_le)
237
+GEN_VEXT_TLB_LD_ELEM(lde_b, uint8_t, H1, ldb)
238
+GEN_VEXT_TLB_LD_ELEM(lde_h, uint16_t, H2, ldw)
239
+GEN_VEXT_TLB_LD_ELEM(lde_w, uint32_t, H4, ldl)
240
+GEN_VEXT_TLB_LD_ELEM(lde_d, uint64_t, H8, ldq)
241
228
-#define GEN_VEXT_ST_ELEM(NAME, ETYPE, H, STSUF) \
242
+GEN_VEXT_HOST_LD_ELEM(lde_b, uint8_t, H1, ldub)
243
+GEN_VEXT_HOST_LD_ELEM(lde_h, uint16_t, H2, lduw_le)
244
+GEN_VEXT_HOST_LD_ELEM(lde_w, uint32_t, H4, ldl_le)
245
+GEN_VEXT_HOST_LD_ELEM(lde_d, uint64_t, H8, ldq_le)
246
+
247
+#define GEN_VEXT_TLB_ST_ELEM(NAME, ETYPE, H, STSUF) \
248
static inline QEMU_ALWAYS_INLINE \
249
void NAME##_tlb(CPURISCVState *env, abi_ptr addr, \
250
uint32_t idx, void *vd, uintptr_t retaddr) \
251
{ \
252
ETYPE data = *((ETYPE *)vd + H(idx)); \
234
- cpu_##STSUF##_data_ra(env, addr, data, retaddr); \
253
+ MemOpIdx oi = vext_make_memop_idx(env, sizeof(ETYPE)); \
254
+ cpu_##STSUF##_mmu(env, addr, data, oi, retaddr); \
255
} \
236
- \
256
+
257
+#define GEN_VEXT_HOST_ST_ELEM(NAME, ETYPE, H, STSUF) \
258
static inline QEMU_ALWAYS_INLINE \
259
void NAME##_host(void *vd, uint32_t idx, void *host) \
260
{ \
@@ -241,10 +262,15 @@ void NAME##_host(void *vd, uint32_t idx, void *host) \
262
STSUF##_p(host, data); \
263
}
264
244
-GEN_VEXT_ST_ELEM(ste_b, uint8_t, H1, stb)
245
-GEN_VEXT_ST_ELEM(ste_h, uint16_t, H2, stw_le)
246
-GEN_VEXT_ST_ELEM(ste_w, uint32_t, H4, stl_le)
247
-GEN_VEXT_ST_ELEM(ste_d, uint64_t, H8, stq_le)
265
+GEN_VEXT_TLB_ST_ELEM(ste_b, uint8_t, H1, stb)
266
+GEN_VEXT_TLB_ST_ELEM(ste_h, uint16_t, H2, stw)
267
+GEN_VEXT_TLB_ST_ELEM(ste_w, uint32_t, H4, stl)
268
+GEN_VEXT_TLB_ST_ELEM(ste_d, uint64_t, H8, stq)
269
+
270
+GEN_VEXT_HOST_ST_ELEM(ste_b, uint8_t, H1, stb)
271
+GEN_VEXT_HOST_ST_ELEM(ste_h, uint16_t, H2, stw_le)
272
+GEN_VEXT_HOST_ST_ELEM(ste_w, uint32_t, H4, stl_le)
273
+GEN_VEXT_HOST_ST_ELEM(ste_d, uint64_t, H8, stq_le)
274
275
static inline QEMU_ALWAYS_INLINE void
276
vext_continuous_ldst_tlb(CPURISCVState *env, vext_ldst_elem_fn_tlb *ldst_tlb,
@@ -398,7 +424,16 @@ vext_page_ldst_us(CPURISCVState *env, void *vd, target_ulong addr,
424
probe_pages(env, addr, size, ra, access_type, mmu_index, &host, &flags,
425
true);
426
401
- if (flags == 0) {
427
+ bool misaligned = addr & (esz - 1);
428
+
429
+ /*
430
+ * Allow the host fast-pash when:
431
+ * 1. Page permission/pmp/watchpoint are checked and we have a contigous
432
+ * host mapping.
433
+ * 2. Zicclsm is enabled or load/store is not a misaligned access.
434
+ * Otherwise, we will fall back to the slow TLB-path.
435
+ */
436
+ if (flags == 0 && (riscv_cpu_cfg(env)->ext_zicclsm || !misaligned)) {
437
if (nf == 1) {
438
vext_continuous_ldst_host(env, ldst_host, vd, evl, env->vstart,
439
host, esz, is_load);