[LLVMdev] [RFC] New StackMap format proposal (StackMap v2)

Philip Reames listmail at philipreames.com
Fri Jul 17 14:28:53 PDT 2015



On 07/17/2015 01:16 PM, Juergen Ributzka wrote:
>
>> On Jul 14, 2015, at 11:02 PM, Philip Reames 
>> <listmail at philipreames.com <mailto:listmail at philipreames.com>> wrote:
>>
>> Comments inline.  I tried to reply to particular points of discussion 
>> downthread, so this is probably best read last.
>>
>> On 07/09/2015 02:04 PM, Juergen Ributzka wrote:
>>> Hi @ll,
>>>
>>> over the past year we gained more experience with the 
>>> patchpoint/stackmap/statepoint intrinsics and it exposed limitations 
>>> in the stackmap format.
>>> The following proposal includes feedback and request from several 
>>> interested parties and I would like to hear your feedback.
>>>
>>> Missing correlation between functions and stackmap records:
>>> Originally the client had to keep track of the ID to know which 
>>> stackmap record belongs to which function, but this would stop 
>>> working once functions are inlined.
>>> The new format fixes that by adding a direct reference from the 
>>> function to the stackmap records.
>> +1
>>>
>>> Call Size and Function Size:
>>> These are additional information that are of interest and have been 
>>> added to the format.
>>> @Swaroop: Could you please provide a little more detailed 
>>> explanation on the "Call Size" field and what exactly is there 
>>> recorded. Is it just the call instruction
>>> or also the materialization code for the address? For what is this 
>>> used for?
>> +1 subject to details being settled
>>>
>>> Flat format:
>>> We think moving to a flat form will make parsing easier, because 
>>> every record has a fixed size and offsets can be calculated easily.
>> I'm still not convinced this is actually a good idea, but I have no 
>> strong objection either.
>>> The plan is to also
>>> provide parsers for both stackmap versions (there is already one for 
>>> the first format in tree) and a corresponding C-API to make it 
>>> easier for clients to
>>> adopt the new format. There is no plan to drop the original format 
>>> and we will continue to support both formats. I will ask for 
>>> feedback on the C API in a
>>> separate RFC.
>> I strongly feel that trying to support multiple versions of the file 
>> format in tree is a bad idea.  It's a distraction, support burden, 
>> and provides extremely little value.  What's the value in supporting 
>> a parser for a format that ToT will no longer be able to emit?  
>> Anyone who is actually consuming the old format would have to be 
>> using the old version in process anyways.
>
> I don’t think supporting multiple version will be that hard and I 
> don’t plan to keep all current and future version in tree forever. But 
> we should provide a reasonable long transition period that allows 
> clients to transition from one version to another - basically one 
> release cycle.
I would be perfectly okay with this proposal.  I just want there to be a 
documented point at which we can remove complexity.  Would you mind 
posting a patch for the StackMaps docs which mentions the existence of 
the parser and explicitly states this policy?  Having it documented will 
cut down on future confusion.
>
>>
>>
>> (Possible exception: Is this intended to simplify the object caching 
>> model?  If so, have we ever given any guarantees about object caching 
>> in the JIT across versions?  That seems risky at best..)
>>
>> I'm also opposed to the complexity of C API, but I'll defer that 
>> until the separate RFC is actually posted.
>>>
>>> Another benefit we hope to achieve from this format is to optimize 
>>> for size by uniquing entries - but that is independent optimization 
>>> and not required.
>>>
>>> More detailed frame record:
>>> Clients require more information about the function frame, such as 
>>> spilled registers, etc. The frame base register i.e. might change 
>>> when dynamic stack
>>> realignment is performed on X86.
>>>
>>>
>>> If there is anything missing please let me know.
>>>
>>> Thanks
>>>
>>> Cheers,
>>> Juergen
>>>
>>>
>> Is the formatLittle endian, bit endian? native endian?
>
> I would say whatever the object file format is.
That's perfectly workable.  Can you make sure that gets into the docs?
>
>>> Header v2 {
>>> uint8  : Stack Map Version (2)
>>> uint8  : Reserved [3] (0)
>>> uint32 : Constants Offset (bytes)
>>> uint32 : Frame Records Offset (bytes)
>>> uint32 : Frame Registers Offset (bytes)
>>> uint32 : StackMap Records Offset (bytes)
>>> uint32 : Locations Offset (bytes)
>>> uint32 : LiveOuts Offset (bytes)
>> Let's reserve at least two uint32s for future field groups so that we 
>> can extend in a backwards compatible way.  Another option would be to 
>> steal one of the reserved bytes and require parsers to ignore 
>> sections they don't understand.
>>
>> Actually, so long as we document that there might be unknown bytes 
>> between Header.end and Constants.begin we should be fine.  It just 
>> needs to be *clearly* documented.
>
> That was the idea. That way we could add new sections and old parsers 
> would still work. They would just ignore those sections. Although with 
> the addition of official parser in trees I am not sure how important 
> this is anymore.
>
>>
>> As an example, if we need to add the SymbolConstants section 
>> mentioned down thread, that shouldn't require an version change.
>
> Once we release a LLVM version any change should require a version change.
Unless existing systems will continue to work without needing the new 
optional information, yes.  Thinking about it, my particular example 
doesn't actually meet this requirement.  :)  But adding a purely 
informational section without a version change would be fine.
>
>>
>>> }
>>>
>>> align to 8 bytes
>>> Constants[] {
>>> uint64 : LargeConstant
>>> }
>>>
>>> align to 8 bytes
>>> FrameRecord[] {
>>> uint64 : Function Address
>>> uint32 : Function Size
>>> uint32 : Stack Size
>>> uint16 : Flags {
>>>     bool : HasFrame
>> What does this mean?
>
> If the function has a frame pointer.
So a name like hasFramePointerRegister might be more clear?  Given that 
different registers can be used as frame pointers (i.e. not just RBP), 
maybe this should be a Dwarf register number instead?
>
>>
>>> bool : HasVariableSizeAlloca
>>> bool : HasStackRealignment
>>> bool : HasLiveOutInfo
>>> bool : Reserved [12]
>>
>>> }
>>> uint16 : Frame Base Register Dwarf RegNum
>> I don't think this is actually sufficient for dynamic stack 
>> realignment. Don't you end up with some things indexed off RSP and 
>> others indexed off RBP?  Actually, wait.. that's entirely handled 
>> within the record format.  What does tracking the Frame Base Register 
>> give us?  I think I'm missing a use case here.
>>> uint16 : Num Frame Registers
>>> uint16 : Frame Register Index
>> Are these (begin, begin+length) offsets into the Frame Registers 
>> table?  If so, should we swap them?
>
> Sure
>
>>> uint16 : Num StackMap Records
>>> uint16 : StackMap Record Index
>>> }
>>>
>>> align to 4 bytes
>>> FrameRegister[] {
>>> uint16 : Dwarf RegNum
>>> int16  : Offset
>>> uint8  : Size in Bytes
>>> uint8  : Flags {
>>>     bool : IsSpilled
>>> bool : Reserved [7]
>>> }
>>> }
>> This seems tied specifically to the assumption that registers are 
>> spilled on entry.  Given the recent work towards shrink wrapping, is 
>> that something we want to do?
>>
>> p.s. What's with the IsSpilled bit?  Why would you have a record for 
>> an unspilled register?
>
> This was done before shrink wrapping and pristine register support. 
> The original problem was that the live-out set didn’t contain pristine 
> registers and we have to add all callee-saved register to the live-out 
> set too to make sure we didn’t miss anything. We could do better by 
> recording which callee-save registers actually got spilled and which not.
Makes sense, but isn't this still per call site information, not per 
function?  The format seems to imply per-function.
>
>>>
>>> align to 8 bytes
>>> StackMapRecord[] {
>>> uint64 : PatchPoint ID
>> "ID" (remove the patchpoint since statepoint now has one too)
> sure
>
>>> uint32 : Instruction Offset
>>> uint8  : Call size (bytes)
>> "Patchable size" per down thread discussion
>>> uint8  : Flags {
>>>     bool : HasLiveOutInfo
>>> bool : Reserved [7]
>>> }
>> Why do we need these flags?  Isn't this Num LiveOuts == 0?
>
> A verification that the information was actually computed - we might 
> have 0 live-outs too. We could also change this in the future to 
> attach this information only to call-sites that have an attribute that 
> request this information.
Ok.
>
>>> uint16 : Num Locations
>>> uint16 : Location Index
>>> uint16 : Num LiveOuts
>>> uint16 : LiveOut Index
>>> }
>>>
>>> align to 4 bytes
>>> Location[] {
>>> uint8  : Register | Direct | Indirect | Constant | ConstantIndex
>>> uint8  : Reserved (location flags)
>>> uint16 : Dwarf RegNum
>>> int32  : Offset or SmallConstant
>>> }
>>>
>>> align to 2 bytes
>>> LiveOuts[] {
>>> uint16 : Dwarf RegNum
>>> uint8  : Reserved
>>> uint8  : Size in Bytes
>>> }
>>>
>>
>>
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20150717/e66bf2ec/attachment.html>


More information about the llvm-dev mailing list