In SFrame V2 the size of the one to three offsets following a SFrame FDE
can be either signed 8-bit, 16-bit, or 32-bit integer, which the largest
offset determining their size:
1. CFA offset from CFA base register
2. RA (stack save slot) offset from CFA, usually -48 on s390x if saved
3. FP (stack save slot) offset from CFA, usually -72 on s390x if saved
The FP and RA offsets from CFA, when FP/RA saved on the stack, usually
have fixed values that fit into signed 8-bit SFrame offsets. Likewise
the DWARF register numbers on s390x of general registers (GR; 0-15) and
floating-point registers (FPR; 16-31), when FP/RA saved in registers.
With that the CFA offset from CFA base register has the greatest impact
on the signed SFrame offset size.
The s390x ELF ABI [1] defines the CFA as stack pointer (SP) at call
site +160. [2] Therefore the minimum CFA offset from CFA base register
on s390x is 160. This does not fit into a signed 8-bit integer and
therefore effectively prevents any use of signed 8-bit SFrame offsets
on s390x.
For s390x store the CFA offset from CFA base register adjusted by -160
to enable the use of signed 8-bit SFrame offsets.
[1]: s390x ELF ABI, https://github.com/IBM/s390x-abi/releases
[2]: s390x ELF ABI, commit 4e38ad9c8a88 ("Document the CFA"),
https://github.com/IBM/s390x-abi/commit/4e38ad9c8a88
include/
* sframe.h (SFRAME_S390_CFA_OFFSET_ADJUSTMENT): Define
s390x-specific CFA offset adjustment.
(SFRAME_V2_FRE_S390_CFA_OFFSET_ENCODE,
SFRAME_V2_FRE_S390_CFA_OFFSET_DECODE): New S390-specific macros.
Use SFRAME_S390_CFA_OFFSET_ADJUSTMENT to en-/decode CFA offset.
bfd/
* elf64-s390.c (elf_s390x_sframe_plt_fre): Use
SFRAME_V2_FRE_S390_CFA_OFFSET_ENCODE on CFA offset to store it
adjusted and switch to 8-bit offsets.
gas/
* gen-sframe.c (sframe_fre_set_cfa_offset): For S390 use
SFRAME_V2_FRE_S390_CFA_OFFSET_ENCODE on CFA offset to store it
adjusted.
(sframe_fre_get_cfa_offset): New helper. For S390 use
SFRAME_V2_FRE_S390_CFA_OFFSET_DECODE on CFA offset to undo its
adjustment.
(sframe_xlate_do_def_cfa_register): Use new helper
sframe_fre_get_cfa_offset.
libsframe/
* sframe.c (sframe_fre_get_cfa_offset): For S390 use
SFRAME_V2_FRE_S390_CFA_OFFSET_DECODE on CFA offset to undo its
adjustment.
* doc/sframe-spec.texi (S390,
SFRAME_S390_CFA_OFFSET_ADJUSTMENT,
SFRAME_V2_FRE_S390_CFA_OFFSET_ENCODE,
SFRAME_V2_FRE_S390_CFA_OFFSET_DECODE): Document S390-specific
adjustment of CFA offset.
Suggested-by: Indu Bhagat <indu.bhagat@oracle.com>
Signed-off-by: Jens Remus <jremus@linux.ibm.com>
---
Notes (jremus):
A test build of Glibc tag 2.41 on s390x libc.so shows a ~8.8% reduction in
.sframe section size due to storing the SFrame CFA offsets adjusted by
-160.
Statistics for libc.so - base (no CFA offset adjustment):
.sframe size: 169,749 bytes
VALUE TOTAL MIN MAX AVG
FDEs: 3652 - - -
FREs/FDE: 15236 1 20 4
Offsets/FDE: 29792 1 38 8
8-bit: 0 0 0 0
16-bit: 29792 1 38 8
32-bit: 0 0 0 0
Offsets/FRE: 29792 1 3 1
8-bit: - 0 0 0
16-bit: - 1 3 1
32-bit: - 0 0 0
O_Padd/FDE: 342 - - 0
8-bit: 0
16-bit: 342
32-bit: 0
Statistics for libc.so - CFA offset adjustment:
.sframe size: 154,757 bytes
VALUE TOTAL MIN MAX AVG
FDEs: 3654 - - -
FREs/FDE: 15238 1 20 4
Offsets/FDE: 29794 1 38 8
8-bit: 14992 1 38 4
16-bit: 14802 0 0 4
32-bit: 0 0 0 0
Offsets/FRE: 29794 2 6 1
8-bit: - 1 3 0
16-bit: - 1 3 0
32-bit: - 0 0 0
O_Padd/FDE: 342 - - 0
8-bit: 283
16-bit: 59
32-bit: 0
bfd/elf64-s390.c | 4 ++--
gas/gen-sframe.c | 18 +++++++++++++++++-
include/sframe.h | 8 ++++++++
libsframe/doc/sframe-spec.texi | 10 +++++++++-
libsframe/sframe.c | 10 ++++++++--
5 files changed, 44 insertions(+), 6 deletions(-)
@@ -621,8 +621,8 @@ struct elf_s390x_sframe_plt
static const sframe_frame_row_entry elf_s390x_sframe_plt_fre =
{
0, /* SFrame FRE start address. */
- {0, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* 12 bytes. */
- SFRAME_V1_FRE_INFO (SFRAME_BASE_REG_SP, 1, SFRAME_FRE_OFFSET_2B) /* FRE info. */
+ { SFRAME_V2_FRE_S390_CFA_OFFSET_ENCODE (160), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* Offset bytes. */
+ SFRAME_V1_FRE_INFO (SFRAME_BASE_REG_SP, 1, SFRAME_FRE_OFFSET_1B) /* FRE info. */
};
/* SFrame helper object for PLT. */
@@ -146,10 +146,26 @@ sframe_fre_set_cfa_base_reg (struct sframe_row_entry *fre,
fre->merge_candidate = false;
}
+static offsetT
+sframe_fre_get_cfa_offset (const struct sframe_row_entry * fre)
+{
+ offsetT offset = fre->cfa_offset;
+
+ /* For S390 undo adjustment of CFA offset (to enable 8-bit offsets). */
+ if (sframe_get_abi_arch () == SFRAME_ABI_S390_ENDIAN_BIG)
+ offset = SFRAME_V2_FRE_S390_CFA_OFFSET_DECODE (offset);
+
+ return offset;
+}
+
static void
sframe_fre_set_cfa_offset (struct sframe_row_entry *fre,
offsetT cfa_offset)
{
+ /* For S390 adjust CFA offset to enable 8-bit offsets. */
+ if (sframe_get_abi_arch () == SFRAME_ABI_S390_ENDIAN_BIG)
+ cfa_offset = SFRAME_V2_FRE_S390_CFA_OFFSET_ENCODE (cfa_offset);
+
fre->cfa_offset = cfa_offset;
fre->merge_candidate = false;
}
@@ -1056,7 +1072,7 @@ sframe_xlate_do_def_cfa_register (struct sframe_xlate_ctx *xlate_ctx,
return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */
}
sframe_fre_set_cfa_base_reg (cur_fre, cfi_insn->u.r);
- sframe_fre_set_cfa_offset (cur_fre, last_fre->cfa_offset);
+ sframe_fre_set_cfa_offset (cur_fre, sframe_fre_get_cfa_offset (last_fre));
cur_fre->merge_candidate = false;
return SFRAME_XLATE_OK;
@@ -364,6 +364,14 @@ typedef struct sframe_frame_row_entry_addr4
#define SFRAME_FRE_TYPE_ADDR4_LIMIT \
(1ULL << ((SFRAME_FRE_TYPE_ADDR4 * 2) * 8))
+/* On S390, the CFA offset from CFA base register is by definition a minimum
+ of 160. Store it adjusted by -160 to enable use of 8-bit SFrame offsets. */
+#define SFRAME_S390_CFA_OFFSET_ADJUSTMENT SFRAME_S390_SP_VAL_OFFSET
+#define SFRAME_V2_FRE_S390_CFA_OFFSET_ENCODE(offset) \
+ ((offset) + SFRAME_S390_CFA_OFFSET_ADJUSTMENT)
+#define SFRAME_V2_FRE_S390_CFA_OFFSET_DECODE(offset) \
+ ((offset) - SFRAME_S390_CFA_OFFSET_ADJUSTMENT)
+
/* On S390, the CFA is defined as SP at call site + 160. Therefore the
SP value offset from CFA is -160. */
#define SFRAME_S390_SP_VAL_OFFSET -160
@@ -837,7 +837,15 @@ Hence, in summary:
@section S390
Irrespective of the ABI, the first stack offset is always used to locate the
-CFA, by interpreting it as: CFA = @code{BASE_REG} + offset1.
+CFA. On S390 the value of the offset is stored adjusted by the S390-specific
+@code{SFRAME_S390_CFA_OFFSET_ADJUSTMENT} to enable the use of signed 8-bit
+offsets on S390.
+S390-specific helpers @code{SFRAME_V2_FRE_S390_CFA_OFFSET_ENCODE} and
+@code{SFRAME_V2_FRE_S390_CFA_OFFSET_DECODE} are provided to perform and undo the
+adjustment. The CFA offset can therefore be interpreted as:
+CFA = @code{BASE_REG} + offset1 - @code{SFRAME_S390_CFA_OFFSET_ADJUSTMENT}
+or
+CFA = @code{BASE_REG} + @code{SFRAME_V2_FRE_S390_CFA_OFFSET_DECODE(offset1)}.
The identification of the @code{BASE_REG} is done by using the
@code{fre_cfa_base_reg_id} field in the SFrame FRE info word.
@@ -682,10 +682,16 @@ sframe_fre_get_base_reg_id (sframe_frame_row_entry *fre, int *errp)
/* Get the CFA offset from the FRE. If the offset is invalid, sets errp. */
int32_t
-sframe_fre_get_cfa_offset (sframe_decoder_ctx *dctx ATTRIBUTE_UNUSED,
+sframe_fre_get_cfa_offset (sframe_decoder_ctx *dctx,
sframe_frame_row_entry *fre, int *errp)
{
- return sframe_get_fre_offset (fre, SFRAME_FRE_CFA_OFFSET_IDX, errp);
+ int32_t offset = sframe_get_fre_offset (fre, SFRAME_FRE_CFA_OFFSET_IDX, errp);
+
+ /* For S390 undo adjustment of CFA offset (to enable 8-bit offsets). */
+ if (sframe_decoder_get_abi_arch (dctx) == SFRAME_ABI_S390_ENDIAN_BIG)
+ offset = SFRAME_V2_FRE_S390_CFA_OFFSET_DECODE (offset);
+
+ return offset;
}
/* Get the FP offset from the FRE. If the offset is invalid, sets errp. */