From 13b6c7639cfdca892a3f02b63596b097e1839f38 Mon Sep 17 00:00:00 2001 From: Andrew Stubbs Date: Thu, 11 Nov 2021 13:43:04 +0000 Subject: [PATCH] dwarf: Multi-register CFI address support. Add support for architectures such as AMD GCN, in which the pointer size is larger than the register size. This allows the CFI information to include multi-register locations for the stack pointer, frame pointer, and return address. This patch was originally posted by Andrew Stubbs in https://gcc.gnu.org/pipermail/gcc-patches/2020-August/552873.html It has now been re-worked according to the review comments. It does not use DW_OP_piece or DW_OP_LLVM_piece_end. Instead it uses DW_OP_bregx/DW_OP_shl/DW_OP_bregx/DW_OP_plus to build the CFA from multiple consecutive registers. Here is how .debug_frame looks before and after this patch: $ cat factorial.c int factorial(int n) { if (n == 0) return 1; return n * factorial (n - 1); } $ amdgcn-amdhsa-gcc -g factorial.c -O0 -c -o fac.o $ llvm-dwarfdump -debug-frame fac.o *** without this patch (edited for brevity)*** 00000000 00000014 ffffffff CIE DW_CFA_def_cfa: reg48 +0 DW_CFA_register: reg16 reg50 00000018 0000002c 00000000 FDE cie=00000000 pc=00000000...000001ac DW_CFA_advance_loc4: 96 DW_CFA_offset: reg46 0 DW_CFA_offset: reg47 4 DW_CFA_offset: reg50 8 DW_CFA_offset: reg51 12 DW_CFA_offset: reg16 8 DW_CFA_advance_loc4: 4 DW_CFA_def_cfa_sf: reg46 -16 *** with this patch (edited for brevity)*** 00000000 00000024 ffffffff CIE DW_CFA_def_cfa_expression: DW_OP_bregx SGPR49+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR48+0, DW_OP_plus DW_CFA_expression: reg16 DW_OP_bregx SGPR51+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR50+0, DW_OP_plus 00000028 0000003c 00000000 FDE cie=00000000 pc=00000000...000001ac DW_CFA_advance_loc4: 96 DW_CFA_offset: reg46 0 DW_CFA_offset: reg47 4 DW_CFA_offset: reg50 8 DW_CFA_offset: reg51 12 DW_CFA_offset: reg16 8 DW_CFA_advance_loc4: 4 DW_CFA_def_cfa_expression: DW_OP_bregx SGPR47+0, DW_OP_const1u 0x20, DW_OP_shl, DW_OP_bregx SGPR46+0, DW_OP_plus, DW_OP_lit16, DW_OP_minus gcc/ChangeLog: * dwarf2cfi.c (dw_stack_pointer_regnum): Change type to struct cfa_reg. (dw_frame_pointer_regnum): Likewise. (new_cfi_row): Use set_by_dwreg. (get_cfa_from_loc_descr): Use set_by_dwreg. Support register spans. handle DW_OP_bregx with DW_OP_breg{0-31}. Support DW_OP_lit*, DW_OP_const*, DW_OP_minus, DW_OP_shl and DW_OP_plus. (lookup_cfa_1): Use set_by_dwreg. (def_cfa_0): Update for cfa_reg and support register spans. (reg_save): Change sreg parameter to struct cfa_reg. Support register spans. (dwf_cfa_reg): New function. (dwarf2out_flush_queued_reg_saves): Use dwf_cfa_reg instead of dwf_regno. (dwarf2out_frame_debug_def_cfa): Likewise. (dwarf2out_frame_debug_adjust_cfa): Likewise. (dwarf2out_frame_debug_cfa_offset): Likewise. Update reg_save usage. (dwarf2out_frame_debug_cfa_register): Likewise. (dwarf2out_frame_debug_expr): Likewise. (create_pseudo_cfg): Use set_by_dwreg. (initial_return_save): Use set_by_dwreg and dwf_cfa_reg, (create_cie_data): Use dwf_cfa_reg. (execute_dwarf2_frame): Use dwf_cfa_reg. (dump_cfi_row): Use set_by_dwreg. * dwarf2out.c (build_span_loc, build_breg_loc): New function. (build_cfa_loc): Support register spans. (build_cfa_aligned_loc): Update cfa_reg usage. (convert_cfa_to_fb_loc_list): Use set_by_dwreg. * dwarf2out.h (struct cfa_reg): New type. (struct dw_cfa_location): Use struct cfa_reg. (build_span_loc): New prototype. co-authored-By: Hafiz Abid Qadeer --- gcc/dwarf2cfi.c | 264 ++++++++++++++++++++++++++++++++++++++++++-------------- gcc/dwarf2out.c | 54 ++++++++++-- gcc/dwarf2out.h | 36 +++++++- 3 files changed, 284 insertions(+), 70 deletions(-) diff --git a/gcc/dwarf2cfi.c b/gcc/dwarf2cfi.c index df9b625..9dd1dfe 100644 --- a/gcc/dwarf2cfi.c +++ b/gcc/dwarf2cfi.c @@ -229,8 +229,8 @@ static vec queued_reg_saves; static bool any_cfis_emitted; /* Short-hand for commonly used register numbers. */ -static unsigned dw_stack_pointer_regnum; -static unsigned dw_frame_pointer_regnum; +static struct cfa_reg dw_stack_pointer_regnum; +static struct cfa_reg dw_frame_pointer_regnum; /* Hook used by __throw. */ @@ -430,7 +430,7 @@ new_cfi_row (void) { dw_cfi_row *row = ggc_cleared_alloc (); - row->cfa.reg = INVALID_REGNUM; + row->cfa.reg.set_by_dwreg (INVALID_REGNUM); return row; } @@ -538,7 +538,7 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc) cfa->offset = 0; cfa->base_offset = 0; cfa->indirect = 0; - cfa->reg = -1; + cfa->reg.set_by_dwreg (INVALID_REGNUM); for (ptr = loc; ptr != NULL; ptr = ptr->dw_loc_next) { @@ -578,10 +578,10 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc) case DW_OP_reg29: case DW_OP_reg30: case DW_OP_reg31: - cfa->reg = op - DW_OP_reg0; + cfa->reg.set_by_dwreg (op - DW_OP_reg0); break; case DW_OP_regx: - cfa->reg = ptr->dw_loc_oprnd1.v.val_int; + cfa->reg.set_by_dwreg (ptr->dw_loc_oprnd1.v.val_int); break; case DW_OP_breg0: case DW_OP_breg1: @@ -615,16 +615,95 @@ get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc) case DW_OP_breg29: case DW_OP_breg30: case DW_OP_breg31: - cfa->reg = op - DW_OP_breg0; - cfa->base_offset = ptr->dw_loc_oprnd1.v.val_int; - break; case DW_OP_bregx: - cfa->reg = ptr->dw_loc_oprnd1.v.val_int; - cfa->base_offset = ptr->dw_loc_oprnd2.v.val_int; + if (cfa->reg.reg == INVALID_REGNUM) + { + unsigned regno + = (op == DW_OP_bregx + ? ptr->dw_loc_oprnd1.v.val_int : op - DW_OP_breg0); + cfa->reg.set_by_dwreg (regno); + cfa->base_offset = ptr->dw_loc_oprnd1.v.val_int; + } + else + { + /* Handle case when span can cover multiple registers. We + only support the simple case of consecutive registers + all with the same size. DWARF that we are dealing with + will look something like: + */ + + unsigned regno + = (op == DW_OP_bregx + ? ptr->dw_loc_oprnd1.v.val_int : op - DW_OP_breg0); + gcc_assert (regno == cfa->reg.reg - 1); + cfa->reg.span++; + /* From all the consecutive registers used, we want to set + cfa->reg.reg to lower number register. */ + cfa->reg.reg = regno; + /* The offset was the shift value. Use it to get the + span_width and then set it to 0. */ + cfa->reg.span_width = cfa->offset.to_constant () / 8; + cfa->offset = 0; + } break; case DW_OP_deref: cfa->indirect = 1; break; + case DW_OP_shl: + break; + case DW_OP_lit0: + case DW_OP_lit1: + case DW_OP_lit2: + case DW_OP_lit3: + case DW_OP_lit4: + case DW_OP_lit5: + case DW_OP_lit6: + case DW_OP_lit7: + case DW_OP_lit8: + case DW_OP_lit9: + case DW_OP_lit10: + case DW_OP_lit11: + case DW_OP_lit12: + case DW_OP_lit13: + case DW_OP_lit14: + case DW_OP_lit15: + case DW_OP_lit16: + case DW_OP_lit17: + case DW_OP_lit18: + case DW_OP_lit19: + case DW_OP_lit20: + case DW_OP_lit21: + case DW_OP_lit22: + case DW_OP_lit23: + case DW_OP_lit24: + case DW_OP_lit25: + case DW_OP_lit26: + case DW_OP_lit27: + case DW_OP_lit28: + case DW_OP_lit29: + case DW_OP_lit30: + case DW_OP_lit31: + gcc_assert (known_eq (cfa->offset, 0)); + cfa->offset = op - DW_OP_lit0; + break; + case DW_OP_const1u: + case DW_OP_const1s: + case DW_OP_const2u: + case DW_OP_const2s: + case DW_OP_const4s: + case DW_OP_const8s: + case DW_OP_constu: + case DW_OP_consts: + gcc_assert (known_eq (cfa->offset, 0)); + cfa->offset = ptr->dw_loc_oprnd1.v.val_int; + break; + case DW_OP_minus: + cfa->offset = -cfa->offset; + break; + case DW_OP_plus: + /* The offset is already in place. */ + break; case DW_OP_plus_uconst: cfa->offset = ptr->dw_loc_oprnd1.v.val_unsigned; break; @@ -648,11 +727,11 @@ lookup_cfa_1 (dw_cfi_ref cfi, dw_cfa_location *loc, dw_cfa_location *remember) loc->offset = cfi->dw_cfi_oprnd1.dw_cfi_offset; break; case DW_CFA_def_cfa_register: - loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num; + loc->reg.set_by_dwreg (cfi->dw_cfi_oprnd1.dw_cfi_reg_num); break; case DW_CFA_def_cfa: case DW_CFA_def_cfa_sf: - loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num; + loc->reg.set_by_dwreg (cfi->dw_cfi_oprnd1.dw_cfi_reg_num); loc->offset = cfi->dw_cfi_oprnd2.dw_cfi_offset; break; case DW_CFA_def_cfa_expression: @@ -798,6 +877,7 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa) HOST_WIDE_INT const_offset; if (new_cfa->reg == old_cfa->reg + && new_cfa->reg.span == 1 && !new_cfa->indirect && !old_cfa->indirect && new_cfa->offset.is_constant (&const_offset)) @@ -814,7 +894,8 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa) } else if (new_cfa->offset.is_constant () && known_eq (new_cfa->offset, old_cfa->offset) - && old_cfa->reg != INVALID_REGNUM + && old_cfa->reg.reg != INVALID_REGNUM + && new_cfa->reg.span == 1 && !new_cfa->indirect && !old_cfa->indirect) { @@ -824,10 +905,11 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa) been set as a register plus offset rather than a general DW_CFA_def_cfa_expression. */ cfi->dw_cfi_opc = DW_CFA_def_cfa_register; - cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg; + cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg.reg; } else if (new_cfa->indirect == 0 - && new_cfa->offset.is_constant (&const_offset)) + && new_cfa->offset.is_constant (&const_offset) + && new_cfa->reg.span == 1) { /* Construct a "DW_CFA_def_cfa " instruction, indicating the CFA register has changed to with @@ -838,7 +920,7 @@ def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa) cfi->dw_cfi_opc = DW_CFA_def_cfa_sf; else cfi->dw_cfi_opc = DW_CFA_def_cfa; - cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg; + cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg.reg; cfi->dw_cfi_oprnd2.dw_cfi_offset = const_offset; } else @@ -885,18 +967,18 @@ def_cfa_1 (dw_cfa_location *new_cfa) } /* Add the CFI for saving a register. REG is the CFA column number. - If SREG is -1, the register is saved at OFFSET from the CFA; + If SREG is INVALID_REGISTER, the register is saved at OFFSET from the CFA; otherwise it is saved in SREG. */ static void -reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset) +reg_save (unsigned int reg, struct cfa_reg sreg, poly_int64 offset) { dw_fde_ref fde = cfun ? cfun->fde : NULL; dw_cfi_ref cfi = new_cfi (); cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg; - if (sreg == INVALID_REGNUM) + if (sreg.reg == INVALID_REGNUM) { HOST_WIDE_INT const_offset; /* When stack is aligned, store REG using DW_CFA_expression with FP. */ @@ -926,7 +1008,7 @@ reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset) = build_cfa_loc (&cur_row->cfa, offset); } } - else if (sreg == reg) + else if (sreg.reg == reg) { /* While we could emit something like DW_CFA_same_value or DW_CFA_restore, we never expect to see something like that @@ -934,10 +1016,16 @@ reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset) can always bypass this by using REG_CFA_RESTORE directly. */ gcc_unreachable (); } + else if (sreg.span > 1) + { + cfi->dw_cfi_opc = DW_CFA_expression; + cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg; + cfi->dw_cfi_oprnd2.dw_cfi_loc = build_span_loc (sreg); + } else { cfi->dw_cfi_opc = DW_CFA_register; - cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg; + cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg.reg; } add_cfi (cfi); @@ -1018,6 +1106,44 @@ dwf_regno (const_rtx reg) return DWARF_FRAME_REGNUM (REGNO (reg)); } +/* Like dwf_regno, but when the value can span multiple registers. */ + +static struct cfa_reg +dwf_cfa_reg (rtx reg) +{ + struct cfa_reg result; + + gcc_assert (REGNO (reg) < FIRST_PSEUDO_REGISTER); + + result.reg = dwf_regno (reg); + result.span = 1; + result.span_width = 0; + + rtx span = targetm.dwarf_register_span (reg); + if (span) + { + /* We only support the simple case of consecutive registers all with the + same size. */ + result.span = XVECLEN (span, 0); + result.span_width = GET_MODE_SIZE (GET_MODE (XVECEXP (span, 0, 0))) + .to_constant (); + + if (CHECKING_P) + { + /* Ensure that the above assumption is accurate. */ + for (unsigned int i = 0; i < result.span; i++) + { + gcc_assert (GET_MODE_SIZE (GET_MODE (XVECEXP (span, 0, i))) + .to_constant () == result.span_width); + gcc_assert (REG_P (XVECEXP (span, 0, i))); + gcc_assert (dwf_regno (XVECEXP (span, 0, i)) == result.reg + i); + } + } + } + + return result; +} + /* Compare X and Y for equivalence. The inputs may be REGs or PC_RTX. */ static bool @@ -1086,7 +1212,8 @@ dwarf2out_flush_queued_reg_saves (void) FOR_EACH_VEC_ELT (queued_reg_saves, i, q) { - unsigned int reg, sreg; + unsigned int reg; + struct cfa_reg sreg; record_reg_saved_in_reg (q->saved_reg, q->reg); @@ -1095,9 +1222,9 @@ dwarf2out_flush_queued_reg_saves (void) else reg = dwf_regno (q->reg); if (q->saved_reg) - sreg = dwf_regno (q->saved_reg); + sreg = dwf_cfa_reg (q->saved_reg); else - sreg = INVALID_REGNUM; + sreg.set_by_dwreg (INVALID_REGNUM); reg_save (reg, sreg, q->cfa_offset); } @@ -1169,7 +1296,7 @@ dwarf2out_frame_debug_def_cfa (rtx pat) /* ??? If this fails, we could be calling into the _loc functions to define a full expression. So far no port does that. */ gcc_assert (REG_P (pat)); - cur_cfa->reg = dwf_regno (pat); + cur_cfa->reg = dwf_cfa_reg (pat); } /* A subroutine of dwarf2out_frame_debug, process a REG_ADJUST_CFA note. */ @@ -1186,7 +1313,7 @@ dwarf2out_frame_debug_adjust_cfa (rtx pat) switch (GET_CODE (src)) { case PLUS: - gcc_assert (dwf_regno (XEXP (src, 0)) == cur_cfa->reg); + gcc_assert (dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg); cur_cfa->offset -= rtx_to_poly_int64 (XEXP (src, 1)); break; @@ -1197,7 +1324,7 @@ dwarf2out_frame_debug_adjust_cfa (rtx pat) gcc_unreachable (); } - cur_cfa->reg = dwf_regno (dest); + cur_cfa->reg = dwf_cfa_reg (dest); gcc_assert (cur_cfa->indirect == 0); } @@ -1219,11 +1346,11 @@ dwarf2out_frame_debug_cfa_offset (rtx set) switch (GET_CODE (addr)) { case REG: - gcc_assert (dwf_regno (addr) == cur_cfa->reg); + gcc_assert (dwf_cfa_reg (addr) == cur_cfa->reg); offset = -cur_cfa->offset; break; case PLUS: - gcc_assert (dwf_regno (XEXP (addr, 0)) == cur_cfa->reg); + gcc_assert (dwf_cfa_reg (XEXP (addr, 0)) == cur_cfa->reg); offset = rtx_to_poly_int64 (XEXP (addr, 1)) - cur_cfa->offset; break; default: @@ -1243,8 +1370,10 @@ dwarf2out_frame_debug_cfa_offset (rtx set) /* ??? We'd like to use queue_reg_save, but we need to come up with a different flushing heuristic for epilogues. */ + struct cfa_reg invalid; + invalid.set_by_dwreg (INVALID_REGNUM); if (!span) - reg_save (sregno, INVALID_REGNUM, offset); + reg_save (sregno, invalid, offset); else { /* We have a PARALLEL describing where the contents of SRC live. @@ -1258,7 +1387,7 @@ dwarf2out_frame_debug_cfa_offset (rtx set) { rtx elem = XVECEXP (span, 0, par_index); sregno = dwf_regno (src); - reg_save (sregno, INVALID_REGNUM, span_offset); + reg_save (sregno, invalid, span_offset); span_offset += GET_MODE_SIZE (GET_MODE (elem)); } } @@ -1270,7 +1399,8 @@ static void dwarf2out_frame_debug_cfa_register (rtx set) { rtx src, dest; - unsigned sregno, dregno; + unsigned sregno; + struct cfa_reg dregno; src = XEXP (set, 1); dest = XEXP (set, 0); @@ -1281,7 +1411,7 @@ dwarf2out_frame_debug_cfa_register (rtx set) else sregno = dwf_regno (src); - dregno = dwf_regno (dest); + dregno = dwf_cfa_reg (dest); /* ??? We'd like to use queue_reg_save, but we need to come up with a different flushing heuristic for epilogues. */ @@ -1667,7 +1797,7 @@ dwarf2out_frame_debug_expr (rtx expr) { /* Setting FP from SP. */ case REG: - if (cur_cfa->reg == dwf_regno (src)) + if (cur_cfa->reg == dwf_cfa_reg (src)) { /* Rule 1 */ /* Update the CFA rule wrt SP or FP. Make sure src is @@ -1677,7 +1807,7 @@ dwarf2out_frame_debug_expr (rtx expr) ARM copies SP to a temporary register, and from there to FP. So we just rely on the backends to only set RTX_FRAME_RELATED_P on appropriate insns. */ - cur_cfa->reg = dwf_regno (dest); + cur_cfa->reg = dwf_cfa_reg (dest); cur_trace->cfa_temp.reg = cur_cfa->reg; cur_trace->cfa_temp.offset = cur_cfa->offset; } @@ -1698,7 +1828,7 @@ dwarf2out_frame_debug_expr (rtx expr) { gcc_assert (REGNO (dest) == HARD_FRAME_POINTER_REGNUM && fde->drap_reg != INVALID_REGNUM - && cur_cfa->reg != dwf_regno (src) + && cur_cfa->reg != dwf_cfa_reg (src) && fde->rule18); fde->rule18 = 0; /* The save of hard frame pointer has been deferred @@ -1722,7 +1852,7 @@ dwarf2out_frame_debug_expr (rtx expr) /* Adjusting SP. */ if (REG_P (XEXP (src, 1))) { - gcc_assert (dwf_regno (XEXP (src, 1)) + gcc_assert (dwf_cfa_reg (XEXP (src, 1)) == cur_trace->cfa_temp.reg); offset = cur_trace->cfa_temp.offset; } @@ -1756,7 +1886,7 @@ dwarf2out_frame_debug_expr (rtx expr) gcc_assert (frame_pointer_needed); gcc_assert (REG_P (XEXP (src, 0)) - && dwf_regno (XEXP (src, 0)) == cur_cfa->reg); + && dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg); offset = rtx_to_poly_int64 (XEXP (src, 1)); if (GET_CODE (src) != MINUS) offset = -offset; @@ -1769,14 +1899,14 @@ dwarf2out_frame_debug_expr (rtx expr) /* Rule 4 */ if (REG_P (XEXP (src, 0)) - && dwf_regno (XEXP (src, 0)) == cur_cfa->reg + && dwf_cfa_reg (XEXP (src, 0)) == cur_cfa->reg && poly_int_rtx_p (XEXP (src, 1), &offset)) { /* Setting a temporary CFA register that will be copied into the FP later on. */ offset = -offset; cur_cfa->offset += offset; - cur_cfa->reg = dwf_regno (dest); + cur_cfa->reg = dwf_cfa_reg (dest); /* Or used to save regs to the stack. */ cur_trace->cfa_temp.reg = cur_cfa->reg; cur_trace->cfa_temp.offset = cur_cfa->offset; @@ -1784,13 +1914,13 @@ dwarf2out_frame_debug_expr (rtx expr) /* Rule 5 */ else if (REG_P (XEXP (src, 0)) - && dwf_regno (XEXP (src, 0)) == cur_trace->cfa_temp.reg + && dwf_cfa_reg (XEXP (src, 0)) == cur_trace->cfa_temp.reg && XEXP (src, 1) == stack_pointer_rtx) { /* Setting a scratch register that we will use instead of SP for saving registers to the stack. */ gcc_assert (cur_cfa->reg == dw_stack_pointer_regnum); - cur_trace->cfa_store.reg = dwf_regno (dest); + cur_trace->cfa_store.reg = dwf_cfa_reg (dest); cur_trace->cfa_store.offset = cur_cfa->offset - cur_trace->cfa_temp.offset; } @@ -1799,7 +1929,7 @@ dwarf2out_frame_debug_expr (rtx expr) else if (GET_CODE (src) == LO_SUM && poly_int_rtx_p (XEXP (src, 1), &cur_trace->cfa_temp.offset)) - cur_trace->cfa_temp.reg = dwf_regno (dest); + cur_trace->cfa_temp.reg = dwf_cfa_reg (dest); else gcc_unreachable (); } @@ -1808,17 +1938,17 @@ dwarf2out_frame_debug_expr (rtx expr) /* Rule 6 */ case CONST_INT: case CONST_POLY_INT: - cur_trace->cfa_temp.reg = dwf_regno (dest); + cur_trace->cfa_temp.reg = dwf_cfa_reg (dest); cur_trace->cfa_temp.offset = rtx_to_poly_int64 (src); break; /* Rule 7 */ case IOR: gcc_assert (REG_P (XEXP (src, 0)) - && dwf_regno (XEXP (src, 0)) == cur_trace->cfa_temp.reg + && dwf_cfa_reg (XEXP (src, 0)) == cur_trace->cfa_temp.reg && CONST_INT_P (XEXP (src, 1))); - cur_trace->cfa_temp.reg = dwf_regno (dest); + cur_trace->cfa_temp.reg = dwf_cfa_reg (dest); if (!can_ior_p (cur_trace->cfa_temp.offset, INTVAL (XEXP (src, 1)), &cur_trace->cfa_temp.offset)) /* The target shouldn't generate this kind of CFI note if we @@ -1851,14 +1981,17 @@ dwarf2out_frame_debug_expr (rtx expr) dwarf2out_flush_queued_reg_saves (); gcc_assert (cur_trace->cfa_store.reg - == dwf_regno (XEXP (src, 0))); + == dwf_cfa_reg (XEXP (src, 0))); fde->stack_realign = 1; fde->stack_realignment = INTVAL (XEXP (src, 1)); cur_trace->cfa_store.offset = 0; if (cur_cfa->reg != dw_stack_pointer_regnum && cur_cfa->reg != dw_frame_pointer_regnum) - fde->drap_reg = cur_cfa->reg; + { + gcc_assert (cur_cfa->reg.span == 1); + fde->drap_reg = cur_cfa->reg.reg; + } } return; @@ -1935,14 +2068,14 @@ dwarf2out_frame_debug_expr (rtx expr) case MINUS: case LO_SUM: { - unsigned int regno; + struct cfa_reg regno; gcc_assert (REG_P (XEXP (XEXP (dest, 0), 0))); offset = rtx_to_poly_int64 (XEXP (XEXP (dest, 0), 1)); if (GET_CODE (XEXP (dest, 0)) == MINUS) offset = -offset; - regno = dwf_regno (XEXP (XEXP (dest, 0), 0)); + regno = dwf_cfa_reg (XEXP (XEXP (dest, 0), 0)); if (cur_cfa->reg == regno) offset -= cur_cfa->offset; @@ -1960,7 +2093,7 @@ dwarf2out_frame_debug_expr (rtx expr) /* Without an offset. */ case REG: { - unsigned int regno = dwf_regno (XEXP (dest, 0)); + struct cfa_reg regno = dwf_cfa_reg (XEXP (dest, 0)); if (cur_cfa->reg == regno) offset = -cur_cfa->offset; @@ -1977,7 +2110,7 @@ dwarf2out_frame_debug_expr (rtx expr) /* Rule 14 */ case POST_INC: gcc_assert (cur_trace->cfa_temp.reg - == dwf_regno (XEXP (XEXP (dest, 0), 0))); + == dwf_cfa_reg (XEXP (XEXP (dest, 0), 0))); offset = -cur_trace->cfa_temp.offset; cur_trace->cfa_temp.offset -= GET_MODE_SIZE (GET_MODE (dest)); break; @@ -1995,7 +2128,7 @@ dwarf2out_frame_debug_expr (rtx expr) if (REG_P (src) && REGNO (src) != STACK_POINTER_REGNUM && REGNO (src) != HARD_FRAME_POINTER_REGNUM - && dwf_regno (src) == cur_cfa->reg) + && dwf_cfa_reg (src) == cur_cfa->reg) { /* We're storing the current CFA reg into the stack. */ @@ -2012,7 +2145,7 @@ dwarf2out_frame_debug_expr (rtx expr) && cur_cfa->indirect == 0 && cur_cfa->reg != dw_frame_pointer_regnum) { - gcc_assert (fde->drap_reg == cur_cfa->reg); + gcc_assert (fde->drap_reg == cur_cfa->reg.reg); cur_cfa->indirect = 1; cur_cfa->reg = dw_frame_pointer_regnum; @@ -2039,7 +2172,7 @@ dwarf2out_frame_debug_expr (rtx expr) x = XEXP (x, 0); gcc_assert (REG_P (x)); - cur_cfa->reg = dwf_regno (x); + cur_cfa->reg = dwf_cfa_reg (x); cur_cfa->base_offset = offset; cur_cfa->indirect = 1; break; @@ -2951,7 +3084,7 @@ create_pseudo_cfg (void) ti.head = get_insns (); ti.beg_row = cie_cfi_row; ti.cfa_store = cie_cfi_row->cfa; - ti.cfa_temp.reg = INVALID_REGNUM; + ti.cfa_temp.reg.set_by_dwreg (INVALID_REGNUM); trace_info.quick_push (ti); if (cie_return_save) @@ -3014,14 +3147,15 @@ create_pseudo_cfg (void) static void initial_return_save (rtx rtl) { - unsigned int reg = INVALID_REGNUM; + struct cfa_reg reg; + reg.set_by_dwreg (INVALID_REGNUM); poly_int64 offset = 0; switch (GET_CODE (rtl)) { case REG: /* RA is in a register. */ - reg = dwf_regno (rtl); + reg = dwf_cfa_reg (rtl); break; case MEM: @@ -3062,9 +3196,9 @@ initial_return_save (rtx rtl) gcc_unreachable (); } - if (reg != DWARF_FRAME_RETURN_COLUMN) + if (reg.reg != DWARF_FRAME_RETURN_COLUMN) { - if (reg != INVALID_REGNUM) + if (reg.reg != INVALID_REGNUM) record_reg_saved_in_reg (rtl, pc_rtx); reg_save (DWARF_FRAME_RETURN_COLUMN, reg, offset - cur_row->cfa.offset); } @@ -3076,7 +3210,8 @@ create_cie_data (void) dw_cfa_location loc; dw_trace_info cie_trace; - dw_stack_pointer_regnum = DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM); + dw_stack_pointer_regnum = dwf_cfa_reg (gen_rtx_REG (Pmode, + STACK_POINTER_REGNUM)); memset (&cie_trace, 0, sizeof (cie_trace)); cur_trace = &cie_trace; @@ -3135,7 +3270,8 @@ static unsigned int execute_dwarf2_frame (void) { /* Different HARD_FRAME_POINTER_REGNUM might coexist in the same file. */ - dw_frame_pointer_regnum = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM); + dw_frame_pointer_regnum + = dwf_cfa_reg (gen_rtx_REG (Pmode, HARD_FRAME_POINTER_REGNUM)); /* The first time we're called, compute the incoming frame state. */ if (cie_cfi_vec == NULL) @@ -3515,7 +3651,7 @@ dump_cfi_row (FILE *f, dw_cfi_row *row) { dw_cfa_location dummy; memset (&dummy, 0, sizeof (dummy)); - dummy.reg = INVALID_REGNUM; + dummy.reg.set_by_dwreg (INVALID_REGNUM); cfi = def_cfa_0 (&dummy, &row->cfa); } output_cfi_directive (f, cfi); diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index e36ef56..d6513d3 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -2780,6 +2780,43 @@ output_loc_sequence_raw (dw_loc_descr_ref loc) } } +static void +build_breg_loc (struct dw_loc_descr_node **head, unsigned int regno) +{ + if (regno <= 31) + add_loc_descr (head, new_loc_descr ((enum dwarf_location_atom) + (DW_OP_breg0 + regno), 0, 0)); + else + add_loc_descr (head, new_loc_descr (DW_OP_bregx, regno, 0)); +} + +/* Build a dwarf location for a cfa_reg spanning multiple + consecutive registers. */ + +struct dw_loc_descr_node * +build_span_loc (struct cfa_reg reg) +{ + struct dw_loc_descr_node *head = NULL; + + gcc_assert (reg.span_width > 0); + gcc_assert (reg.span > 1); + + /* Start from the highest number register as it goes in the upper bits. */ + unsigned int regno = reg.reg + reg.span - 1; + build_breg_loc (&head, regno); + + /* Deal with the remaining registers in the span. */ + for (int i = reg.span - 2; i >= 0; i--) + { + add_loc_descr (&head, int_loc_descriptor (reg.span_width * 8)); + add_loc_descr (&head, new_loc_descr (DW_OP_shl, 0, 0)); + regno--; + build_breg_loc (&head, regno); + add_loc_descr (&head, new_loc_descr (DW_OP_plus, 0, 0)); + } + return head; +} + /* This function builds a dwarf location descriptor sequence from a dw_cfa_location, adding the given OFFSET to the result of the expression. */ @@ -2791,9 +2828,16 @@ build_cfa_loc (dw_cfa_location *cfa, poly_int64 offset) offset += cfa->offset; - if (cfa->indirect) + if (cfa->reg.span > 1) + { + head = build_span_loc (cfa->reg); + + if (maybe_ne (offset, 0)) + loc_descr_plus_const (&head, offset); + } + else if (cfa->indirect) { - head = new_reg_loc_descr (cfa->reg, cfa->base_offset); + head = new_reg_loc_descr (cfa->reg.reg, cfa->base_offset); head->dw_loc_oprnd1.val_class = dw_val_class_const; head->dw_loc_oprnd1.val_entry = NULL; tmp = new_loc_descr (DW_OP_deref, 0, 0); @@ -2801,7 +2845,7 @@ build_cfa_loc (dw_cfa_location *cfa, poly_int64 offset) loc_descr_plus_const (&head, offset); } else - head = new_reg_loc_descr (cfa->reg, offset); + head = new_reg_loc_descr (cfa->reg.reg, offset); return head; } @@ -2819,7 +2863,7 @@ build_cfa_aligned_loc (dw_cfa_location *cfa, = DWARF_FRAME_REGNUM (HARD_FRAME_POINTER_REGNUM); /* When CFA is defined as FP+OFFSET, emulate stack alignment. */ - if (cfa->reg == HARD_FRAME_POINTER_REGNUM && cfa->indirect == 0) + if (cfa->reg.reg == HARD_FRAME_POINTER_REGNUM && cfa->indirect == 0) { head = new_reg_loc_descr (dwarf_fp, 0); add_loc_descr (&head, int_loc_descriptor (alignment)); @@ -20882,7 +20926,7 @@ convert_cfa_to_fb_loc_list (HOST_WIDE_INT offset) list = NULL; memset (&next_cfa, 0, sizeof (next_cfa)); - next_cfa.reg = INVALID_REGNUM; + next_cfa.reg.set_by_dwreg (INVALID_REGNUM); remember = next_cfa; start_label = fde->dw_fde_begin; diff --git a/gcc/dwarf2out.h b/gcc/dwarf2out.h index 312a990..f009834 100644 --- a/gcc/dwarf2out.h +++ b/gcc/dwarf2out.h @@ -119,6 +119,39 @@ struct GTY(()) dw_fde_node { }; +/* This represents a register, in DWARF_FRAME_REGNUM space, for use in CFA + definitions and expressions. + Most architectures only need a single register number, but some (amdgcn) + have pointers that span multiple registers. DWARF permits arbitrary + register sets but existing use-cases only require contiguous register + sets, as represented here. */ +struct GTY(()) cfa_reg { + unsigned int reg; + unsigned short span; + unsigned short span_width; /* A.K.A. register mode size. */ + + cfa_reg& set_by_dwreg (unsigned int r) + { + reg = r; + span = 1; + span_width = 0; /* Unknown size (permitted when span == 1). */ + return *this; + } + + bool operator== (const cfa_reg &other) const + { + return (reg == other.reg && span == other.span + && (span_width == other.span_width + || (span == 1 + && (span_width == 0 || other.span_width == 0)))); + } + + bool operator!= (const cfa_reg &other) const + { + return !(*this == other); + } +}; + /* This is how we define the location of the CFA. We use to handle it as REG + OFFSET all the time, but now it can be more complex. It can now be either REG + CFA_OFFSET or *(REG + BASE_OFFSET) + CFA_OFFSET. @@ -128,7 +161,7 @@ struct GTY(()) dw_cfa_location { poly_int64_pod offset; poly_int64_pod base_offset; /* REG is in DWARF_FRAME_REGNUM space, *not* normal REGNO space. */ - unsigned int reg; + struct cfa_reg reg; BOOL_BITFIELD indirect : 1; /* 1 if CFA is accessed via a dereference. */ BOOL_BITFIELD in_use : 1; /* 1 if a saved cfa is stored here. */ }; @@ -285,6 +318,7 @@ extern struct dw_loc_descr_node *build_cfa_loc (dw_cfa_location *, poly_int64); extern struct dw_loc_descr_node *build_cfa_aligned_loc (dw_cfa_location *, poly_int64, HOST_WIDE_INT); +extern struct dw_loc_descr_node *build_span_loc (struct cfa_reg); extern struct dw_loc_descr_node *mem_loc_descriptor (rtx, machine_mode mode, machine_mode mem_mode, enum var_init_status); -- 2.7.4