[LLVMABI] Create ABI Utils (#185105)

This PR introduces `ABIFunctionInfo` and surrounding utility helpers,
and is part of the set of breakout PRs to upstream the LLVM ABI lowering
library prototyped in https://github.com/llvm/llvm-project/pull/140112.

`ABIFunctionInfo` is directly analogous to `CGFunctionInfo` from Clang's
existing CodeGen pipeline, and represents an ABI lowered view of the
function signature, decoupled from both the Clang AST and LLVM IR.

`ABIArgInfo` encodes lowering decisions and currently supports
Direct,Extend,Indirect and Ignore which are required for our initial
goal of implementing x86-64 SysV and BPF, but this will change as the
library grows to represent more targets that need them.

This PR is a direct precursor to the implementation of `ABIInfo` in the
library as demonstrated in the PR linked above..
This commit is contained in:
Narayan 2026-03-31 00:34:50 +05:30 committed by GitHub
parent 14ab059dec
commit 04785adec3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 301 additions and 0 deletions

View File

@ -0,0 +1,269 @@
//===----- FunctionInfo.h - ABI Function Information --------- 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
//
//===----------------------------------------------------------------------===//
//
// Defines FunctionInfo and associated types used in representing the
// ABI-coerced types for function arguments and return values.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_ABI_FUNCTIONINFO_H
#define LLVM_ABI_FUNCTIONINFO_H
#include "llvm/ABI/Types.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/IR/CallingConv.h"
#include "llvm/Support/Alignment.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/TrailingObjects.h"
#include <optional>
namespace llvm {
namespace abi {
/// Helper class to encapsulate information about how a specific type should be
/// passed to or returned from a function.
class ArgInfo {
public:
enum Kind {
/// Pass the argument directly using the normal converted LLVM type, or by
/// coercing to another specified type stored in 'CoerceToType'.
Direct,
/// Valid only for integer argument types. Same as 'direct' but also emit a
/// zero/sign extension attribute.
Extend,
/// Pass the argument indirectly via a hidden pointer with the specified
/// alignment and address space.
Indirect,
/// Ignore the argument (treat as void). Useful for void and empty structs.
Ignore,
};
private:
const Type *CoercionType = nullptr;
struct DirectAttrInfo {
unsigned Offset;
MaybeAlign Align;
};
struct IndirectAttrInfo {
Align Align;
unsigned AddrSpace;
};
union {
DirectAttrInfo DirectAttr;
IndirectAttrInfo IndirectAttr;
};
Kind TheKind;
bool SignExt : 1;
bool ZeroExt : 1;
bool IndirectByVal : 1;
bool IndirectRealign : 1;
ArgInfo(Kind K = Direct)
: TheKind(K), SignExt(false), ZeroExt(false), IndirectByVal(false),
IndirectRealign(false) {}
public:
/// \param T The type to coerce to. If null, the argument's original type is
/// used directly.
/// \param Offset Byte offset into the memory representation at which the
/// coerced type begins. Used when only part of a larger value
/// is passed directly (e.g. the high word of a multi-eightbyte
/// return value on x86-64).
/// \param Align Override for the argument's alignment. If absent, the
/// default alignment for \p T is used.
static ArgInfo getDirect(const Type *T = nullptr, unsigned Offset = 0,
MaybeAlign Align = std::nullopt) {
ArgInfo AI(Direct);
AI.CoercionType = T;
AI.DirectAttr.Offset = Offset;
AI.DirectAttr.Align = Align;
return AI;
}
static ArgInfo getExtend(const Type *T) {
assert(T && "Type cannot be null");
assert(T->isInteger() && "Unexpected type - only integers can be extended");
ArgInfo AI(Extend);
AI.CoercionType = T;
AI.DirectAttr.Offset = 0;
AI.DirectAttr.Align = std::nullopt;
const IntegerType *IntTy = cast<IntegerType>(T);
if (IntTy->isSigned())
AI.setSignExt();
else
AI.setZeroExt();
return AI;
}
/// Realign: the caller couldn't guarantee sufficient alignment - the callee
/// must copy the argument to a properly aligned temporary before use.
static ArgInfo getIndirect(Align Align, bool ByVal, unsigned AddrSpace = 0,
bool Realign = false) {
ArgInfo AI(Indirect);
AI.IndirectAttr.Align = Align;
AI.IndirectAttr.AddrSpace = AddrSpace;
AI.IndirectByVal = ByVal;
AI.IndirectRealign = Realign;
return AI;
}
static ArgInfo getIgnore() { return ArgInfo(Ignore); }
ArgInfo &setSignExt(bool SignExtend = true) {
this->SignExt = SignExtend;
if (SignExtend)
this->ZeroExt = false;
return *this;
}
ArgInfo &setZeroExt(bool ZeroExtend = true) {
this->ZeroExt = ZeroExtend;
if (ZeroExtend)
this->SignExt = false;
return *this;
}
Kind getKind() const { return TheKind; }
bool isDirect() const { return TheKind == Direct; }
bool isIndirect() const { return TheKind == Indirect; }
bool isIgnore() const { return TheKind == Ignore; }
bool isExtend() const { return TheKind == Extend; }
unsigned getDirectOffset() const {
assert((isDirect() || isExtend()) && "Not a direct or extend kind");
return DirectAttr.Offset;
}
MaybeAlign getDirectAlign() const {
assert((isDirect() || isExtend()) && "Not a direct or extend kind");
return DirectAttr.Align;
}
Align getIndirectAlign() const {
assert(isIndirect() && "Invalid Kind!");
return IndirectAttr.Align;
}
unsigned getIndirectAddrSpace() const {
assert(isIndirect() && "Invalid Kind!");
return IndirectAttr.AddrSpace;
}
bool getIndirectByVal() const {
assert(isIndirect() && "Invalid Kind!");
return IndirectByVal;
}
bool getIndirectRealign() const {
assert(isIndirect() && "Invalid Kind!");
return IndirectRealign;
}
bool isSignExt() const {
assert(isExtend() && "Invalid Kind!");
return SignExt;
}
bool isZeroExt() const {
assert(isExtend() && "Invalid Kind!");
return ZeroExt;
}
bool isNoExt() const {
assert(isExtend() && "Invalid Kind!");
return !SignExt && !ZeroExt;
}
const Type *getCoerceToType() const {
assert((isDirect() || isExtend()) && "Invalid Kind!");
return CoercionType;
}
};
struct ArgEntry {
const Type *ABIType;
ArgInfo Info;
ArgEntry(const Type *T) : ABIType(T), Info(ArgInfo::getDirect()) {}
ArgEntry(const Type *T, ArgInfo A) : ABIType(T), Info(A) {}
};
class FunctionInfo final : private TrailingObjects<FunctionInfo, ArgEntry> {
private:
const Type *ReturnType;
ArgInfo ReturnInfo;
unsigned NumArgs;
CallingConv::ID CC = CallingConv::C;
std::optional<unsigned> NumRequired;
FunctionInfo(CallingConv::ID CC, const Type *RetTy, unsigned NumArguments,
std::optional<unsigned> NumRequired)
: ReturnType(RetTy), ReturnInfo(ArgInfo::getDirect()),
NumArgs(NumArguments), CC(CC), NumRequired(NumRequired) {}
friend class TrailingObjects;
public:
using const_arg_iterator = const ArgEntry *;
using arg_iterator = ArgEntry *;
void operator delete(void *p) { ::operator delete(p); }
const_arg_iterator arg_begin() const { return getTrailingObjects(); }
const_arg_iterator arg_end() const { return getTrailingObjects() + NumArgs; }
arg_iterator arg_begin() { return getTrailingObjects(); }
arg_iterator arg_end() { return getTrailingObjects() + NumArgs; }
unsigned arg_size() const { return NumArgs; }
static FunctionInfo *
create(CallingConv::ID CC, const Type *ReturnType,
ArrayRef<const Type *> ArgTypes,
std::optional<unsigned> NumRequired = std::nullopt);
const Type *getReturnType() const { return ReturnType; }
ArgInfo &getReturnInfo() { return ReturnInfo; }
const ArgInfo &getReturnInfo() const { return ReturnInfo; }
CallingConv::ID getCallingConvention() const { return CC; }
bool isVariadic() const { return NumRequired.has_value(); }
unsigned getNumRequiredArgs() const {
return isVariadic() ? *NumRequired : arg_size();
}
ArrayRef<ArgEntry> arguments() const {
return {getTrailingObjects(), NumArgs};
}
MutableArrayRef<ArgEntry> arguments() {
return {getTrailingObjects(), NumArgs};
}
ArgEntry &getArgInfo(unsigned Index) {
assert(Index < NumArgs && "Invalid argument index");
return arguments()[Index];
}
const ArgEntry &getArgInfo(unsigned Index) const {
assert(Index < NumArgs && "Invalid argument index");
return arguments()[Index];
}
};
} // namespace abi
} // namespace llvm
#endif // LLVM_ABI_FUNCTIONINFO_H

View File

@ -1,5 +1,6 @@
add_llvm_component_library(LLVMABI
Types.cpp
FunctionInfo.cpp
ADDITIONAL_HEADER_DIRS
${LLVM_MAIN_INCLUDE_DIR}/llvm/ABI

View File

@ -0,0 +1,31 @@
//===----- FunctionInfo.cpp - ABI Function Information ----------- 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
//
//===----------------------------------------------------------------------===//
#include "llvm/ABI/FunctionInfo.h"
#include <optional>
using namespace llvm;
using namespace llvm::abi;
FunctionInfo *FunctionInfo::create(CallingConv::ID CC, const Type *ReturnType,
ArrayRef<const Type *> ArgTypes,
std::optional<unsigned> NumRequired) {
assert(!NumRequired || *NumRequired <= ArgTypes.size());
void *Buffer = operator new(totalSizeToAlloc<ArgEntry>(ArgTypes.size()));
FunctionInfo *FI =
new (Buffer) FunctionInfo(CC, ReturnType, ArgTypes.size(), NumRequired);
ArgEntry *Args = FI->getTrailingObjects();
for (unsigned I = 0; I < ArgTypes.size(); ++I)
new (&Args[I]) ArgEntry(ArgTypes[I]);
return FI;
}