When the target lacks standalone floating-point registers (FPRs), such
as in soft-float configurations or with the Zfinx extension,
floating-point values reside in general-purpose registers (GPRs). Which
means we may be able to materialize FP constants using int instruction.
Currently, however, FP constants are typically loaded from constant
pool via memory loads, even when they reside in GPRs.
This patch allows the compiler to materialize FP constants directly in
GPRs using integer instructions (e.g., lui, addi) if the target lacks
standalone FPRs and the cost is sufficiently low.
For example, given the following C code:
float foo() {
// 0x3fc00000
return 1.5f;
}
Original codegen (with Zfinx):
foo():
lui a5,%hi(.LC0)
lw a0,%lo(.LC0)(a5)
ret
.LC0:
.word 1069547520
After this patch:
foo():
lui a0, 261120 # hex(261120) = 0x3FC00
ret
gcc/ChangeLog:
* config/riscv/predicates.md (move_operand): Disallow
CONST_DOUBLE for soft-float and zfinx to prevent reload from
forcing them to memory.
* config/riscv/riscv.cc (riscv_const_fp_to_int): New static
helper function to get int representaion for a compile-time
const FP value.
(riscv_const_insns): Calculate cost for materializing FP
constants as integers when the target lacks standalone FPRs.
(riscv_legitimize_const_move): Attempt to materialize FP
constants using integer instructions.
gcc/testsuite/ChangeLog:
* gcc.target/riscv/zfinx-const-li.c: New test for FP
materialization when zfinx-like extension is enabled.
* gcc.target/riscv/softfloat-const-li.c: New test for FP
materialization when target is in softfloat mode.
Signed-off-by: Meng-Tsung Tsai <[email protected]>
---
Changes in v2:
- Extended the optimization scope from just Zfinx to all soft-float
targets (!TARGET_HARD_FLOAT).
- Remove unneeded pattern from iterator.md
- Moved the materialization logic to riscv_legitimize_const_move in
riscv.cc
- Introduced a new helper function riscv_const_fp_to_int to get int
representaion for a compile-time const FP value.
- A new test suite softfloat-const-li.c is introduced for rv32i/rv64i
target.
gcc/config/riscv/predicates.md | 3 ++
gcc/config/riscv/riscv.cc | 43 +++++++++++++++
.../gcc.target/riscv/softfloat-const-li.c | 52 +++++++++++++++++++
.../gcc.target/riscv/zfinx-const-li.c | 52 +++++++++++++++++++
4 files changed, 150 insertions(+)
create mode 100644 gcc/testsuite/gcc.target/riscv/softfloat-const-li.c
create mode 100644 gcc/testsuite/gcc.target/riscv/zfinx-const-li.c
diff --git a/gcc/config/riscv/predicates.md b/gcc/config/riscv/predicates.md
index 5b44165ec99..a79e5f7db76 100644
--- a/gcc/config/riscv/predicates.md
+++ b/gcc/config/riscv/predicates.md
@@ -341,6 +341,9 @@
case SUBREG:
return REG_P (SUBREG_REG (op));
+ case CONST_DOUBLE:
+ return TARGET_HARD_FLOAT;
+
default:
return true;
}
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 96519c96a2b..ccdfec65b46 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -2579,6 +2579,22 @@ riscv_address_insns (rtx x, machine_mode mode,
bool might_split_p)
return n;
}
+static unsigned HOST_WIDE_INT
+riscv_const_fp_to_int (rtx src, machine_mode mode)
+{
+ long target_vals[2] = {0};
+ real_to_target (target_vals, CONST_DOUBLE_REAL_VALUE (src), mode);
+
+ int order = BYTES_BIG_ENDIAN ? 1 : 0;
+ unsigned HOST_WIDE_INT lo = target_vals[order];
+ unsigned HOST_WIDE_INT hi = target_vals[1 - order];
+ unsigned HOST_WIDE_INT val = (hi << 32) | lo;
+
+ val &= GET_MODE_MASK (mode);
+
+ return val;
+}
+
/* Return the number of instructions needed to load constant X.
Return 0 if X isn't a valid constant.
@@ -2614,6 +2630,16 @@ riscv_const_insns (rtx x, bool allow_new_pseudos)
if (satisfies_constraint_zfli (x))
return 1;
+ /* When target support Zfinx-like extension, we can use li to
+ materialize FP constants. */
+ if (!TARGET_HARD_FLOAT
+ && known_le (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD))
+ {
+ unsigned HOST_WIDE_INT val = riscv_const_fp_to_int (x, GET_MODE (x));
+ int cost = riscv_integer_cost (val, allow_new_pseudos);
+ return cost < 4 ? cost : 0;
+ }
+
/* We can use x0 to load floating-point zero. */
return x == CONST0_RTX (GET_MODE (x)) ? 1 : 0;
case CONST_VECTOR:
@@ -3437,6 +3463,23 @@ riscv_legitimize_const_move (machine_mode mode,
rtx dest, rtx src)
return;
}
+ /* We can use integer instructions to materialize FP constants for
+ soft-float or Zfinx-like targets. */
+ bool fits_in_gpr = known_le (GET_MODE_SIZE (mode), UNITS_PER_WORD);
+ if (!TARGET_HARD_FLOAT && GET_CODE (src) == CONST_DOUBLE && fits_in_gpr)
+ {
+ unsigned HOST_WIDE_INT val = riscv_const_fp_to_int (src, mode);
+ val &= GET_MODE_MASK (mode);
+ int cost = riscv_integer_cost (val, true);
+ if (cost < 4)
+ {
+ machine_mode int_mode = int_mode_for_mode (mode).require ();
+ rtx int_reg = gen_lowpart (int_mode, dest);
+ riscv_move_integer (int_reg, int_reg, val, int_mode);
+ return;
+ }
+ }
+
src = force_const_mem (mode, src);
/* When using explicit relocs, constant pool references are sometimes
diff --git a/gcc/testsuite/gcc.target/riscv/softfloat-const-li.c
b/gcc/testsuite/gcc.target/riscv/softfloat-const-li.c
new file mode 100644
index 00000000000..5c822ebbf20
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/softfloat-const-li.c
@@ -0,0 +1,52 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64i -mabi=lp64" { target rv64 } } */
+/* { dg-options "-march=rv32i -mabi=ilp32" { target rv32 } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+float foo_sf () {
+ /* 1.5f = 0x3fc00000 in float */
+
+ /*
+ li a0,1069547520
+ ret
+ */
+ return 1.5;
+}
+
+double foo_df () {
+ /* 1.5f = 0x3ff8000000000000 in double */
+
+ /*
+ target rv64:
+ li a0,2047
+ slli a0,a0,51
+ ret
+
+ target rv32:
+ lui a5,%hi(.LC0)
+ lw a0,%lo(.LC0)(a5)
+ lw a1,%lo(.LC0+4)(a5)
+ ret
+ .LC0:
+ .word 0
+ .word 1073217536
+ */
+ return 1.5;
+}
+
+_Float16 foo_hf () {
+ /* 1.5 = 0x3e00 in half-float */
+
+ /*
+ li a0,16384
+ addi a0,a0,-512
+ ret
+ */
+ return 1.5;
+}
+
+/* { dg-final { scan-assembler "li|lui" } } */
+/* For rv64 target, there should be no lw */
+/* { dg-final { scan-assembler-not "\tlw\t" { target { rv64 } } } } */
+/* For rv32 target, only foo_df will emit 2 lw */
+/* { dg-final { scan-assembler-times "\tlw\t" 2 { target { rv32 } } } } */
diff --git a/gcc/testsuite/gcc.target/riscv/zfinx-const-li.c
b/gcc/testsuite/gcc.target/riscv/zfinx-const-li.c
new file mode 100644
index 00000000000..b0a312fbf65
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zfinx-const-li.c
@@ -0,0 +1,52 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64i_zfinx -mabi=lp64" { target rv64 } } */
+/* { dg-options "-march=rv32i_zfinx -mabi=ilp32" { target rv32 } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+float foo_sf () {
+ /* 1.5f = 0x3fc00000 in float */
+
+ /*
+ li a0,1069547520
+ ret
+ */
+ return 1.5;
+}
+
+double foo_df () {
+ /* 1.5f = 0x3ff8000000000000 in double */
+
+ /*
+ target rv64:
+ li a0,2047
+ slli a0,a0,51
+ ret
+
+ target rv32:
+ lui a5,%hi(.LC0)
+ lw a0,%lo(.LC0)(a5)
+ lw a1,%lo(.LC0+4)(a5)
+ ret
+ .LC0:
+ .word 0
+ .word 1073217536
+ */
+ return 1.5;
+}
+
+_Float16 foo_hf () {
+ /* 1.5 = 0x3e00 in half-float */
+
+ /*
+ li a0,16384
+ addi a0,a0,-512
+ ret
+ */
+ return 1.5;
+}
+
+/* { dg-final { scan-assembler "li|lui" } } */
+/* For rv64 target, there should be no lw */
+/* { dg-final { scan-assembler-not "\tlw\t" { target { rv64 } } } } */
+/* For rv32 target, only foo_df will emit 2 lw */
+/* { dg-final { scan-assembler-times "\tlw\t" 2 { target { rv32 } } } } */
--
2.43.0