-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JIT: Add a uniform representation for parameter ABI information #100138
Changes from 1 commit
1a5d1f7
af61b2c
9cfbbf5
bb247be
aedbcd8
a5e24a0
8adac9e
7cc33d8
cda86e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
This adds a uniform representation that can represent the ABI information for all of our targets without needing to fall back to handling ABI specific details in all places that need to handle calling conventions. Currently nothing is using this information. I want to incrementally migrate our ABI handling to use this representation. Also, there are several potential future improvements: - Split out ABI classification per ABI instead of keeping them all within the same function - Unify `InitVarDscInfo::stackArgSize` and `InitVarDscInfo::argSize`. I am unsure why the latter is needed - Remove `LclVarDsc::GetArgReg()`, `LclVarDscInfo::GetOtherArgReg()`, HFA related members - Reuse the representation in `CallArgABIInformation` and unify the classifiers The end goal here is rewriting `genFnPrologCalleeRegArgs` to handle float and integer registers simultaneously, and to support some of the registers that the Swift calling convention is using.
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
#include "jitpch.h" | ||
#include "abi.h" | ||
|
||
//----------------------------------------------------------------------------- | ||
// IsPassedInRegister: | ||
// Check if this segment is passed in a register. | ||
// | ||
// Return Value: | ||
// True if this is passed in a register. | ||
// | ||
bool AbiPassingSegment::IsPassedInRegister() const | ||
{ | ||
return m_register != REG_NA; | ||
} | ||
|
||
//----------------------------------------------------------------------------- | ||
// IsPassedOnStack: | ||
// Check if this segment is passed on the stack. | ||
// | ||
// Return Value: | ||
// True if this is passed on the stack. | ||
// | ||
bool AbiPassingSegment::IsPassedOnStack() const | ||
{ | ||
return m_register == REG_NA; | ||
} | ||
|
||
//----------------------------------------------------------------------------- | ||
// GetRegister: | ||
// Get the register that this segment is passed in. | ||
// | ||
// Return Value: | ||
// The register. | ||
// | ||
regNumber AbiPassingSegment::GetRegister() const | ||
{ | ||
assert(IsPassedInRegister()); | ||
return m_register; | ||
} | ||
|
||
//----------------------------------------------------------------------------- | ||
// GetStackOffset: | ||
// Get the stack offset where this segment is passed. | ||
// | ||
// Return Value: | ||
// Offset relative to the first stack argument. | ||
// | ||
unsigned AbiPassingSegment::GetStackOffset() const | ||
{ | ||
assert(IsPassedOnStack()); | ||
return m_stackOffset; | ||
} | ||
|
||
//----------------------------------------------------------------------------- | ||
// InRegister: | ||
// Create an AbiPassingSegment representing that a segment is passed in a | ||
// register. | ||
// | ||
// Parameters: | ||
// reg - The register the segment is passed in | ||
// offset - The offset of the segment that is passed in the register | ||
// size - The size of the segment passed in the register | ||
// | ||
// Return Value: | ||
// New instance of AbiPassingSegment. | ||
// | ||
AbiPassingSegment AbiPassingSegment::InRegister(regNumber reg, unsigned offset, unsigned size) | ||
{ | ||
assert(reg != REG_NA); | ||
AbiPassingSegment segment; | ||
segment.m_register = reg; | ||
segment.m_stackOffset = 0; | ||
segment.Offset = offset; | ||
segment.Size = size; | ||
return segment; | ||
} | ||
|
||
//----------------------------------------------------------------------------- | ||
// OnStack: | ||
// Create an AbiPassingSegment representing that a segment is passed on the | ||
// stack. | ||
// | ||
// Parameters: | ||
// stackOffset - Offset relative to the first stack parameter/argument | ||
// offset - The offset of the segment that is passed in the register | ||
// size - The size of the segment passed in the register | ||
// | ||
// Return Value: | ||
// New instance of AbiPassingSegment. | ||
// | ||
AbiPassingSegment AbiPassingSegment::OnStack(unsigned stackOffset, unsigned offset, unsigned size) | ||
{ | ||
AbiPassingSegment segment; | ||
segment.m_register = REG_NA; | ||
segment.m_stackOffset = stackOffset; | ||
segment.Offset = offset; | ||
segment.Size = size; | ||
return segment; | ||
} | ||
|
||
//----------------------------------------------------------------------------- | ||
// IsSplitAcrossRegistersAndStack: | ||
// Check if this AbiPassingInformation represents passing a value in both | ||
// registers and on stack. | ||
// | ||
// Return Value: | ||
// True if the value is passed in both registers and on stack. | ||
// | ||
bool AbiPassingInformation::IsSplitAcrossRegistersAndStack() const | ||
{ | ||
bool anyReg = false; | ||
bool anyStack = false; | ||
for (unsigned i = 0; i < NumSegments; i++) | ||
{ | ||
anyReg |= Segments[i].IsPassedInRegister(); | ||
anyStack |= Segments[i].IsPassedOnStack(); | ||
} | ||
return anyReg && anyStack; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
#pragma once | ||
|
||
class AbiPassingSegment | ||
{ | ||
regNumber m_register = REG_NA; | ||
unsigned m_stackOffset = 0; | ||
|
||
public: | ||
bool IsPassedInRegister() const; | ||
bool IsPassedOnStack() const; | ||
|
||
// Start offset of the segment within the parameter/argument. For example, a struct like { int32_t x; uint64_t y } | ||
// may have two segments | ||
// 1. Register(Offset=0, Type=TYP_INT, Size=4, Register=REG_ESI) | ||
// 2. Register(Offset=8, Type=TYP_LONG, Size=8, Register=REG_EDI) | ||
// on some ABIs, where the size of the first segment is not sufficient to | ||
// compute the offset of the second. | ||
unsigned Offset = 0; | ||
// Size of the segment being passed. | ||
unsigned Size = 0; | ||
|
||
// If this segment is passed in a register, return the particular register. | ||
regNumber GetRegister() const; | ||
|
||
// If this segment is passed on the stack then return the particular stack | ||
// offset, relative to the first stack argument's offset. | ||
unsigned GetStackOffset() const; | ||
|
||
static AbiPassingSegment InRegister(regNumber reg, unsigned offset, unsigned size); | ||
static AbiPassingSegment OnStack(unsigned stackOffset, unsigned offset, unsigned size); | ||
}; | ||
|
||
struct AbiPassingInformation | ||
{ | ||
// The number of segments used to pass the value. Examples: | ||
// - On x86, TYP_LONG can be passed in two registers, resulting in two | ||
// register segments | ||
// - On SysV x64, structs can be passed in two registers, resulting in two | ||
// register segments | ||
// - On arm64/arm32, HFAs can be passed in up to four registers, giving | ||
// four register segments | ||
Comment on lines
+43
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should also include HVAs, which looks like we might be missing handling around: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are just examples to indicate examples of the representation. The intention of this PR is not to make any functional changes. |
||
// - On arm32, structs can be split out over register and stack, giving | ||
// multiple register segments and a struct segment. | ||
// - On Windows x64, all parameters always belong into one stack slot or register, | ||
// and thus always have NumSegments == 1 | ||
unsigned NumSegments = 0; | ||
AbiPassingSegment* Segments = nullptr; | ||
|
||
bool IsSplitAcrossRegistersAndStack() const; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it relevant to track the other common qualifications like
HFA
andHVA
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so. HFA's/HVA's are just passed in more registers than other struct arguments, so hopefully by the end of this clean up that's a detail that's constrained fully to be within the ABI classification and not leaked out anywhere to the rest of the JIT.