summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorRusty Wagner <rusty.wagner@gmail.com>2026-02-10 20:09:41 -0500
committerRusty Wagner <rusty.wagner@gmail.com>2026-05-22 16:30:57 -0400
commitf905a0133585fc7385c6e8ef0270bc6e0704cc2a (patch)
tree84b94ad4e5b211d15b76265080ec5879bcea8ff4 /arch
parent217b4a3361cbf62020658f12efb47b8fabb6a24d (diff)
Implement return value location handling for x86/x86_64 ELF ABI
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/arch_x86.cpp842
1 files changed, 841 insertions, 1 deletions
diff --git a/arch/x86/arch_x86.cpp b/arch/x86/arch_x86.cpp
index 98454dd9..bdd360bc 100644
--- a/arch/x86/arch_x86.cpp
+++ b/arch/x86/arch_x86.cpp
@@ -3847,6 +3847,52 @@ public:
};
+class X86SystemVCallingConvention: public X86BaseCallingConvention
+{
+public:
+ X86SystemVCallingConvention(Architecture* arch): X86BaseCallingConvention(arch, "sysv")
+ {
+ }
+
+ bool IsReturnTypeRegisterCompatible(BinaryView*, Type* type) override
+ {
+ if (!type)
+ return false;
+ if (type->IsFloat())
+ return true;
+ if (type->IsStructure() || type->IsArray())
+ return false;
+
+ // If we have an unresolved NTR, we don't actually know what the type is. But it is more likely to
+ // be a structure than anything else, so use the same logic as an identified structure.
+ if (type->IsNamedTypeRefer())
+ return false;
+
+ if (type->GetWidth() == 0 || type->GetWidth() == 1 || type->GetWidth() == 2 || type->GetWidth() == 4
+ || type->GetWidth() == 8)
+ return true;
+ return false;
+ }
+
+ bool IsStackAdjustedOnReturn() override
+ {
+ // Only for indirect returns, see GetStackAdjustmentForLocations below
+ return true;
+ }
+
+ int64_t GetStackAdjustmentForLocations(BinaryView*, const std::optional<ValueLocation>& returnValue,
+ const vector<ValueLocation>&, const vector<Ref<Type>>&) override
+ {
+ if (!returnValue.has_value())
+ return 0;
+ // Indirect return values have the pointer popped off the stack by the called function
+ if (returnValue->indirect)
+ return 4;
+ return 0;
+ }
+};
+
+
class X86CdeclCallingConvention: public X86BaseCallingConvention
{
public:
@@ -3870,6 +3916,34 @@ public:
};
+class X86SystemVStdcallCallingConvention: public X86BaseCallingConvention
+{
+public:
+ X86SystemVStdcallCallingConvention(Architecture* arch): X86BaseCallingConvention(arch, "sysv-stdcall")
+ {
+ }
+
+ bool IsStackAdjustedOnReturn() override
+ {
+ return true;
+ }
+
+ bool IsReturnTypeRegisterCompatible(BinaryView*, Type* type) override
+ {
+ if (!type)
+ return false;
+ if (type->IsFloat())
+ return true;
+ if (type->IsStructure() || type->IsArray())
+ return false;
+ if (type->GetWidth() == 0 || type->GetWidth() == 1 || type->GetWidth() == 2 || type->GetWidth() == 4
+ || type->GetWidth() == 8)
+ return true;
+ return false;
+ }
+};
+
+
class X86RegParmCallingConvention: public X86BaseCallingConvention
{
public:
@@ -3877,10 +3951,33 @@ public:
{
}
- virtual vector<uint32_t> GetIntegerArgumentRegisters() override
+ vector<uint32_t> GetIntegerArgumentRegisters() override
{
return vector<uint32_t>{ XED_REG_EAX, XED_REG_EDX, XED_REG_ECX };
}
+
+ bool IsReturnTypeRegisterCompatible(BinaryView*, Type* type) override
+ {
+ if (!type)
+ return false;
+ if (type->IsFloat())
+ return true;
+ if (type->IsStructure() || type->IsArray())
+ return false;
+ if (type->GetWidth() == 0 || type->GetWidth() == 1 || type->GetWidth() == 2 || type->GetWidth() == 4
+ || type->GetWidth() == 8)
+ return true;
+ return false;
+ }
+
+ bool IsArgumentTypeRegisterCompatible(BinaryView*, Type* type) override
+ {
+ if (!type)
+ return false;
+ if (type->IsFloat())
+ return true;
+ return type->GetWidth() <= 12;
+ }
};
@@ -3903,6 +4000,82 @@ public:
};
+class X86GCCFastcallCallingConvention: public X86BaseCallingConvention
+{
+public:
+ X86GCCFastcallCallingConvention(Architecture* arch): X86BaseCallingConvention(arch, "gcc-fastcall")
+ {
+ }
+
+ vector<uint32_t> GetIntegerArgumentRegisters() override
+ {
+ return vector<uint32_t>{ XED_REG_ECX, XED_REG_EDX };
+ }
+
+ bool IsStackAdjustedOnReturn() override
+ {
+ return true;
+ }
+
+ bool IsReturnTypeRegisterCompatible(BinaryView*, Type* type) override
+ {
+ if (!type)
+ return false;
+ if (type->IsFloat())
+ return true;
+ if (type->IsStructure() || type->IsArray())
+ return false;
+ if (type->GetWidth() == 0 || type->GetWidth() == 1 || type->GetWidth() == 2 || type->GetWidth() == 4
+ || type->GetWidth() == 8)
+ return true;
+ return false;
+ }
+
+ Variable GetIndirectReturnValueLocation() override
+ {
+ return Variable::Register(XED_REG_ECX);
+ }
+};
+
+
+class X86ClangFastcallCallingConvention: public X86BaseCallingConvention
+{
+public:
+ X86ClangFastcallCallingConvention(Architecture* arch): X86BaseCallingConvention(arch, "clang-fastcall")
+ {
+ }
+
+ vector<uint32_t> GetIntegerArgumentRegisters() override
+ {
+ return vector<uint32_t>{ XED_REG_ECX, XED_REG_EDX };
+ }
+
+ bool IsStackAdjustedOnReturn() override
+ {
+ return true;
+ }
+
+ bool IsReturnTypeRegisterCompatible(BinaryView*, Type* type) override
+ {
+ if (!type)
+ return false;
+ if (type->IsFloat())
+ return true;
+ if (type->IsStructure() || type->IsArray())
+ return false;
+ if (type->GetWidth() == 0 || type->GetWidth() == 1 || type->GetWidth() == 2 || type->GetWidth() == 4
+ || type->GetWidth() == 8)
+ return true;
+ return false;
+ }
+
+ Variable GetIndirectReturnValueLocation() override
+ {
+ return Variable::StackOffset(4);
+ }
+};
+
+
class X86ThiscallCallingConvention: public X86BaseCallingConvention
{
public:
@@ -3927,6 +4100,92 @@ public:
};
+class X86GCCThiscallCallingConvention: public X86BaseCallingConvention
+{
+public:
+ X86GCCThiscallCallingConvention(Architecture* arch): X86BaseCallingConvention(arch, "gcc-thiscall")
+ {
+ }
+
+ vector<uint32_t> GetIntegerArgumentRegisters() override
+ {
+ return vector<uint32_t>{ XED_REG_ECX };
+ }
+
+ vector<uint32_t> GetRequiredArgumentRegisters() override
+ {
+ return vector<uint32_t>{ XED_REG_ECX };
+ }
+
+ bool IsStackAdjustedOnReturn() override
+ {
+ return true;
+ }
+
+ bool IsReturnTypeRegisterCompatible(BinaryView*, Type* type) override
+ {
+ if (!type)
+ return false;
+ if (type->IsFloat())
+ return true;
+ if (type->IsStructure() || type->IsArray())
+ return false;
+ if (type->GetWidth() == 0 || type->GetWidth() == 1 || type->GetWidth() == 2 || type->GetWidth() == 4
+ || type->GetWidth() == 8)
+ return true;
+ return false;
+ }
+
+ Variable GetIndirectReturnValueLocation() override
+ {
+ return Variable::Register(XED_REG_ECX);
+ }
+};
+
+
+class X86ClangThiscallCallingConvention: public X86BaseCallingConvention
+{
+public:
+ X86ClangThiscallCallingConvention(Architecture* arch): X86BaseCallingConvention(arch, "clang-thiscall")
+ {
+ }
+
+ vector<uint32_t> GetIntegerArgumentRegisters() override
+ {
+ return vector<uint32_t>{ XED_REG_ECX };
+ }
+
+ vector<uint32_t> GetRequiredArgumentRegisters() override
+ {
+ return vector<uint32_t>{ XED_REG_ECX };
+ }
+
+ bool IsStackAdjustedOnReturn() override
+ {
+ return true;
+ }
+
+ bool IsReturnTypeRegisterCompatible(BinaryView*, Type* type) override
+ {
+ if (!type)
+ return false;
+ if (type->IsFloat())
+ return true;
+ if (type->IsStructure() || type->IsArray())
+ return false;
+ if (type->GetWidth() == 0 || type->GetWidth() == 1 || type->GetWidth() == 2 || type->GetWidth() == 4
+ || type->GetWidth() == 8)
+ return true;
+ return false;
+ }
+
+ Variable GetIndirectReturnValueLocation() override
+ {
+ return Variable::StackOffset(4);
+ }
+};
+
+
class X86LinuxSystemCallConvention: public CallingConvention
{
public:
@@ -4167,6 +4426,276 @@ public:
class X64SystemVCallingConvention: public X64BaseCallingConvention
{
+ enum ValueClass
+ {
+ NoClass,
+ Integer,
+ SSE,
+ SSEUpper,
+ X87,
+ X87Upper,
+ X87Complex
+ };
+
+ struct Component
+ {
+ ValueClass valueClass;
+ uint64_t offset;
+ uint64_t size;
+ };
+
+ std::optional<vector<Component>> GetTypeFields(BinaryView* view, Type* type, std::set<std::string>& visitedTypes)
+ {
+ vector<Component> result;
+ if (!type || type->GetWidth() > 64)
+ return std::nullopt;
+
+ if (type->GetWidth() == 0)
+ return result;
+
+ switch (type->GetClass())
+ {
+ case VoidTypeClass:
+ break;
+ case BoolTypeClass:
+ case IntegerTypeClass:
+ case EnumerationTypeClass:
+ case PointerTypeClass:
+ case WideCharTypeClass:
+ for (uint64_t offset = 0; offset < type->GetWidth(); offset += 8)
+ {
+ if (offset + 8 > type->GetWidth())
+ result.emplace_back(Integer, offset, type->GetWidth() - offset);
+ else
+ result.emplace_back(Integer, offset, 8);
+ }
+ break;
+ case FloatTypeClass:
+ if (type->GetWidth() == 10)
+ {
+ result.emplace_back(X87, 0, 8);
+ result.emplace_back(X87Upper, 8, 2);
+ }
+ else
+ {
+ for (uint64_t offset = 0; offset < type->GetWidth(); offset += 8)
+ {
+ ValueClass valueClass;
+ if (offset == 0)
+ valueClass = SSE;
+ else
+ valueClass = SSEUpper;
+ if (offset + 8 > type->GetWidth())
+ result.emplace_back(valueClass, offset, type->GetWidth() - offset);
+ else
+ result.emplace_back(valueClass, offset, 8);
+ }
+ }
+ break;
+ case StructureTypeClass:
+ {
+ Ref<Structure> structure = type->GetStructure();
+ if (!structure || !view)
+ return std::nullopt;
+ for (auto& member : structure->GetMembersIncludingInherited(view->GetTypeContainer()))
+ {
+ // Ensure that everything is aligned on natural boundaries. If it is not, it is stored in memory.
+ uint64_t alignment = 1;
+ if (member.member.type.GetValue() && member.member.type->GetAlignment() != 0)
+ alignment = member.member.type->GetAlignment();
+ if (member.member.offset % alignment != 0)
+ return std::nullopt;
+
+ std::set<std::string> fieldVisitedTypes = visitedTypes;
+ std::optional<vector<Component>> fieldComponents =
+ GetTypeFields(view, member.member.type.GetValue(), fieldVisitedTypes);
+ if (!fieldComponents.has_value())
+ return std::nullopt;
+ for (auto& component : fieldComponents.value())
+ result.emplace_back(component.valueClass, member.member.offset + component.offset, component.size);
+ }
+ break;
+ }
+ case ArrayTypeClass:
+ {
+ Ref<Type> elementType = type->GetChildType().GetValue();
+ std::optional<std::vector<Component>> elementComponents = GetTypeFields(view, elementType, visitedTypes);
+ if (!elementComponents.has_value() || elementComponents->empty())
+ return std::nullopt;
+ if (type->GetElementCount() > 64)
+ return std::nullopt;
+ for (uint64_t i = 0; i < type->GetElementCount(); i++)
+ {
+ uint64_t offset = i * elementType->GetWidth();
+ for (auto& component : elementComponents.value())
+ result.emplace_back(component.valueClass, offset + component.offset, component.size);
+ }
+ break;
+ }
+ case NamedTypeReferenceClass:
+ {
+ auto ntr = type->GetNamedTypeReference();
+ if (!view || visitedTypes.contains(ntr->GetTypeId()))
+ return std::nullopt;
+ visitedTypes.insert(ntr->GetTypeId());
+ return GetTypeFields(view, view->GetTypeByRef(ntr), visitedTypes);
+ }
+ default:
+ return std::nullopt;
+ }
+ return result;
+ }
+
+ std::optional<std::vector<Component>> GetValueClassificationForType(BinaryView* view, Type* type)
+ {
+ if (!type)
+ return std::nullopt;
+ if (type->GetWidth() > 64)
+ return std::nullopt;
+
+ // Split the component up into fields, with each field split into 8 byte components
+ std::optional<std::vector<Component>> fields;
+ std::set<std::string> visitedTypes;
+ fields = GetTypeFields(view, type, visitedTypes);
+ if (!fields.has_value() || fields->empty())
+ return fields;
+
+ // Initialize components for each 8 bytes of the type. The last component may be smaller than 8 bytes,
+ // but will be padded to fit in an 8-byte component.
+ vector<Component> result;
+ for (uint64_t offset = 0; offset < type->GetWidth(); offset += 8)
+ {
+ if (offset + 8 > type->GetWidth())
+ result.emplace_back(NoClass, offset, type->GetWidth() - offset);
+ else
+ result.emplace_back(NoClass, offset, 8);
+ }
+
+ // Resolve the classes of each component based on the fields
+ for (auto& field : fields.value())
+ {
+ size_t componentIndex = (size_t)(field.offset / 8);
+ if (componentIndex >= result.size())
+ return std::nullopt;
+
+ auto& component = result[componentIndex];
+ if (component.valueClass == field.valueClass)
+ {
+ continue;
+ }
+ if (component.valueClass == NoClass)
+ {
+ component.valueClass = field.valueClass;
+ continue;
+ }
+
+ switch (field.valueClass)
+ {
+ case NoClass:
+ break;
+ case Integer:
+ component.valueClass = Integer;
+ break;
+ case X87:
+ case X87Upper:
+ case X87Complex:
+ return std::nullopt;
+ default:
+ if (component.valueClass != Integer)
+ component.valueClass = SSE;
+ break;
+ }
+ }
+
+ // Any components that are still assigned to NoClass are converted to Integer as a default. This
+ // usually happens when there are chunks of a structure that have no fields defined yet. This is
+ // the most likely class by far, and there isn't any information available to know for sure.
+ for (auto& component : result)
+ {
+ if (component.valueClass == NoClass)
+ component.valueClass = Integer;
+ }
+
+ // The X87Upper class is only valid if preceded by X87 and is only valid for a 16-byte result
+ if (result.size() == 2 && result[0].valueClass == X87 && result[1].valueClass == X87Upper)
+ return result;
+ if (std::ranges::any_of(result, [](const Component& component) { return component.valueClass == X87Upper; }))
+ return std::nullopt;
+
+ // If the result is larger than 16 bytes, it is passed in memory unless it is all SSE, with SSEUpper
+ // as the class for all but the first component.
+ if (result.size() > 2)
+ {
+ if (result[0].valueClass != SSE)
+ return std::nullopt;
+ if (std::any_of(result.begin() + 1, result.end(), [](const Component& component) {
+ return component.valueClass != SSEUpper;
+ }))
+ return std::nullopt;
+ return result;
+ }
+
+ // Clean up SSEUpper components that aren't preceded by an SSE component
+ for (size_t i = 0; i < result.size(); i++)
+ {
+ if (result[i].valueClass != SSEUpper)
+ continue;
+ if (i == 0 || (result[i - 1].valueClass != SSE && result[i - 1].valueClass != SSEUpper))
+ result[i].valueClass = SSE;
+ }
+
+ return result;
+ }
+
+ bool IsTypeRegisterCompatible(BinaryView* view, Type* type, size_t maxIntegerRegs, size_t maxSSERegs,
+ size_t maxX87Regs, size_t maxX87ComplexRegs)
+ {
+ auto components = GetValueClassificationForType(view, type);
+ if (!components.has_value())
+ return false;
+
+ size_t integerCount = 0;
+ size_t sseCount = 0;
+ size_t x87Count = 0;
+ size_t x87ComplexCount = 0;
+ for (auto& component : components.value())
+ {
+ switch (component.valueClass)
+ {
+ case NoClass:
+ return false;
+ case Integer:
+ integerCount++;
+ break;
+ case SSE:
+ sseCount++;
+ break;
+ case X87:
+ x87Count++;
+ break;
+ case X87Complex:
+ x87ComplexCount++;
+ break;
+ case SSEUpper:
+ case X87Upper:
+ break;
+ }
+ }
+
+ if (integerCount > maxIntegerRegs)
+ return false;
+ if (sseCount > maxSSERegs)
+ return false;
+ if (x87Count > maxX87Regs)
+ return false;
+ if (x87ComplexCount % 4 != 0)
+ return false;
+ x87Count += x87ComplexCount / 2;
+ if (x87Count > maxX87ComplexRegs)
+ return false;
+ return true;
+ }
+
public:
X64SystemVCallingConvention(Architecture* arch): X64BaseCallingConvention(arch, "sysv")
{
@@ -4197,6 +4726,305 @@ public:
XED_REG_RBX, XED_REG_RBP,
XED_REG_R12, XED_REG_R13, XED_REG_R14, XED_REG_R15 };
}
+
+ bool IsReturnTypeRegisterCompatible(BinaryView* view, Type* type) override
+ {
+ // If we have an unresolved NTR, use the default handling. We can't really know what
+ // this type is, so just preserve the old behavior.
+ if (type && type->GetClass() == NamedTypeReferenceClass && type->GetWidth() == 0)
+ return DefaultIsReturnTypeRegisterCompatible(type);
+
+ return IsTypeRegisterCompatible(view, type, 2, 2, 1, 2);
+ }
+
+ bool IsArgumentTypeRegisterCompatible(BinaryView* view, Type* type) override
+ {
+ // If we have an unresolved NTR, use the default handling. We can't really know what
+ // this type is, so just preserve the old behavior.
+ if (type && type->GetClass() == NamedTypeReferenceClass && type->GetWidth() == 0)
+ return DefaultIsArgumentTypeRegisterCompatible(type);
+
+ return IsTypeRegisterCompatible(view, type, 6, 8, 0, 0);
+ }
+
+ ValueLocation GetReturnValueLocation(BinaryView* view, const ReturnValue& returnValue) override
+ {
+ Ref<Type> type = returnValue.type.GetValue();
+ if (!type || type->IsVoid())
+ return ValueLocation();
+
+ // If we have an unresolved NTR, use the default handling. We can't really know what
+ // this type is, so just preserve the old behavior.
+ if (type->GetClass() == NamedTypeReferenceClass && type->GetWidth() == 0)
+ return GetDefaultReturnValueLocation(view, returnValue);
+
+ auto components = GetValueClassificationForType(view, type);
+ if (!components.has_value())
+ {
+ // Value doesn't work in a register, return through an indirect pointer
+ return ValueLocation({GetIndirectReturnValueLocation()}, true, GetReturnedIndirectReturnValuePointer());
+ }
+
+ ValueLocation result;
+ size_t integerCount = 0;
+ static constexpr uint32_t integerRegs[2] = {XED_REG_RAX, XED_REG_RDX};
+ size_t sseCount = 0;
+ static constexpr uint32_t sseRegs[2] = {XED_REG_ZMM0, XED_REG_ZMM1};
+ size_t x87Count = 0;
+ static constexpr uint32_t x87Regs[2] = {XED_REG_ST0, XED_REG_ST1};
+ bool valid = true;
+ for (auto& component : components.value())
+ {
+ switch (component.valueClass)
+ {
+ case NoClass:
+ valid = false;
+ break;
+ case Integer:
+ if (integerCount >= 2)
+ {
+ valid = false;
+ break;
+ }
+ result.components.emplace_back(
+ Variable::Register(integerRegs[integerCount++]), component.offset, component.size);
+ break;
+ case SSE:
+ if (sseCount >= 2)
+ {
+ valid = false;
+ break;
+ }
+ result.components.emplace_back(
+ Variable::Register(sseRegs[sseCount++]), component.offset, component.size);
+ break;
+ case X87:
+ if (x87Count >= 1)
+ {
+ valid = false;
+ break;
+ }
+ result.components.emplace_back(
+ Variable::Register(x87Regs[x87Count++]), component.offset, component.size);
+ break;
+ case SSEUpper:
+ case X87Upper:
+ if (result.components.empty())
+ {
+ valid = false;
+ break;
+ }
+ result.components.back().size = result.components.back().size.value_or(0) + component.size;
+ break;
+ case X87Complex:
+ if (result.components.empty() || result.components.back().size.value_or(0) > 8)
+ {
+ if (x87Count >= 2)
+ {
+ valid = false;
+ break;
+ }
+ result.components.emplace_back(
+ Variable::Register(x87Regs[x87Count++]), component.offset, component.size);
+ }
+ else
+ {
+ result.components.back().size = result.components.back().size.value_or(0) + component.size;
+ }
+ break;
+ }
+ }
+
+ // Single component values shouldn't have the size set, otherwise heuristically determined locations
+ // will not match.
+ if (result.components.size() == 1)
+ result.components[0].size.reset();
+
+ if (valid)
+ return result;
+
+ // Value doesn't work in a register, return through an indirect pointer
+ return ValueLocation({GetIndirectReturnValueLocation()}, true, GetReturnedIndirectReturnValuePointer());
+ }
+
+ Variable GetIndirectReturnValueLocation() override { return Variable::Register(XED_REG_RDI); }
+ std::optional<Variable> GetReturnedIndirectReturnValuePointer() override { return Variable::Register(XED_REG_RAX); }
+
+ std::vector<ValueLocation> GetParameterLocations(BinaryView* view, const std::optional<ValueLocation>& returnValue,
+ const std::vector<FunctionParameter>& params,
+ const std::optional<std::set<uint32_t>>& permittedRegs = std::nullopt) override
+ {
+ vector<ValueLocation> result;
+ result.reserve(params.size());
+
+ vector<uint32_t> intArgs = GetIntegerArgumentRegisters();
+ vector<uint32_t> sseArgs = GetFloatArgumentRegisters();
+
+ auto intArgIter = intArgs.begin();
+ auto sseArgIter = sseArgs.begin();
+ size_t addrSize = GetArchitecture()->GetAddressSize();
+ int64_t stackOffset = addrSize;
+
+ if (returnValue.has_value() && returnValue->indirect)
+ {
+ // If the return value is stored as an indirect location parameter, ensure that the normal parameters
+ // don't overlap with it.
+ for (auto& component : returnValue->components)
+ {
+ if (component.variable.type == RegisterVariableSourceType)
+ {
+ if (intArgIter != intArgs.end() && *intArgIter == component.variable.storage)
+ intArgIter++;
+ if (sseArgIter != sseArgs.end() && *sseArgIter == component.variable.storage)
+ sseArgIter++;
+ }
+ else if (component.variable.type == StackVariableSourceType
+ && component.variable.storage >= stackOffset)
+ {
+ // Adjust the next automatic stack location to after this one
+ stackOffset = component.variable.storage;
+ stackOffset += 8;
+ }
+ }
+ }
+
+ for (auto& param : params)
+ {
+ if (param.locationSource == CustomLocationSource)
+ {
+ // Parameter is not stored in a normal location, use custom variable
+ result.push_back(param.location);
+ for (auto& component : param.location.components)
+ {
+ if (component.variable.type == RegisterVariableSourceType)
+ {
+ // If the non-default location matches the next register in the register parameter
+ // lists, advance the iterators. It may just be a type mismatch, and we still
+ // want to maintain the state for future parameters.
+ if (intArgIter != intArgs.end() && *intArgIter == component.variable.storage)
+ intArgIter++;
+ if (sseArgIter != sseArgs.end() && *sseArgIter == component.variable.storage)
+ sseArgIter++;
+ }
+ else if (component.variable.type == StackVariableSourceType
+ && component.variable.storage >= stackOffset)
+ {
+ // Adjust the next automatic stack location to after this one
+ stackOffset = component.variable.storage;
+ stackOffset += 8;
+ }
+ }
+ continue;
+ }
+
+ Ref<Type> type = param.type.GetValue();
+ size_t width = type->GetWidth();
+ bool indirect = false;
+
+ if ((type->GetClass() == ArrayTypeClass || param.locationSource == PassByReferenceLocationSource)
+ && param.locationSource != PassByValueLocationSource)
+ {
+ type = Type::PointerType(GetArchitecture(), type);
+ indirect = true;
+ width = type->GetWidth();
+ }
+
+ if (IsArgumentTypeRegisterCompatible(view, type))
+ {
+ auto components = GetValueClassificationForType(view, type);
+ if (components.has_value() && !components->empty())
+ {
+ // Save off the current register iterators. If we fail to find enough registers, we
+ // need to revert the register allocations.
+ auto savedIntArgIter = intArgIter;
+ auto savedSseArgIter = sseArgIter;
+
+ ValueLocation location;
+ bool valid = true;
+ for (auto& component : components.value())
+ {
+ switch (component.valueClass)
+ {
+ case Integer:
+ if (intArgIter == intArgs.end()
+ || (permittedRegs.has_value() && !permittedRegs->contains(*intArgIter)))
+ {
+ valid = false;
+ break;
+ }
+ location.components.emplace_back(
+ Variable::Register(*intArgIter), component.offset, component.size);
+ ++intArgIter;
+ break;
+ case SSE:
+ if (sseArgIter == sseArgs.end()
+ || (permittedRegs.has_value() && !permittedRegs->contains(*sseArgIter)))
+ {
+ valid = false;
+ break;
+ }
+ location.components.emplace_back(
+ Variable::Register(*sseArgIter), component.offset, component.size);
+ ++sseArgIter;
+ break;
+ case SSEUpper:
+ if (location.components.empty())
+ {
+ valid = false;
+ break;
+ }
+ location.components.back().size =
+ location.components.back().size.value_or(0) + component.size;
+ break;
+ default:
+ valid = false;
+ break;
+ }
+ }
+
+ if (indirect)
+ {
+ location.indirect = true;
+ std::ranges::for_each(location.components, [&](auto& component) {
+ component.size = param.type->GetWidth();
+ });
+ }
+
+ // Single component values shouldn't have the size set, otherwise heuristically determined locations
+ // will not match.
+ if (location.components.size() == 1)
+ location.components[0].size.reset();
+
+ if (valid)
+ {
+ // Value fit in registers
+ result.emplace_back(location);
+ continue;
+ }
+
+ // Value does not fit in available registers, revert to prior available registers
+ intArgIter = savedIntArgIter;
+ sseArgIter = savedSseArgIter;
+ }
+ }
+
+ // Value must be placed on the stack
+ if (width % addrSize != 0)
+ width += addrSize - width % addrSize;
+
+ // Stack offset must be naturally aligned. Alignment is performed on the offset before the
+ // return address is pushed (using caller's stack alignment).
+ int64_t alignment = type->GetAlignment();
+ int64_t afterRetOffset = stackOffset - addrSize;
+ if (alignment != 0 && afterRetOffset % alignment != 0)
+ stackOffset += alignment - afterRetOffset % alignment;
+
+ result.emplace_back(ValueLocation({{Variable::StackOffset(stackOffset), 0}}, indirect));
+ stackOffset += width;
+ }
+
+ return result;
+ }
};
@@ -5310,15 +6138,27 @@ extern "C"
x86->RegisterCallingConvention(conv);
x86->SetDefaultCallingConvention(conv);
x86->SetCdeclCallingConvention(conv);
+ conv = new X86SystemVCallingConvention(x86);
+ x86->RegisterCallingConvention(conv);
conv = new X86StdcallCallingConvention(x86);
x86->RegisterCallingConvention(conv);
x86->SetStdcallCallingConvention(conv);
+ conv = new X86SystemVStdcallCallingConvention(x86);
+ x86->RegisterCallingConvention(conv);
conv = new X86RegParmCallingConvention(x86);
x86->RegisterCallingConvention(conv);
conv = new X86FastcallCallingConvention(x86);
x86->RegisterCallingConvention(conv);
+ conv = new X86GCCFastcallCallingConvention(x86);
+ x86->RegisterCallingConvention(conv);
+ conv = new X86ClangFastcallCallingConvention(x86);
+ x86->RegisterCallingConvention(conv);
conv = new X86ThiscallCallingConvention(x86);
x86->RegisterCallingConvention(conv);
+ conv = new X86GCCThiscallCallingConvention(x86);
+ x86->RegisterCallingConvention(conv);
+ conv = new X86ClangThiscallCallingConvention(x86);
+ x86->RegisterCallingConvention(conv);
conv = new X86LinuxSystemCallConvention(x86);
x86->RegisterCallingConvention(conv);
conv = new X86PascalCallingConvention(x86);