mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2026-07-25 12:46:26 -04:00
d98e321d12
When we pass a AArch64 Homogeneous Floating-Point
Aggregate (HFA) argument with increased alignment
requirements, for example
struct S {
__attribute__ ((__aligned__(16))) double v[4];
};
Clang uses `[4 x double]` for the parameter, which is passed
on the stack at alignment 8, whereas it should be at
alignment 16, following Rule C.4 in
AAPCS (https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst#642parameter-passing-rules)
Currently we don't have a way to express in LLVM IR the
alignment requirements of the function arguments. The align
attribute is applicable to pointers only, and only for some
special ways of passing arguments (e..g byval). When
implementing AAPCS32/AAPCS64, clang resorts to dubious hacks
of coercing to types, which naturally have the needed
alignment. We don't have enough types to cover all the
cases, though.
This patch introduces a new use of the stackalign attribute
to control stack slot alignment, when and if an argument is
passed in memory.
The attribute align is left as an optimizer hint - it still
applies to pointer types only and pertains to the content of
the pointer, whereas the alignment of the pointer itself is
determined by the stackalign attribute.
For byval arguments, the stackalign attribute assumes the
role, previously perfomed by align, falling back to align if
stackalign` is absent.
On the clang side, when passing arguments using the "direct"
style (cf. `ABIArgInfo::Kind`), now we can optionally
specify an alignment, which is emitted as the new
`stackalign` attribute.
Patch by Momchil Velikov and Lucas Prates.
Differential Revision: https://reviews.llvm.org/D98794
258 lines
8.6 KiB
C++
258 lines
8.6 KiB
C++
//===-- llvm/CodeGen/TargetCallingConv.h - Calling Convention ---*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines types for working with calling-convention information.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_CODEGEN_TARGETCALLINGCONV_H
|
|
#define LLVM_CODEGEN_TARGETCALLINGCONV_H
|
|
|
|
#include "llvm/CodeGen/ValueTypes.h"
|
|
#include "llvm/Support/Alignment.h"
|
|
#include "llvm/Support/MachineValueType.h"
|
|
#include "llvm/Support/MathExtras.h"
|
|
#include <cassert>
|
|
#include <climits>
|
|
#include <cstdint>
|
|
|
|
namespace llvm {
|
|
namespace ISD {
|
|
|
|
struct ArgFlagsTy {
|
|
private:
|
|
unsigned IsZExt : 1; ///< Zero extended
|
|
unsigned IsSExt : 1; ///< Sign extended
|
|
unsigned IsInReg : 1; ///< Passed in register
|
|
unsigned IsSRet : 1; ///< Hidden struct-ret ptr
|
|
unsigned IsByVal : 1; ///< Struct passed by value
|
|
unsigned IsByRef : 1; ///< Passed in memory
|
|
unsigned IsNest : 1; ///< Nested fn static chain
|
|
unsigned IsReturned : 1; ///< Always returned
|
|
unsigned IsSplit : 1;
|
|
unsigned IsInAlloca : 1; ///< Passed with inalloca
|
|
unsigned IsPreallocated : 1; ///< ByVal without the copy
|
|
unsigned IsSplitEnd : 1; ///< Last part of a split
|
|
unsigned IsSwiftSelf : 1; ///< Swift self parameter
|
|
unsigned IsSwiftError : 1; ///< Swift error parameter
|
|
unsigned IsCFGuardTarget : 1; ///< Control Flow Guard target
|
|
unsigned IsHva : 1; ///< HVA field for
|
|
unsigned IsHvaStart : 1; ///< HVA structure start
|
|
unsigned IsSecArgPass : 1; ///< Second argument
|
|
unsigned MemAlign : 4; ///< Log 2 of alignment when arg is passed in memory
|
|
///< (including byval/byref)
|
|
unsigned OrigAlign : 5; ///< Log 2 of original alignment
|
|
unsigned IsInConsecutiveRegsLast : 1;
|
|
unsigned IsInConsecutiveRegs : 1;
|
|
unsigned IsCopyElisionCandidate : 1; ///< Argument copy elision candidate
|
|
unsigned IsPointer : 1;
|
|
|
|
unsigned ByValOrByRefSize; ///< Byval or byref struct size
|
|
|
|
unsigned PointerAddrSpace; ///< Address space of pointer argument
|
|
|
|
public:
|
|
ArgFlagsTy()
|
|
: IsZExt(0), IsSExt(0), IsInReg(0), IsSRet(0), IsByVal(0), IsByRef(0),
|
|
IsNest(0), IsReturned(0), IsSplit(0), IsInAlloca(0), IsPreallocated(0),
|
|
IsSplitEnd(0), IsSwiftSelf(0), IsSwiftError(0), IsCFGuardTarget(0),
|
|
IsHva(0), IsHvaStart(0), IsSecArgPass(0), MemAlign(0),
|
|
OrigAlign(0), IsInConsecutiveRegsLast(0), IsInConsecutiveRegs(0),
|
|
IsCopyElisionCandidate(0), IsPointer(0), ByValOrByRefSize(0),
|
|
PointerAddrSpace(0) {
|
|
static_assert(sizeof(*this) == 3 * sizeof(unsigned), "flags are too big");
|
|
}
|
|
|
|
bool isZExt() const { return IsZExt; }
|
|
void setZExt() { IsZExt = 1; }
|
|
|
|
bool isSExt() const { return IsSExt; }
|
|
void setSExt() { IsSExt = 1; }
|
|
|
|
bool isInReg() const { return IsInReg; }
|
|
void setInReg() { IsInReg = 1; }
|
|
|
|
bool isSRet() const { return IsSRet; }
|
|
void setSRet() { IsSRet = 1; }
|
|
|
|
bool isByVal() const { return IsByVal; }
|
|
void setByVal() { IsByVal = 1; }
|
|
|
|
bool isByRef() const { return IsByRef; }
|
|
void setByRef() { IsByRef = 1; }
|
|
|
|
bool isInAlloca() const { return IsInAlloca; }
|
|
void setInAlloca() { IsInAlloca = 1; }
|
|
|
|
bool isPreallocated() const { return IsPreallocated; }
|
|
void setPreallocated() { IsPreallocated = 1; }
|
|
|
|
bool isSwiftSelf() const { return IsSwiftSelf; }
|
|
void setSwiftSelf() { IsSwiftSelf = 1; }
|
|
|
|
bool isSwiftError() const { return IsSwiftError; }
|
|
void setSwiftError() { IsSwiftError = 1; }
|
|
|
|
bool isCFGuardTarget() const { return IsCFGuardTarget; }
|
|
void setCFGuardTarget() { IsCFGuardTarget = 1; }
|
|
|
|
bool isHva() const { return IsHva; }
|
|
void setHva() { IsHva = 1; }
|
|
|
|
bool isHvaStart() const { return IsHvaStart; }
|
|
void setHvaStart() { IsHvaStart = 1; }
|
|
|
|
bool isSecArgPass() const { return IsSecArgPass; }
|
|
void setSecArgPass() { IsSecArgPass = 1; }
|
|
|
|
bool isNest() const { return IsNest; }
|
|
void setNest() { IsNest = 1; }
|
|
|
|
bool isReturned() const { return IsReturned; }
|
|
void setReturned(bool V = true) { IsReturned = V; }
|
|
|
|
bool isInConsecutiveRegs() const { return IsInConsecutiveRegs; }
|
|
void setInConsecutiveRegs(bool Flag = true) { IsInConsecutiveRegs = Flag; }
|
|
|
|
bool isInConsecutiveRegsLast() const { return IsInConsecutiveRegsLast; }
|
|
void setInConsecutiveRegsLast(bool Flag = true) {
|
|
IsInConsecutiveRegsLast = Flag;
|
|
}
|
|
|
|
bool isSplit() const { return IsSplit; }
|
|
void setSplit() { IsSplit = 1; }
|
|
|
|
bool isSplitEnd() const { return IsSplitEnd; }
|
|
void setSplitEnd() { IsSplitEnd = 1; }
|
|
|
|
bool isCopyElisionCandidate() const { return IsCopyElisionCandidate; }
|
|
void setCopyElisionCandidate() { IsCopyElisionCandidate = 1; }
|
|
|
|
bool isPointer() const { return IsPointer; }
|
|
void setPointer() { IsPointer = 1; }
|
|
|
|
Align getNonZeroMemAlign() const {
|
|
return decodeMaybeAlign(MemAlign).valueOrOne();
|
|
}
|
|
|
|
void setMemAlign(Align A) {
|
|
MemAlign = encode(A);
|
|
assert(getNonZeroMemAlign() == A && "bitfield overflow");
|
|
}
|
|
|
|
Align getNonZeroByValAlign() const {
|
|
assert(isByVal());
|
|
MaybeAlign A = decodeMaybeAlign(MemAlign);
|
|
assert(A && "ByValAlign must be defined");
|
|
return *A;
|
|
}
|
|
|
|
Align getNonZeroOrigAlign() const {
|
|
return decodeMaybeAlign(OrigAlign).valueOrOne();
|
|
}
|
|
|
|
void setOrigAlign(Align A) {
|
|
OrigAlign = encode(A);
|
|
assert(getNonZeroOrigAlign() == A && "bitfield overflow");
|
|
}
|
|
|
|
unsigned getByValSize() const {
|
|
assert(isByVal() && !isByRef());
|
|
return ByValOrByRefSize;
|
|
}
|
|
void setByValSize(unsigned S) {
|
|
assert(isByVal() && !isByRef());
|
|
ByValOrByRefSize = S;
|
|
}
|
|
|
|
unsigned getByRefSize() const {
|
|
assert(!isByVal() && isByRef());
|
|
return ByValOrByRefSize;
|
|
}
|
|
void setByRefSize(unsigned S) {
|
|
assert(!isByVal() && isByRef());
|
|
ByValOrByRefSize = S;
|
|
}
|
|
|
|
unsigned getPointerAddrSpace() const { return PointerAddrSpace; }
|
|
void setPointerAddrSpace(unsigned AS) { PointerAddrSpace = AS; }
|
|
};
|
|
|
|
/// InputArg - This struct carries flags and type information about a
|
|
/// single incoming (formal) argument or incoming (from the perspective
|
|
/// of the caller) return value virtual register.
|
|
///
|
|
struct InputArg {
|
|
ArgFlagsTy Flags;
|
|
MVT VT = MVT::Other;
|
|
EVT ArgVT;
|
|
bool Used = false;
|
|
|
|
/// Index original Function's argument.
|
|
unsigned OrigArgIndex;
|
|
/// Sentinel value for implicit machine-level input arguments.
|
|
static const unsigned NoArgIndex = UINT_MAX;
|
|
|
|
/// Offset in bytes of current input value relative to the beginning of
|
|
/// original argument. E.g. if argument was splitted into four 32 bit
|
|
/// registers, we got 4 InputArgs with PartOffsets 0, 4, 8 and 12.
|
|
unsigned PartOffset;
|
|
|
|
InputArg() = default;
|
|
InputArg(ArgFlagsTy flags, EVT vt, EVT argvt, bool used,
|
|
unsigned origIdx, unsigned partOffs)
|
|
: Flags(flags), Used(used), OrigArgIndex(origIdx), PartOffset(partOffs) {
|
|
VT = vt.getSimpleVT();
|
|
ArgVT = argvt;
|
|
}
|
|
|
|
bool isOrigArg() const {
|
|
return OrigArgIndex != NoArgIndex;
|
|
}
|
|
|
|
unsigned getOrigArgIndex() const {
|
|
assert(OrigArgIndex != NoArgIndex && "Implicit machine-level argument");
|
|
return OrigArgIndex;
|
|
}
|
|
};
|
|
|
|
/// OutputArg - This struct carries flags and a value for a
|
|
/// single outgoing (actual) argument or outgoing (from the perspective
|
|
/// of the caller) return value virtual register.
|
|
///
|
|
struct OutputArg {
|
|
ArgFlagsTy Flags;
|
|
MVT VT;
|
|
EVT ArgVT;
|
|
|
|
/// IsFixed - Is this a "fixed" value, ie not passed through a vararg "...".
|
|
bool IsFixed = false;
|
|
|
|
/// Index original Function's argument.
|
|
unsigned OrigArgIndex;
|
|
|
|
/// Offset in bytes of current output value relative to the beginning of
|
|
/// original argument. E.g. if argument was splitted into four 32 bit
|
|
/// registers, we got 4 OutputArgs with PartOffsets 0, 4, 8 and 12.
|
|
unsigned PartOffset;
|
|
|
|
OutputArg() = default;
|
|
OutputArg(ArgFlagsTy flags, EVT vt, EVT argvt, bool isfixed,
|
|
unsigned origIdx, unsigned partOffs)
|
|
: Flags(flags), IsFixed(isfixed), OrigArgIndex(origIdx),
|
|
PartOffset(partOffs) {
|
|
VT = vt.getSimpleVT();
|
|
ArgVT = argvt;
|
|
}
|
|
};
|
|
|
|
} // end namespace ISD
|
|
} // end namespace llvm
|
|
|
|
#endif // LLVM_CODEGEN_TARGETCALLINGCONV_H
|