[PATCH 0/2] Add SCFI support for aarch64

Indu Bhagat indu.bhagat@oracle.com
Mon Jul 1 01:03:16 GMT 2024


On 6/27/24 02:40, Richard Sandiford wrote:
> Indu Bhagat <indu.bhagat@oracle.com> writes:
>> On 6/26/24 04:01, Richard Sandiford wrote:
>>> D8-D15 are "interesting" because they are the low 64 bits of Q8-Q15,
>>> and of Z8-Z15 if SVE is used.  However, a CFI save slot always represents
>>> the low 64 bits, regardless of whether a save occurs on D, Q or Z registers.
>>> This matters for big-endian code, because there are two additional
>>> PCS variants:
>>>
>>> * the "vector PCS", which preserves Q8-Q23
>>> * the "SVE PCS", which preserves Z8-Z23 and P3-P15
>>>
>>
>> Is there a way to annotate that a (hand-written asm) function adheres to
>> vectors PCS or SVE PCS ?  I see that there is a .variant_pcs but that
>> does not help differentiate between the above two?
>>
>> I _think_ gas will need to know which of SVE vs vector PCS is in effect
>> for a specific function so that the P3-P15 can be added to the set of
>> callee-saved registers being tracked for SCFI for SVE PCS but not for
>> vector PCS.
> 
> Only the normal base AAPCS64 register set is preserved across abnormal
> control flow (setjmp/longjmp, exceptions, etc.)  The extra call-preserved
> guarantees for vector and SVE PCS functions only apply to normal returns.
> 
> [This means, for example, that:
> 
>    void foo();
>    svbool_t f() {
>      try {
>        foo();
>      } catch (...) {};
>      return svptrue_b8();
>    }
> 
> must manually restore the additional register state when catching
> and returning normally.]
> 
> The CFI requirements therefore don't change: only D8-D15 matter,
> like for normal functions.  But that's also where the big-endian
> complications that I mentioned come from.
> 
> So I don't think the code needs to know which kind of function is
> being assembled.  The code just needs to be able to recognise Q-based
> and Z-based loads and stores of D8-D15 and work out the correct offset
> of the low 64 bits.  (Although, like I say, I think we can punt on
> big-endian SVE PCS functions.)
> 
>>> So vector PCS functions might need to save and restore Q8 when returning
>>> normally, but the CFI only describes the save of the D8 portion (since
>>> that's the only portion that is preserved by exceptions).  This means
>>> that, on big-endian:
>>>
>>> 	str	q8, [sp, #16]
>>>
>>> should record D8 as being saved at sp+24 rather than sp+16.
>>>
>>> A further complication is that STR Qn and STR Zn do not store in
>>> the same byte order for big-endian: STR Qn stores as a 128-bit
>>> integer (MSB first), whereas STR Zn stores as a stream of bytes
>>> (LSB first).  This means that GCC-generated big-endian SVE PCS
>>> functions use things like:
>>>
>>> 	st1d	z8.d, p2, [sp, #1, mul vl]
>>>
>>> with the D8 save slot then being at sp + 2*VL - 64.
>>>
>>> I think it's OK to punt on the big-endian SVE PCS case for now (provided
>>> that there's a warning that the code isn't understood, which it looks
>>> like there is).  But I think it's worth handling the Q register saves.
>>
>> It looks to me that using reg name / size is an unambiguous proxy to
>> deciding  whether SVE PCS is in effect. Is this correct ?
> 
> Not necessarily.  There's nothing stopping code from using Q-based
> loads and stores for normal functions (although it would be an
> odd choice).

Of course, I dont know what I was thinking when I wrote that.

As for Z registers, I realized that I need more time to take a look at 
the SVE insns and see what patterns need to be handled etc for memory 
offset calculation.

For V4 (to be posted soon), I have added handling for D and Q registers 
(little-endian and big-endian), but skipped Z altogether for now (SCFI 
errors out when correctness is affected).  Also added this to the set of 
known limitations to be addressed in a future patch.

>  
> There's also the possiblity of ad-hoc PCSes, but the assumption there
> too would be that only the base AAPCS64 set needs to be preserved
> through unwinding.
> 
>>> Other comments:
>>>
>>> - I like the new approach of using a combination of the iclass and a
>>>     "subclass" field of the flags.  How about making aarch64-gen.c enforce
>>>     that:
>>>
>>>     - if aarch64-ginsn.c looks at the subclass of a particular iclass,
>>>       every instruction of that iclass has a nonzero subclass field
>>>
>>
>> (Let me refer to the above as #1). I can see that there can be ways to
>> achieve this...
>>
>>>     - every other instruction has a zero subclass field
>>>
>>
>> ..but I am not sure I follow this statement. (Let me refer to the above
>> as #2).
>>
>>>     This would help to ensure that the data stays up to date.
>>>     The subclass enum could include a nonzero "other" value where
>>>     necessary.
>>>
>>
>> Currently, we are using the opcode->flags bits to encode:
>>
>> In include/opcode/aarch64.h:
>>
>> /* 4-bit flag field to indicate subclass of operations.
>>      Note that there is an (intended) overlap between the three flag sets
>>      (F_LDST*, F_ARITH* and F_BRANCH*).  This allows space savings.  */
>> #define F_LDST_LOAD (1ULL << 36)
>> #define F_LDST_STORE (2ULL << 36)
>> /* A load followed by a store (using the same address). */
>> #define F_LDST_SWAP (F_LDST_LOAD | F_LDST_STORE)
>> /* Subclasses to denote add, sub and mov insns.  */
>> #define F_ARITH_ADD (1ULL << 36)
>> #define F_ARITH_SUB (2ULL << 36)
>> #define F_ARITH_MOV (4ULL << 36)
>> /* Subclasses to denote call and ret insns.  */
>> #define F_BRANCH_CALL (1ULL << 36)
>> #define F_BRANCH_RET (2ULL << 36)
>>
>> We can dedicate F_SUBCLASS_NONE (8ULL << 36) and enforce this subclass
>> on all insns which use none of the above subclasses in a specific
>> iclass.  This can help address (#1), but not sure about (#2).
> 
> I think the 4 bits are really an enum rather than true independent flags.
> So it might be better to use 15ULL, so that the other 14 nonzero values are
> consecutive.
> 
> But yeah, I think it addresses both #1 and #2.  #2 makes sure that a
> subclass is only present when we expect one.  If we define:
> 
> #define F_SUBCLASS (15ULL << 36)
> 
> then #2 makes sure that (flags & F_SUBCLASS) == 0 for classes that
> are not interpreted by ginsns.
> 

Thanks.  This should be addressed in the V4 series which I will be 
posting soon.

Thanks
Indu



More information about the Binutils mailing list