From 52682366adc75410bd23f97483bb65850856b8d1 Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Wed, 1 Oct 2025 17:37:03 -0700 Subject: [PATCH 1/4] [RISCV][GISel] Support Zalasr. We need additional patterns for GISel because we make s16 and s32 legal for load/store. GISel does not distinquish integer and FP scalar types in LLT. We only know whether the load should be integer or FP after register bank selection. --- llvm/lib/Target/RISCV/RISCVGISel.td | 26 +++ llvm/lib/Target/RISCV/RISCVInstrInfoZalasr.td | 9 +- .../RISCV/GlobalISel/atomic-load-store.ll | 219 ++++++++++++++++++ 3 files changed, 249 insertions(+), 5 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVGISel.td b/llvm/lib/Target/RISCV/RISCVGISel.td index cf6f83a09610d..97cf2b4956fb5 100644 --- a/llvm/lib/Target/RISCV/RISCVGISel.td +++ b/llvm/lib/Target/RISCV/RISCVGISel.td @@ -202,3 +202,29 @@ let Predicates = [HasStdExtZbkb, NoStdExtZbb, IsRV64] in { def : Pat<(i64 (zext (i16 GPR:$rs))), (PACKW GPR:$rs, (XLenVT X0))>; def : Pat<(i32 (zext (i16 GPR:$rs))), (PACKW GPR:$rs, (XLenVT X0))>; } + +//===----------------------------------------------------------------------===// +// Zalasr patterns not used by SelectionDAG +//===----------------------------------------------------------------------===// + +let Predicates = [HasStdExtZalasr] in { + // the sequentially consistent loads use + // .aq instead of .aqrl to match the psABI/A.7 + def : PatLAQ, LB_AQ, i16>; + def : PatLAQ, LB_AQ, i16>; + + def : PatLAQ, LH_AQ, i16>; + def : PatLAQ, LH_AQ, i16>; + + def : PatSRL, SB_RL, i16>; + def : PatSRL, SB_RL, i16>; + + def : PatSRL, SH_RL, i16>; + def : PatSRL, SH_RL, i16>; +} + +let Predicates = [HasStdExtZalasr, IsRV64] in { + // Load pattern is in RISCVInstrInfoZalasr.td and shared with RV32. + def : PatSRL, SW_RL, i32>; + def : PatSRL, SW_RL, i32>; +} diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoZalasr.td b/llvm/lib/Target/RISCV/RISCVInstrInfoZalasr.td index 1674c957b6579..aa37679f68ddf 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfoZalasr.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoZalasr.td @@ -93,12 +93,11 @@ let Predicates = [HasStdExtZalasr] in { def : PatSRL, SW_RL>; def : PatSRL, SW_RL>; -} // Predicates = [HasStdExtZalasr] -let Predicates = [HasStdExtZalasr, IsRV32] in { - def : PatLAQ, LW_AQ>; - def : PatLAQ, LW_AQ>; -} // Predicates = [HasStdExtZalasr, IsRV32] + // Used by GISel for RV32 and RV64. + def : PatLAQ, LW_AQ, i32>; + def : PatLAQ, LW_AQ, i32>; +} // Predicates = [HasStdExtZalasr] let Predicates = [HasStdExtZalasr, IsRV64] in { def : PatLAQ, LW_AQ, i64>; diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/atomic-load-store.ll b/llvm/test/CodeGen/RISCV/GlobalISel/atomic-load-store.ll index 1d5d918422b28..5d3fed48bf82b 100644 --- a/llvm/test/CodeGen/RISCV/GlobalISel/atomic-load-store.ll +++ b/llvm/test/CodeGen/RISCV/GlobalISel/atomic-load-store.ll @@ -23,6 +23,15 @@ ; RUN: llc -mtriple=riscv64 -global-isel -mattr=+a,+ztso -verify-machineinstrs < %s \ ; RUN: | FileCheck -check-prefixes=RV64IA,RV64IA-TSO-TRAILING-FENCE %s +; RUN: llc -mtriple=riscv32 -global-isel -mattr=+a,+experimental-zalasr -verify-machineinstrs < %s \ +; RUN: | FileCheck -check-prefixes=RV32IA,RV32IA-ZALASR,RV32IA-ZALASR-WMO %s +; RUN: llc -mtriple=riscv32 -global-isel -mattr=+a,+experimental-zalasr,+ztso -verify-machineinstrs < %s \ +; RUN: | FileCheck -check-prefixes=RV32IA,RV32IA-ZALASR,RV32IA-ZALASR-TSO %s + +; RUN: llc -mtriple=riscv64 -global-isel -mattr=+a,+experimental-zalasr -verify-machineinstrs < %s \ +; RUN: | FileCheck -check-prefixes=RV64IA,RV64IA-ZALASR,RV64IA-ZALASR-WMO %s +; RUN: llc -mtriple=riscv64 -global-isel -mattr=+a,+experimental-zalasr,+ztso -verify-machineinstrs < %s \ +; RUN: | FileCheck -check-prefixes=RV64IA,RV64IA-ZALASR,RV64IA-ZALASR-TSO %s define i8 @atomic_load_i8_unordered(ptr %a) nounwind { ; RV32I-LABEL: atomic_load_i8_unordered: @@ -156,6 +165,26 @@ define i8 @atomic_load_i8_acquire(ptr %a) nounwind { ; RV64IA-TSO-TRAILING-FENCE: # %bb.0: ; RV64IA-TSO-TRAILING-FENCE-NEXT: lbu a0, 0(a0) ; RV64IA-TSO-TRAILING-FENCE-NEXT: ret +; +; RV32IA-ZALASR-WMO-LABEL: atomic_load_i8_acquire: +; RV32IA-ZALASR-WMO: # %bb.0: +; RV32IA-ZALASR-WMO-NEXT: lb.aq a0, (a0) +; RV32IA-ZALASR-WMO-NEXT: ret +; +; RV32IA-ZALASR-TSO-LABEL: atomic_load_i8_acquire: +; RV32IA-ZALASR-TSO: # %bb.0: +; RV32IA-ZALASR-TSO-NEXT: lbu a0, 0(a0) +; RV32IA-ZALASR-TSO-NEXT: ret +; +; RV64IA-ZALASR-WMO-LABEL: atomic_load_i8_acquire: +; RV64IA-ZALASR-WMO: # %bb.0: +; RV64IA-ZALASR-WMO-NEXT: lb.aq a0, (a0) +; RV64IA-ZALASR-WMO-NEXT: ret +; +; RV64IA-ZALASR-TSO-LABEL: atomic_load_i8_acquire: +; RV64IA-ZALASR-TSO: # %bb.0: +; RV64IA-ZALASR-TSO-NEXT: lbu a0, 0(a0) +; RV64IA-ZALASR-TSO-NEXT: ret %1 = load atomic i8, ptr %a acquire, align 1 ret i8 %1 } @@ -232,6 +261,16 @@ define i8 @atomic_load_i8_seq_cst(ptr %a) nounwind { ; RV64IA-TSO-TRAILING-FENCE-NEXT: fence rw, rw ; RV64IA-TSO-TRAILING-FENCE-NEXT: lbu a0, 0(a0) ; RV64IA-TSO-TRAILING-FENCE-NEXT: ret +; +; RV32IA-ZALASR-LABEL: atomic_load_i8_seq_cst: +; RV32IA-ZALASR: # %bb.0: +; RV32IA-ZALASR-NEXT: lb.aq a0, (a0) +; RV32IA-ZALASR-NEXT: ret +; +; RV64IA-ZALASR-LABEL: atomic_load_i8_seq_cst: +; RV64IA-ZALASR: # %bb.0: +; RV64IA-ZALASR-NEXT: lb.aq a0, (a0) +; RV64IA-ZALASR-NEXT: ret %1 = load atomic i8, ptr %a seq_cst, align 1 ret i8 %1 } @@ -368,6 +407,26 @@ define i16 @atomic_load_i16_acquire(ptr %a) nounwind { ; RV64IA-TSO-TRAILING-FENCE: # %bb.0: ; RV64IA-TSO-TRAILING-FENCE-NEXT: lh a0, 0(a0) ; RV64IA-TSO-TRAILING-FENCE-NEXT: ret +; +; RV32IA-ZALASR-WMO-LABEL: atomic_load_i16_acquire: +; RV32IA-ZALASR-WMO: # %bb.0: +; RV32IA-ZALASR-WMO-NEXT: lh.aq a0, (a0) +; RV32IA-ZALASR-WMO-NEXT: ret +; +; RV32IA-ZALASR-TSO-LABEL: atomic_load_i16_acquire: +; RV32IA-ZALASR-TSO: # %bb.0: +; RV32IA-ZALASR-TSO-NEXT: lh a0, 0(a0) +; RV32IA-ZALASR-TSO-NEXT: ret +; +; RV64IA-ZALASR-WMO-LABEL: atomic_load_i16_acquire: +; RV64IA-ZALASR-WMO: # %bb.0: +; RV64IA-ZALASR-WMO-NEXT: lh.aq a0, (a0) +; RV64IA-ZALASR-WMO-NEXT: ret +; +; RV64IA-ZALASR-TSO-LABEL: atomic_load_i16_acquire: +; RV64IA-ZALASR-TSO: # %bb.0: +; RV64IA-ZALASR-TSO-NEXT: lh a0, 0(a0) +; RV64IA-ZALASR-TSO-NEXT: ret %1 = load atomic i16, ptr %a acquire, align 2 ret i16 %1 } @@ -444,6 +503,16 @@ define i16 @atomic_load_i16_seq_cst(ptr %a) nounwind { ; RV64IA-TSO-TRAILING-FENCE-NEXT: fence rw, rw ; RV64IA-TSO-TRAILING-FENCE-NEXT: lh a0, 0(a0) ; RV64IA-TSO-TRAILING-FENCE-NEXT: ret +; +; RV32IA-ZALASR-LABEL: atomic_load_i16_seq_cst: +; RV32IA-ZALASR: # %bb.0: +; RV32IA-ZALASR-NEXT: lh.aq a0, (a0) +; RV32IA-ZALASR-NEXT: ret +; +; RV64IA-ZALASR-LABEL: atomic_load_i16_seq_cst: +; RV64IA-ZALASR: # %bb.0: +; RV64IA-ZALASR-NEXT: lh.aq a0, (a0) +; RV64IA-ZALASR-NEXT: ret %1 = load atomic i16, ptr %a seq_cst, align 2 ret i16 %1 } @@ -580,6 +649,26 @@ define i32 @atomic_load_i32_acquire(ptr %a) nounwind { ; RV64IA-TSO-TRAILING-FENCE: # %bb.0: ; RV64IA-TSO-TRAILING-FENCE-NEXT: lw a0, 0(a0) ; RV64IA-TSO-TRAILING-FENCE-NEXT: ret +; +; RV32IA-ZALASR-WMO-LABEL: atomic_load_i32_acquire: +; RV32IA-ZALASR-WMO: # %bb.0: +; RV32IA-ZALASR-WMO-NEXT: lw.aq a0, (a0) +; RV32IA-ZALASR-WMO-NEXT: ret +; +; RV32IA-ZALASR-TSO-LABEL: atomic_load_i32_acquire: +; RV32IA-ZALASR-TSO: # %bb.0: +; RV32IA-ZALASR-TSO-NEXT: lw a0, 0(a0) +; RV32IA-ZALASR-TSO-NEXT: ret +; +; RV64IA-ZALASR-WMO-LABEL: atomic_load_i32_acquire: +; RV64IA-ZALASR-WMO: # %bb.0: +; RV64IA-ZALASR-WMO-NEXT: lw.aq a0, (a0) +; RV64IA-ZALASR-WMO-NEXT: ret +; +; RV64IA-ZALASR-TSO-LABEL: atomic_load_i32_acquire: +; RV64IA-ZALASR-TSO: # %bb.0: +; RV64IA-ZALASR-TSO-NEXT: lw a0, 0(a0) +; RV64IA-ZALASR-TSO-NEXT: ret %1 = load atomic i32, ptr %a acquire, align 4 ret i32 %1 } @@ -656,6 +745,16 @@ define i32 @atomic_load_i32_seq_cst(ptr %a) nounwind { ; RV64IA-TSO-TRAILING-FENCE-NEXT: fence rw, rw ; RV64IA-TSO-TRAILING-FENCE-NEXT: lw a0, 0(a0) ; RV64IA-TSO-TRAILING-FENCE-NEXT: ret +; +; RV32IA-ZALASR-LABEL: atomic_load_i32_seq_cst: +; RV32IA-ZALASR: # %bb.0: +; RV32IA-ZALASR-NEXT: lw.aq a0, (a0) +; RV32IA-ZALASR-NEXT: ret +; +; RV64IA-ZALASR-LABEL: atomic_load_i32_seq_cst: +; RV64IA-ZALASR: # %bb.0: +; RV64IA-ZALASR-NEXT: lw.aq a0, (a0) +; RV64IA-ZALASR-NEXT: ret %1 = load atomic i32, ptr %a seq_cst, align 4 ret i32 %1 } @@ -790,6 +889,16 @@ define i64 @atomic_load_i64_acquire(ptr %a) nounwind { ; RV64IA-TSO-TRAILING-FENCE: # %bb.0: ; RV64IA-TSO-TRAILING-FENCE-NEXT: ld a0, 0(a0) ; RV64IA-TSO-TRAILING-FENCE-NEXT: ret +; +; RV64IA-ZALASR-WMO-LABEL: atomic_load_i64_acquire: +; RV64IA-ZALASR-WMO: # %bb.0: +; RV64IA-ZALASR-WMO-NEXT: ld.aq a0, (a0) +; RV64IA-ZALASR-WMO-NEXT: ret +; +; RV64IA-ZALASR-TSO-LABEL: atomic_load_i64_acquire: +; RV64IA-ZALASR-TSO: # %bb.0: +; RV64IA-ZALASR-TSO-NEXT: ld a0, 0(a0) +; RV64IA-ZALASR-TSO-NEXT: ret %1 = load atomic i64, ptr %a acquire, align 8 ret i64 %1 } @@ -850,6 +959,11 @@ define i64 @atomic_load_i64_seq_cst(ptr %a) nounwind { ; RV64IA-TSO-TRAILING-FENCE-NEXT: fence rw, rw ; RV64IA-TSO-TRAILING-FENCE-NEXT: ld a0, 0(a0) ; RV64IA-TSO-TRAILING-FENCE-NEXT: ret +; +; RV64IA-ZALASR-LABEL: atomic_load_i64_seq_cst: +; RV64IA-ZALASR: # %bb.0: +; RV64IA-ZALASR-NEXT: ld.aq a0, (a0) +; RV64IA-ZALASR-NEXT: ret %1 = load atomic i64, ptr %a seq_cst, align 8 ret i64 %1 } @@ -986,6 +1100,26 @@ define void @atomic_store_i8_release(ptr %a, i8 %b) nounwind { ; RV64IA-TSO-TRAILING-FENCE: # %bb.0: ; RV64IA-TSO-TRAILING-FENCE-NEXT: sb a1, 0(a0) ; RV64IA-TSO-TRAILING-FENCE-NEXT: ret +; +; RV32IA-ZALASR-WMO-LABEL: atomic_store_i8_release: +; RV32IA-ZALASR-WMO: # %bb.0: +; RV32IA-ZALASR-WMO-NEXT: sb.rl a1, (a0) +; RV32IA-ZALASR-WMO-NEXT: ret +; +; RV32IA-ZALASR-TSO-LABEL: atomic_store_i8_release: +; RV32IA-ZALASR-TSO: # %bb.0: +; RV32IA-ZALASR-TSO-NEXT: sb a1, 0(a0) +; RV32IA-ZALASR-TSO-NEXT: ret +; +; RV64IA-ZALASR-WMO-LABEL: atomic_store_i8_release: +; RV64IA-ZALASR-WMO: # %bb.0: +; RV64IA-ZALASR-WMO-NEXT: sb.rl a1, (a0) +; RV64IA-ZALASR-WMO-NEXT: ret +; +; RV64IA-ZALASR-TSO-LABEL: atomic_store_i8_release: +; RV64IA-ZALASR-TSO: # %bb.0: +; RV64IA-ZALASR-TSO-NEXT: sb a1, 0(a0) +; RV64IA-ZALASR-TSO-NEXT: ret store atomic i8 %b, ptr %a release, align 1 ret void } @@ -1060,6 +1194,16 @@ define void @atomic_store_i8_seq_cst(ptr %a, i8 %b) nounwind { ; RV64IA-TSO-TRAILING-FENCE-NEXT: sb a1, 0(a0) ; RV64IA-TSO-TRAILING-FENCE-NEXT: fence rw, rw ; RV64IA-TSO-TRAILING-FENCE-NEXT: ret +; +; RV32IA-ZALASR-LABEL: atomic_store_i8_seq_cst: +; RV32IA-ZALASR: # %bb.0: +; RV32IA-ZALASR-NEXT: sb.rl a1, (a0) +; RV32IA-ZALASR-NEXT: ret +; +; RV64IA-ZALASR-LABEL: atomic_store_i8_seq_cst: +; RV64IA-ZALASR: # %bb.0: +; RV64IA-ZALASR-NEXT: sb.rl a1, (a0) +; RV64IA-ZALASR-NEXT: ret store atomic i8 %b, ptr %a seq_cst, align 1 ret void } @@ -1196,6 +1340,26 @@ define void @atomic_store_i16_release(ptr %a, i16 %b) nounwind { ; RV64IA-TSO-TRAILING-FENCE: # %bb.0: ; RV64IA-TSO-TRAILING-FENCE-NEXT: sh a1, 0(a0) ; RV64IA-TSO-TRAILING-FENCE-NEXT: ret +; +; RV32IA-ZALASR-WMO-LABEL: atomic_store_i16_release: +; RV32IA-ZALASR-WMO: # %bb.0: +; RV32IA-ZALASR-WMO-NEXT: sh.rl a1, (a0) +; RV32IA-ZALASR-WMO-NEXT: ret +; +; RV32IA-ZALASR-TSO-LABEL: atomic_store_i16_release: +; RV32IA-ZALASR-TSO: # %bb.0: +; RV32IA-ZALASR-TSO-NEXT: sh a1, 0(a0) +; RV32IA-ZALASR-TSO-NEXT: ret +; +; RV64IA-ZALASR-WMO-LABEL: atomic_store_i16_release: +; RV64IA-ZALASR-WMO: # %bb.0: +; RV64IA-ZALASR-WMO-NEXT: sh.rl a1, (a0) +; RV64IA-ZALASR-WMO-NEXT: ret +; +; RV64IA-ZALASR-TSO-LABEL: atomic_store_i16_release: +; RV64IA-ZALASR-TSO: # %bb.0: +; RV64IA-ZALASR-TSO-NEXT: sh a1, 0(a0) +; RV64IA-ZALASR-TSO-NEXT: ret store atomic i16 %b, ptr %a release, align 2 ret void } @@ -1270,6 +1434,16 @@ define void @atomic_store_i16_seq_cst(ptr %a, i16 %b) nounwind { ; RV64IA-TSO-TRAILING-FENCE-NEXT: sh a1, 0(a0) ; RV64IA-TSO-TRAILING-FENCE-NEXT: fence rw, rw ; RV64IA-TSO-TRAILING-FENCE-NEXT: ret +; +; RV32IA-ZALASR-LABEL: atomic_store_i16_seq_cst: +; RV32IA-ZALASR: # %bb.0: +; RV32IA-ZALASR-NEXT: sh.rl a1, (a0) +; RV32IA-ZALASR-NEXT: ret +; +; RV64IA-ZALASR-LABEL: atomic_store_i16_seq_cst: +; RV64IA-ZALASR: # %bb.0: +; RV64IA-ZALASR-NEXT: sh.rl a1, (a0) +; RV64IA-ZALASR-NEXT: ret store atomic i16 %b, ptr %a seq_cst, align 2 ret void } @@ -1406,6 +1580,26 @@ define void @atomic_store_i32_release(ptr %a, i32 %b) nounwind { ; RV64IA-TSO-TRAILING-FENCE: # %bb.0: ; RV64IA-TSO-TRAILING-FENCE-NEXT: sw a1, 0(a0) ; RV64IA-TSO-TRAILING-FENCE-NEXT: ret +; +; RV32IA-ZALASR-WMO-LABEL: atomic_store_i32_release: +; RV32IA-ZALASR-WMO: # %bb.0: +; RV32IA-ZALASR-WMO-NEXT: sw.rl a1, (a0) +; RV32IA-ZALASR-WMO-NEXT: ret +; +; RV32IA-ZALASR-TSO-LABEL: atomic_store_i32_release: +; RV32IA-ZALASR-TSO: # %bb.0: +; RV32IA-ZALASR-TSO-NEXT: sw a1, 0(a0) +; RV32IA-ZALASR-TSO-NEXT: ret +; +; RV64IA-ZALASR-WMO-LABEL: atomic_store_i32_release: +; RV64IA-ZALASR-WMO: # %bb.0: +; RV64IA-ZALASR-WMO-NEXT: sw.rl a1, (a0) +; RV64IA-ZALASR-WMO-NEXT: ret +; +; RV64IA-ZALASR-TSO-LABEL: atomic_store_i32_release: +; RV64IA-ZALASR-TSO: # %bb.0: +; RV64IA-ZALASR-TSO-NEXT: sw a1, 0(a0) +; RV64IA-ZALASR-TSO-NEXT: ret store atomic i32 %b, ptr %a release, align 4 ret void } @@ -1480,6 +1674,16 @@ define void @atomic_store_i32_seq_cst(ptr %a, i32 %b) nounwind { ; RV64IA-TSO-TRAILING-FENCE-NEXT: sw a1, 0(a0) ; RV64IA-TSO-TRAILING-FENCE-NEXT: fence rw, rw ; RV64IA-TSO-TRAILING-FENCE-NEXT: ret +; +; RV32IA-ZALASR-LABEL: atomic_store_i32_seq_cst: +; RV32IA-ZALASR: # %bb.0: +; RV32IA-ZALASR-NEXT: sw.rl a1, (a0) +; RV32IA-ZALASR-NEXT: ret +; +; RV64IA-ZALASR-LABEL: atomic_store_i32_seq_cst: +; RV64IA-ZALASR: # %bb.0: +; RV64IA-ZALASR-NEXT: sw.rl a1, (a0) +; RV64IA-ZALASR-NEXT: ret store atomic i32 %b, ptr %a seq_cst, align 4 ret void } @@ -1614,6 +1818,16 @@ define void @atomic_store_i64_release(ptr %a, i64 %b) nounwind { ; RV64IA-TSO-TRAILING-FENCE: # %bb.0: ; RV64IA-TSO-TRAILING-FENCE-NEXT: sd a1, 0(a0) ; RV64IA-TSO-TRAILING-FENCE-NEXT: ret +; +; RV64IA-ZALASR-WMO-LABEL: atomic_store_i64_release: +; RV64IA-ZALASR-WMO: # %bb.0: +; RV64IA-ZALASR-WMO-NEXT: sd.rl a1, (a0) +; RV64IA-ZALASR-WMO-NEXT: ret +; +; RV64IA-ZALASR-TSO-LABEL: atomic_store_i64_release: +; RV64IA-ZALASR-TSO: # %bb.0: +; RV64IA-ZALASR-TSO-NEXT: sd a1, 0(a0) +; RV64IA-ZALASR-TSO-NEXT: ret store atomic i64 %b, ptr %a release, align 8 ret void } @@ -1673,6 +1887,11 @@ define void @atomic_store_i64_seq_cst(ptr %a, i64 %b) nounwind { ; RV64IA-TSO-TRAILING-FENCE-NEXT: sd a1, 0(a0) ; RV64IA-TSO-TRAILING-FENCE-NEXT: fence rw, rw ; RV64IA-TSO-TRAILING-FENCE-NEXT: ret +; +; RV64IA-ZALASR-LABEL: atomic_store_i64_seq_cst: +; RV64IA-ZALASR: # %bb.0: +; RV64IA-ZALASR-NEXT: sd.rl a1, (a0) +; RV64IA-ZALASR-NEXT: ret store atomic i64 %b, ptr %a seq_cst, align 8 ret void } From 09a4b765b60249730d750fcc13044a1943e67188 Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Wed, 1 Oct 2025 12:43:03 -0700 Subject: [PATCH 2/4] [RISCV][GISel] Add manual isel for s8/s16/s32 load/store for the GPR bank. GISel doesn't distinquish integer and FP loads and stores. We only know which it is after register bank selection. This results in s16/s32 loads/stores on the GPR register bank that need to be selected. This required extra isel patterns not needed for SDAG and adding i16 and i32 to the GPR register class. Having i16/i32 on the GPR register class makes type interfence in tablegen less effective, requiring explicit casts to be added to patterns. This patch removes the extra isel patterns and replaces it with custom instruction selection similar to what is done on AArch64. A future patch will remove i16 and i32 from the GPR register class. Stacked on #161774. --- .../RISCV/GISel/RISCVInstructionSelector.cpp | 83 +++++++++++++++++++ llvm/lib/Target/RISCV/RISCVGISel.td | 54 ------------ llvm/lib/Target/RISCV/RISCVInstrInfo.td | 4 +- llvm/lib/Target/RISCV/RISCVInstrInfoA.td | 3 +- llvm/lib/Target/RISCV/RISCVInstrInfoZalasr.td | 5 +- 5 files changed, 90 insertions(+), 59 deletions(-) diff --git a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp index 71c21e43998d4..89e7234d78781 100644 --- a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp +++ b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp @@ -675,6 +675,26 @@ static void getOperandsForBranch(Register CondReg, RISCVCC::CondCode &CC, CC = getRISCVCCFromICmp(Pred); } +/// Select the RISC-V opcode for the G_LOAD or G_STORE operation \p GenericOpc, +/// appropriate for the GPR register bank and of memory access size \p OpSize. +/// \returns \p GenericOpc if the combination is unsupported. +static unsigned selectLoadStoreOp(unsigned GenericOpc, unsigned OpSizeInBytes) { + const bool IsStore = GenericOpc == TargetOpcode::G_STORE; + switch (OpSizeInBytes) { + case 1: + // Prefer unsigned due to no c.lb in Zcb. + return IsStore ? RISCV::SB : RISCV::LBU; + case 2: + return IsStore ? RISCV::SH : RISCV::LH; + case 4: + return IsStore ? RISCV::SW : RISCV::LW; + case 8: + return IsStore ? RISCV::SD : RISCV::LD; + } + + return GenericOpc; +} + bool RISCVInstructionSelector::select(MachineInstr &MI) { MachineIRBuilder MIB(MI); @@ -836,6 +856,69 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) { return selectImplicitDef(MI, MIB); case TargetOpcode::G_UNMERGE_VALUES: return selectUnmergeValues(MI, MIB); + case TargetOpcode::G_LOAD: + case TargetOpcode::G_STORE: { + GLoadStore &LdSt = cast(MI); + const Register ValReg = LdSt.getReg(0); + const Register PtrReg = LdSt.getPointerReg(); + LLT PtrTy = MRI->getType(PtrReg); + + const RegisterBank &RB = *RBI.getRegBank(ValReg, *MRI, TRI); + if (RB.getID() != RISCV::GPRBRegBankID) + return false; + +#ifndef NDEBUG + const RegisterBank &PtrRB = + *RBI.getRegBank(PtrReg, *MRI, TRI); + // Check that the pointer register is valid. + assert(PtrRB.getID() == RISCV::GPRBRegBankID && + "Load/Store pointer operand isn't a GPR"); + assert(PtrTy.isPointer() && + "Load/Store pointer operand isn't a pointer"); +#endif + + // Can only handle AddressSpace 0. + if (PtrTy.getAddressSpace() != 0) + return false; + + unsigned MemSizeInBytes = LdSt.getMemSize().getValue(); + AtomicOrdering Order = LdSt.getMMO().getSuccessOrdering(); + + if (isStrongerThanMonotonic(Order)) { + assert(MemSizeInBytes <= 8 && "Unexpected mem size!"); + static constexpr unsigned LoadOpcodes[] = { + RISCV::LB_AQ, RISCV::LH_AQ, RISCV::LW_AQ, RISCV::LD_AQ + }; + static constexpr unsigned StoreOpcodes[] = { + RISCV::SB_RL, RISCV::SH_RL, RISCV::SW_RL, RISCV::SD_RL + }; + ArrayRef Opcodes = isa(LdSt) ? LoadOpcodes : StoreOpcodes; + MI.setDesc(TII.get(Opcodes[Log2_32(MemSizeInBytes)])); + return constrainSelectedInstRegOperands(MI, TII, TRI, RBI); + } + + const unsigned NewOpc = selectLoadStoreOp(MI.getOpcode(), MemSizeInBytes); + if (NewOpc == MI.getOpcode()) + return false; + + // Check if we can fold anything into the addressing mode. + auto AddrModeFns = selectAddrRegImm(MI.getOperand(1)); + if (!AddrModeFns) + return false; + + // Folded something. Create a new instruction and return it. + auto NewInst = MIB.buildInstr(NewOpc, {}, {}, MI.getFlags()); + if (isa(MI)) + NewInst.addUse(ValReg); + else + NewInst.addDef(ValReg); + NewInst.cloneMemRefs(MI); + for (auto &Fn : *AddrModeFns) + Fn(NewInst); + MI.eraseFromParent(); + + return constrainSelectedInstRegOperands(*NewInst, TII, TRI, RBI); + } default: return false; } diff --git a/llvm/lib/Target/RISCV/RISCVGISel.td b/llvm/lib/Target/RISCV/RISCVGISel.td index 6d012500655f0..c9cb423a5336a 100644 --- a/llvm/lib/Target/RISCV/RISCVGISel.td +++ b/llvm/lib/Target/RISCV/RISCVGISel.td @@ -100,39 +100,11 @@ def : LdPat; def : StPat; } -// Load and store patterns for i16, needed because Zfh makes s16 load/store -// legal and regbank select may not constrain registers to FP. -def : LdPat; -def : StPat; - -def : LdPat; // Prefer unsigned due to no c.lb in Zcb. -def : StPat; - -let Predicates = [HasAtomicLdSt] in { - // Prefer unsigned due to no c.lb in Zcb. - def : LdPat, LBU, i16>; - def : LdPat, LH, i16>; - - def : StPat, SB, GPR, i16>; - def : StPat, SH, GPR, i16>; -} - -let Predicates = [HasAtomicLdSt, IsRV64] in { - // Load pattern is in RISCVInstrInfoA.td and shared with RV32. - def : StPat, SW, GPR, i32>; -} - //===----------------------------------------------------------------------===// // RV64 i32 patterns not used by SelectionDAG //===----------------------------------------------------------------------===// let Predicates = [IsRV64] in { -def : LdPat; // Prefer unsigned due to no c.lb in Zcb. -def : LdPat; - -def : StPat; -def : StPat; - def : Pat<(sext (i32 GPR:$src)), (ADDIW GPR:$src, 0)>; def : Pat<(sext_inreg (i64 (add GPR:$rs1, simm12_lo:$imm)), i32), @@ -190,29 +162,3 @@ let Predicates = [HasStdExtZbkb, NoStdExtZbb, IsRV64] in { def : Pat<(i64 (zext (i16 GPR:$rs))), (PACKW GPR:$rs, (XLenVT X0))>; def : Pat<(i32 (zext (i16 GPR:$rs))), (PACKW GPR:$rs, (XLenVT X0))>; } - -//===----------------------------------------------------------------------===// -// Zalasr patterns not used by SelectionDAG -//===----------------------------------------------------------------------===// - -let Predicates = [HasStdExtZalasr] in { - // the sequentially consistent loads use - // .aq instead of .aqrl to match the psABI/A.7 - def : PatLAQ, LB_AQ, i16>; - def : PatLAQ, LB_AQ, i16>; - - def : PatLAQ, LH_AQ, i16>; - def : PatLAQ, LH_AQ, i16>; - - def : PatSRL, SB_RL, i16>; - def : PatSRL, SB_RL, i16>; - - def : PatSRL, SH_RL, i16>; - def : PatSRL, SH_RL, i16>; -} - -let Predicates = [HasStdExtZalasr, IsRV64] in { - // Load pattern is in RISCVInstrInfoZalasr.td and shared with RV32. - def : PatSRL, SW_RL, i32>; - def : PatSRL, SW_RL, i32>; -} diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td index 9855c47a63392..7a149290e8d36 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td @@ -1980,7 +1980,7 @@ def : LdPat; def : LdPat; // Prefer unsigned due to no c.lb in Zcb. def : LdPat; def : LdPat; -def : LdPat; +def : LdPat, Requires<[IsRV32]>; def : LdPat; def : LdPat; @@ -1994,7 +1994,7 @@ class StPat; def : StPat; -def : StPat; +def : StPat, Requires<[IsRV32]>; /// Fences diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoA.td b/llvm/lib/Target/RISCV/RISCVInstrInfoA.td index 25accd93eaa03..216ee0c2cc806 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfoA.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoA.td @@ -174,8 +174,9 @@ let Predicates = [HasAtomicLdSt] in { def : StPat, SB, GPR, XLenVT>; def : StPat, SH, GPR, XLenVT>; def : StPat, SW, GPR, XLenVT>; +} - // Used by GISel for RV32 and RV64. +let Predicates = [HasAtomicLdSt, IsRV32] in { def : LdPat, LW, i32>; } diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoZalasr.td b/llvm/lib/Target/RISCV/RISCVInstrInfoZalasr.td index 1deecd2ca6634..711c52d3bf6a2 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfoZalasr.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoZalasr.td @@ -93,11 +93,12 @@ let Predicates = [HasStdExtZalasr] in { def : PatSRL, SW_RL>; def : PatSRL, SW_RL>; +} // Predicates = [HasStdExtZalasr] - // Used by GISel for RV32 and RV64. +let Predicates = [HasStdExtZalasr, IsRV32] in { def : PatLAQ, LW_AQ, i32>; def : PatLAQ, LW_AQ, i32>; -} // Predicates = [HasStdExtZalasr] +} // Predicates = [HasStdExtZalasr, IsRV32] let Predicates = [HasStdExtZalasr, IsRV64] in { def : PatLAQ, LW_AQ, i64>; From 518b5e89f73a8b8b60eb5000ce0e9a23156a55a5 Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Sat, 4 Oct 2025 22:11:03 -0700 Subject: [PATCH 3/4] fixup! clang-format --- .../RISCV/GISel/RISCVInstructionSelector.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp index 89e7234d78781..b702d4d9ce76c 100644 --- a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp +++ b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp @@ -868,13 +868,11 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) { return false; #ifndef NDEBUG - const RegisterBank &PtrRB = - *RBI.getRegBank(PtrReg, *MRI, TRI); + const RegisterBank &PtrRB = *RBI.getRegBank(PtrReg, *MRI, TRI); // Check that the pointer register is valid. assert(PtrRB.getID() == RISCV::GPRBRegBankID && "Load/Store pointer operand isn't a GPR"); - assert(PtrTy.isPointer() && - "Load/Store pointer operand isn't a pointer"); + assert(PtrTy.isPointer() && "Load/Store pointer operand isn't a pointer"); #endif // Can only handle AddressSpace 0. @@ -886,13 +884,12 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) { if (isStrongerThanMonotonic(Order)) { assert(MemSizeInBytes <= 8 && "Unexpected mem size!"); - static constexpr unsigned LoadOpcodes[] = { - RISCV::LB_AQ, RISCV::LH_AQ, RISCV::LW_AQ, RISCV::LD_AQ - }; - static constexpr unsigned StoreOpcodes[] = { - RISCV::SB_RL, RISCV::SH_RL, RISCV::SW_RL, RISCV::SD_RL - }; - ArrayRef Opcodes = isa(LdSt) ? LoadOpcodes : StoreOpcodes; + static constexpr unsigned LoadOpcodes[] = {RISCV::LB_AQ, RISCV::LH_AQ, + RISCV::LW_AQ, RISCV::LD_AQ}; + static constexpr unsigned StoreOpcodes[] = {RISCV::SB_RL, RISCV::SH_RL, + RISCV::SW_RL, RISCV::SD_RL}; + ArrayRef Opcodes = + isa(LdSt) ? LoadOpcodes : StoreOpcodes; MI.setDesc(TII.get(Opcodes[Log2_32(MemSizeInBytes)])); return constrainSelectedInstRegOperands(MI, TII, TRI, RBI); } From 808075027bbca66330c3f2a269adadd0e862250d Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Mon, 6 Oct 2025 21:09:55 -0700 Subject: [PATCH 4/4] fixup! Add function for picking Zalasr opcode. --- .../RISCV/GISel/RISCVInstructionSelector.cpp | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp index b702d4d9ce76c..be57976fbb795 100644 --- a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp +++ b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp @@ -675,20 +675,39 @@ static void getOperandsForBranch(Register CondReg, RISCVCC::CondCode &CC, CC = getRISCVCCFromICmp(Pred); } -/// Select the RISC-V opcode for the G_LOAD or G_STORE operation \p GenericOpc, -/// appropriate for the GPR register bank and of memory access size \p OpSize. -/// \returns \p GenericOpc if the combination is unsupported. -static unsigned selectLoadStoreOp(unsigned GenericOpc, unsigned OpSizeInBytes) { +/// Select the RISC-V Zalasr opcode for the G_LOAD or G_STORE operation +/// \p GenericOpc, appropriate for the GPR register bank and of memory access +/// size \p OpSize. +static unsigned selectZalasrLoadStoreOp(unsigned GenericOpc, unsigned OpSize) { const bool IsStore = GenericOpc == TargetOpcode::G_STORE; - switch (OpSizeInBytes) { - case 1: + switch (OpSize) { + default: + llvm_unreachable("Unexpected memory size"); + case 8: + return IsStore ? RISCV::SB_RL : RISCV::LB_AQ; + case 16: + return IsStore ? RISCV::SH_RL : RISCV::LH_AQ; + case 32: + return IsStore ? RISCV::SW_RL : RISCV::LW_AQ; + case 64: + return IsStore ? RISCV::SD_RL : RISCV::LD_AQ; + } +} + +/// Select the RISC-V regimm opcode for the G_LOAD or G_STORE operation +/// \p GenericOpc, appropriate for the GPR register bank and of memory access +/// size \p OpSize. \returns \p GenericOpc if the combination is unsupported. +static unsigned selectRegImmLoadStoreOp(unsigned GenericOpc, unsigned OpSize) { + const bool IsStore = GenericOpc == TargetOpcode::G_STORE; + switch (OpSize) { + case 8: // Prefer unsigned due to no c.lb in Zcb. return IsStore ? RISCV::SB : RISCV::LBU; - case 2: + case 16: return IsStore ? RISCV::SH : RISCV::LH; - case 4: + case 32: return IsStore ? RISCV::SW : RISCV::LW; - case 8: + case 64: return IsStore ? RISCV::SD : RISCV::LD; } @@ -879,22 +898,15 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) { if (PtrTy.getAddressSpace() != 0) return false; - unsigned MemSizeInBytes = LdSt.getMemSize().getValue(); + unsigned MemSize = LdSt.getMemSizeInBits().getValue(); AtomicOrdering Order = LdSt.getMMO().getSuccessOrdering(); if (isStrongerThanMonotonic(Order)) { - assert(MemSizeInBytes <= 8 && "Unexpected mem size!"); - static constexpr unsigned LoadOpcodes[] = {RISCV::LB_AQ, RISCV::LH_AQ, - RISCV::LW_AQ, RISCV::LD_AQ}; - static constexpr unsigned StoreOpcodes[] = {RISCV::SB_RL, RISCV::SH_RL, - RISCV::SW_RL, RISCV::SD_RL}; - ArrayRef Opcodes = - isa(LdSt) ? LoadOpcodes : StoreOpcodes; - MI.setDesc(TII.get(Opcodes[Log2_32(MemSizeInBytes)])); + MI.setDesc(TII.get(selectZalasrLoadStoreOp(Opc, MemSize))); return constrainSelectedInstRegOperands(MI, TII, TRI, RBI); } - const unsigned NewOpc = selectLoadStoreOp(MI.getOpcode(), MemSizeInBytes); + const unsigned NewOpc = selectRegImmLoadStoreOp(MI.getOpcode(), MemSize); if (NewOpc == MI.getOpcode()) return false;