[PATCH 4/6] gas: scfi: allow scratch reg state to track values as well

Indu Bhagat indu.bhagat@oracle.com
Thu Aug 8 15:38:51 GMT 2024


To accommodate the 'split sp update' code pattern in aarch64, the SCFI
machinery needs to simulate _some_ data flow.  Update the scratch reg
state management logic to include tracking of both:
  - register location (updated when a mov reg1, reg2 is seen)
  - register value (updated when a mov reg1, imm is seen)

The SCFI machinery will continue to memorize location/value of REG_SP
and REG_FP only at this time.

This patch by itself is preparatory in nature, and does not make any
changes in the current SCFI workflow.

gas/
        * scfi.c (enum reg_scratch_state): New enum.
        (struct reg_scratch): New definition.
	(struct scfi_state): Update data type of scratch to reg_scratchS
	instead of the earlier cfi_reglocS.
        (scfi_state_update_scratch_reg): New definition.
        (scfi_state_scratch_reg_loc_p): Likewise.
        (scfi_state_scratch_reg_get_loc): Likewise.
        (verify_heuristic_traceable_stack_manipulation): Use the new
	function.
        (gen_scfi_ops): Likewise.
---
 gas/scfi.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 111 insertions(+), 8 deletions(-)

diff --git a/gas/scfi.c b/gas/scfi.c
index 6c59a8eeabc..d9a5d59587d 100644
--- a/gas/scfi.c
+++ b/gas/scfi.c
@@ -62,6 +62,51 @@ struct cfi_regloc
 
 typedef struct cfi_regloc cfi_reglocS;
 
+/* Register scratch state enumerators.  */
+
+enum reg_scratch_state
+{
+  /* Register not available in scratch state.  */
+  REG_SCRATCH_UNKNOWN,
+  /* Register location known in scratch state.  */
+  REG_SCRATCH_LOC,
+  /* Rgister value known in scratch state.  */
+  REG_SCRATCH_VALUE
+};
+
+/* Register scratch state.
+
+   This is used to simulate _some_ data flow between ginsns.  Typically used by
+   the SCFI machinery to memorize information when following code patterns are
+   seen:
+
+	mov REG_GP (dst), REG_SP (src)
+	mov REG_SP (dst), REG_GP (src)
+
+	or
+
+	mov REG (dst), IMM
+	add REG_SP (dst), REG, IMM
+
+	etc.
+
+   Register location or value may be saved.  */
+
+struct reg_scratch
+{
+  union
+    {
+      /* Location information.  */
+      cfi_reglocS loc;
+      /* Value data.  */
+      offsetT value;
+    } r;
+  /* Whether location or value is saved.  */
+  enum reg_scratch_state state;
+};
+
+typedef struct reg_scratch reg_scratchS;
+
 struct scfi_op_data
 {
   const char *name;
@@ -105,7 +150,7 @@ struct scfi_op
 struct scfi_state
 {
   cfi_reglocS regs[MAX_NUM_SCFI_REGS];
-  cfi_reglocS scratch[MAX_NUM_SCFI_REGS];
+  reg_scratchS scratch[MAX_NUM_SCFI_REGS];
   /* Current stack size.  */
   offsetT stack_size;
   /* Whether the stack size is known.
@@ -115,6 +160,65 @@ struct scfi_state
   bool traceable_p;
 };
 
+static void
+scfi_state_update_scratch_reg (scfi_stateS *state, unsigned int reg,
+			       offsetT value, bool loc_p)
+{
+  if (reg >= MAX_NUM_SCFI_REGS)
+    return;
+
+  if (loc_p)
+    {
+      state->scratch[reg].state = REG_SCRATCH_LOC;
+      state->scratch[reg].r.loc.base = REG_CFA;
+      state->scratch[reg].r.loc.offset = value;
+      state->scratch[reg].r.loc.state = CFI_IN_REG;
+    }
+  else
+    {
+      state->scratch[reg].state = REG_SCRATCH_VALUE;
+      state->scratch[reg].r.value = value;
+    }
+}
+
+/* Whether REG has previously been saved in the scratch reg state due to
+   a mov reg, reg insn.  */
+
+static bool
+scfi_state_scratch_reg_loc_p (scfi_stateS *state, unsigned int reg)
+{
+  bool loc_p = false;
+
+  if (reg >= MAX_NUM_SCFI_REGS)
+    return false;
+
+  loc_p = (state->scratch[reg].state == REG_SCRATCH_LOC
+	   && state->scratch[reg].r.loc.state == CFI_IN_REG);
+
+  return loc_p;
+}
+
+/* Return the location offset (stack offset from CFA, ATM) of REG.
+   IMP: Caller _must_ ensure this is done when scfi_state_scratch_reg_loc_p ()
+   is true for this to be the intended value.  */
+
+static offsetT
+scfi_state_scratch_reg_get_loc (scfi_stateS *state, unsigned int reg)
+{
+  offsetT value = 0;
+
+  if (scfi_state_scratch_reg_loc_p (state, reg))
+    {
+      /* Just a sanity check.  Other possibilities are not implemented yet, as
+	 they are unnecessary for SCFI, ATM.  */
+      gas_assert (state->scratch[reg].r.loc.base == REG_CFA);
+
+      value = state->scratch[reg].r.loc.offset;
+    }
+
+  return value;
+}
+
 /* Initialize a new SCFI op.  */
 
 static scfi_opS *
@@ -597,8 +701,7 @@ verify_heuristic_traceable_stack_manipulation (ginsnS *ginsn,
     {
       /* A previous mov %rsp, %reg must have been seen earlier for this to be
 	 an OK for stack manipulation.  */
-      if (state->scratch[src1_reg].base != REG_CFA
-	  || state->scratch[src1_reg].state != CFI_IN_REG)
+      if (!scfi_state_scratch_reg_loc_p (state, src1_reg))
 	possibly_untraceable = true;
     }
   /* Check add/sub/and insn usage when CFA base register is REG_SP.
@@ -809,18 +912,18 @@ gen_scfi_ops (ginsnS *ginsn, scfi_stateS *state)
 		 IMP: The workflow in gen_scfi_ops must keep it updated.
 		 PS: Not taking the value from state->scratch[REG_SP] is
 		 intentional.  */
-	      state->scratch[dst_reg].base = REG_CFA;
-	      state->scratch[dst_reg].offset = -state->stack_size;
-	      state->scratch[dst_reg].state = CFI_IN_REG;
+	      scfi_state_update_scratch_reg (state, dst_reg,
+					     -state->stack_size, true);
 	    }
 	  else if (src1_type == GINSN_SRC_REG
 		   && dst_type == GINSN_DST_REG && dst_reg == REG_SP)
 	    {
 	      /* mov %reg, %rsp.  */
 	      /* Keep the value of REG_SP updated.  */
-	      if (state->scratch[src1_reg].state == CFI_IN_REG)
+	      if (scfi_state_scratch_reg_loc_p (state, src1_reg))
 		{
-		  state->stack_size = -state->scratch[src1_reg].offset;
+		  offset = scfi_state_scratch_reg_get_loc (state, src1_reg);
+		  state->stack_size = -offset;
 		  state->traceable_p = true;
 		}
 # if 0
-- 
2.43.0



More information about the Binutils mailing list