Skip to content

[clang] Use different memory layout type for _BitInt(N) in LLVM IR #91364

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

Merged
merged 27 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
bbc1ff9
[clang] Lower long _BitInt(129+) to a different type in LLVM IR
Fznamznon May 7, 2024
fc6f90c
Add load and store type, update more places
Fznamznon May 13, 2024
e7fa9ad
Merge branch 'main' into long-bitint-align
Fznamznon May 29, 2024
15f680f
Cleanup
Fznamznon May 29, 2024
0ee57a3
Address some comments
Fznamznon Jun 4, 2024
87bed2e
Move constant folding into AppendBitField
Fznamznon Jun 5, 2024
ff209c2
Merge branch 'main' into long-bitint-align
Fznamznon Jun 18, 2024
08482d8
EmitStoreOfScalar when emitting return value
Fznamznon Jun 18, 2024
956eeb6
Rename LLVMTypeLayoutMatchesAST
Fznamznon Jun 18, 2024
4243bc4
Remove additional check in EmitToMemory
Fznamznon Jun 21, 2024
934c659
Extend the constant to load/store type before split
Fznamznon Jul 9, 2024
c59d3e6
Address remaining comments
Fznamznon Jul 9, 2024
6ff7436
Add comment for convertTypeForLoadStore
Fznamznon Jul 9, 2024
13cdeb8
Make the comments right
Fznamznon Jul 9, 2024
dade1bb
Fix format
Fznamznon Jul 9, 2024
3bcda80
Merge branch 'main' into long-bitint-align
Fznamznon Jul 9, 2024
28e120e
Check VecTy first
Fznamznon Jul 10, 2024
d00b81a
Use whole number of bytes to represent _BitInt
Fznamznon Jul 10, 2024
fcd8be0
Fix remaining tests
Fznamznon Jul 11, 2024
f43ac1c
Update clang/lib/CodeGen/CodeGenTypes.cpp
Fznamznon Jul 11, 2024
0ec1be5
Add the comment.
Fznamznon Jul 11, 2024
58846f4
Merge branch 'long-bitint-align' of https://github.com/Fznamznon/llvm…
Fznamznon Jul 11, 2024
97e1ef0
Merge branch 'main' into long-bitint-align
Fznamznon Jul 11, 2024
c00be8b
Attempt to fix HIP test
Fznamznon Jul 11, 2024
7956e64
Update clang/lib/CodeGen/CGExprConstant.cpp
Fznamznon Jul 12, 2024
5c4b58b
Fix format
Fznamznon Jul 12, 2024
cd83d6b
Merge branch 'main' into long-bitint-align
Fznamznon Jul 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3863,7 +3863,8 @@ void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI,
LValue ArgVal =
LValue::MakeAddr(ArgAddr, RetTy, getContext(), BaseInfo, TBAAInfo);
EmitStoreOfScalar(
Builder.CreateLoad(ReturnValue), ArgVal, /*isInit*/ true);
EmitLoadOfScalar(MakeAddrLValue(ReturnValue, RetTy), EndLoc), ArgVal,
/*isInit*/ true);
break;
}
}
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/CodeGen/CGDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "clang/Basic/TargetInfo.h"
#include "clang/CodeGen/CGFunctionInfo.h"
#include "clang/Sema/Sema.h"
#include "llvm/Analysis/ConstantFolding.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/GlobalVariable.h"
Expand Down Expand Up @@ -1969,6 +1970,17 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
constant = constWithPadding(CGM, IsPattern::No,
replaceUndef(CGM, isPattern, constant));
}

if (D.getType()->isBitIntType() &&
CGM.getTypes().typeRequiresSplitIntoByteArray(D.getType())) {
// Constants for long _BitInt types are split into individual bytes.
// Try to fold these back into an integer constant so it can be stored
// properly.
llvm::Type *LoadType = CGM.getTypes().convertTypeForLoadStore(
D.getType(), constant->getType());
constant = llvm::ConstantFoldLoadFromConst(
constant, LoadType, llvm::APInt::getZero(32), CGM.getDataLayout());
}
}

if (!constant) {
Expand Down
57 changes: 32 additions & 25 deletions clang/lib/CodeGen/CGExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1986,6 +1986,9 @@ llvm::Value *CodeGenFunction::EmitLoadOfScalar(Address Addr, bool Volatile,
return EmitAtomicLoad(AtomicLValue, Loc).getScalarVal();
}

Addr =
Addr.withElementType(convertTypeForLoadStore(Ty, Addr.getElementType()));

llvm::LoadInst *Load = Builder.CreateLoad(Addr, Volatile);
if (isNontemporal) {
llvm::MDNode *Node = llvm::MDNode::get(
Expand All @@ -2008,27 +2011,33 @@ llvm::Value *CodeGenFunction::EmitLoadOfScalar(Address Addr, bool Volatile,
return EmitFromMemory(Load, Ty);
}

/// Converts a scalar value from its primary IR type (as returned
/// by ConvertType) to its load/store type (as returned by
/// convertTypeForLoadStore).
llvm::Value *CodeGenFunction::EmitToMemory(llvm::Value *Value, QualType Ty) {
// Bool has a different representation in memory than in registers.
if (hasBooleanRepresentation(Ty)) {
// This should really always be an i1, but sometimes it's already
// an i8, and it's awkward to track those cases down.
if (Value->getType()->isIntegerTy(1))
return Builder.CreateZExt(Value, ConvertTypeForMem(Ty), "frombool");
assert(Value->getType()->isIntegerTy(getContext().getTypeSize(Ty)) &&
"wrong value rep of bool");
if (hasBooleanRepresentation(Ty) || Ty->isBitIntType()) {
llvm::Type *StoreTy = convertTypeForLoadStore(Ty, Value->getType());
bool Signed = Ty->isSignedIntegerOrEnumerationType();
return Builder.CreateIntCast(Value, StoreTy, Signed, "storedv");
}

if (Ty->isExtVectorBoolType()) {
llvm::Type *StoreTy = convertTypeForLoadStore(Ty, Value->getType());
// Expand to the memory bit width.
unsigned MemNumElems = StoreTy->getPrimitiveSizeInBits();
// <N x i1> --> <P x i1>.
Value = emitBoolVecConversion(Value, MemNumElems, "insertvec");
// <P x i1> --> iP.
Value = Builder.CreateBitCast(Value, StoreTy);
}

return Value;
}

/// Converts a scalar value from its load/store type (as returned
/// by convertTypeForLoadStore) to its primary IR type (as returned
/// by ConvertType).
llvm::Value *CodeGenFunction::EmitFromMemory(llvm::Value *Value, QualType Ty) {
// Bool has a different representation in memory than in registers.
if (hasBooleanRepresentation(Ty)) {
assert(Value->getType()->isIntegerTy(getContext().getTypeSize(Ty)) &&
"wrong value rep of bool");
return Builder.CreateTrunc(Value, Builder.getInt1Ty(), "tobool");
}
if (Ty->isExtVectorBoolType()) {
const auto *RawIntTy = Value->getType();
// Bitcast iP --> <P x i1>.
Expand All @@ -2041,6 +2050,11 @@ llvm::Value *CodeGenFunction::EmitFromMemory(llvm::Value *Value, QualType Ty) {
return emitBoolVecConversion(V, ValNumElems, "extractvec");
}

if (hasBooleanRepresentation(Ty) || Ty->isBitIntType()) {
llvm::Type *ResTy = ConvertType(Ty);
return Builder.CreateTrunc(Value, ResTy, "loadedv");
}

return Value;
}

Expand Down Expand Up @@ -2093,17 +2107,10 @@ void CodeGenFunction::EmitStoreOfScalar(llvm::Value *Value, Address Addr,
llvm::Type *SrcTy = Value->getType();
if (const auto *ClangVecTy = Ty->getAs<VectorType>()) {
auto *VecTy = dyn_cast<llvm::FixedVectorType>(SrcTy);
if (VecTy && ClangVecTy->isExtVectorBoolType()) {
auto *MemIntTy = cast<llvm::IntegerType>(Addr.getElementType());
// Expand to the memory bit width.
unsigned MemNumElems = MemIntTy->getPrimitiveSizeInBits();
// <N x i1> --> <P x i1>.
Value = emitBoolVecConversion(Value, MemNumElems, "insertvec");
// <P x i1> --> iP.
Value = Builder.CreateBitCast(Value, MemIntTy);
} else if (!CGM.getCodeGenOpts().PreserveVec3Type) {
if (!CGM.getCodeGenOpts().PreserveVec3Type) {
// Handle vec3 special.
if (VecTy && cast<llvm::FixedVectorType>(VecTy)->getNumElements() == 3) {
if (VecTy && !ClangVecTy->isExtVectorBoolType() &&
cast<llvm::FixedVectorType>(VecTy)->getNumElements() == 3) {
// Our source is a vec3, do a shuffle vector to make it a vec4.
Value = Builder.CreateShuffleVector(Value, ArrayRef<int>{0, 1, 2, -1},
"extractVec");
Expand Down Expand Up @@ -2477,7 +2484,7 @@ void CodeGenFunction::EmitStoreThroughLValue(RValue Src, LValue Dst,
void CodeGenFunction::EmitStoreThroughBitfieldLValue(RValue Src, LValue Dst,
llvm::Value **Result) {
const CGBitFieldInfo &Info = Dst.getBitFieldInfo();
llvm::Type *ResLTy = ConvertTypeForMem(Dst.getType());
llvm::Type *ResLTy = convertTypeForLoadStore(Dst.getType());
Address Ptr = Dst.getBitFieldAddress();

// Get the source value, truncated to the width of the bit-field.
Expand Down
57 changes: 44 additions & 13 deletions clang/lib/CodeGen/CGExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ class ConstStructBuilder {
bool AllowOverwrite = false);

bool AppendBitField(const FieldDecl *Field, uint64_t FieldOffset,
llvm::ConstantInt *InitExpr, bool AllowOverwrite = false);
llvm::Constant *InitExpr, bool AllowOverwrite = false);

bool Build(const InitListExpr *ILE, bool AllowOverwrite);
bool Build(const APValue &Val, const RecordDecl *RD, bool IsPrimaryBase,
Expand All @@ -609,9 +609,25 @@ bool ConstStructBuilder::AppendBytes(CharUnits FieldOffsetInChars,
return Builder.add(InitCst, StartOffset + FieldOffsetInChars, AllowOverwrite);
}

bool ConstStructBuilder::AppendBitField(
const FieldDecl *Field, uint64_t FieldOffset, llvm::ConstantInt *CI,
bool AllowOverwrite) {
bool ConstStructBuilder::AppendBitField(const FieldDecl *Field,
uint64_t FieldOffset, llvm::Constant *C,
bool AllowOverwrite) {

llvm::ConstantInt *CI = dyn_cast<llvm::ConstantInt>(C);
if (!CI) {
// Constants for long _BitInt types are sometimes split into individual
// bytes. Try to fold these back into an integer constant. If that doesn't
// work out, then we are trying to initialize a bitfield with a non-trivial
// constant, this must require run-time code.
llvm::Type *LoadType =
CGM.getTypes().convertTypeForLoadStore(Field->getType(), C->getType());
llvm::Constant *FoldedConstant = llvm::ConstantFoldLoadFromConst(
C, LoadType, llvm::APInt::getZero(32), CGM.getDataLayout());
CI = dyn_cast_if_present<llvm::ConstantInt>(FoldedConstant);
if (!CI)
return false;
}

const CGRecordLayout &RL =
CGM.getTypes().getCGRecordLayout(Field->getParent());
const CGBitFieldInfo &Info = RL.getBitFieldInfo(Field);
Expand Down Expand Up @@ -762,15 +778,9 @@ bool ConstStructBuilder::Build(const InitListExpr *ILE, bool AllowOverwrite) {
AllowOverwrite = true;
} else {
// Otherwise we have a bitfield.
if (auto *CI = dyn_cast<llvm::ConstantInt>(EltInit)) {
if (!AppendBitField(Field, Layout.getFieldOffset(FieldNo), CI,
AllowOverwrite))
return false;
} else {
// We are trying to initialize a bitfield with a non-trivial constant,
// this must require run-time code.
if (!AppendBitField(Field, Layout.getFieldOffset(FieldNo), EltInit,
AllowOverwrite))
return false;
}
}
}

Expand Down Expand Up @@ -871,7 +881,7 @@ bool ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD,
} else {
// Otherwise we have a bitfield.
if (!AppendBitField(*Field, Layout.getFieldOffset(FieldNo) + OffsetBits,
cast<llvm::ConstantInt>(EltInit), AllowOverwrite))
EltInit, AllowOverwrite))
return false;
}
}
Expand Down Expand Up @@ -1888,6 +1898,27 @@ llvm::Constant *ConstantEmitter::emitForMemory(CodeGenModule &CGM,
return Res;
}

if (destType->isBitIntType()) {
ConstantAggregateBuilder Builder(CGM);
llvm::Type *LoadStoreTy = CGM.getTypes().convertTypeForLoadStore(destType);
// ptrtoint/inttoptr should not involve _BitInt in constant expressions, so
// casting to ConstantInt is safe here.
auto *CI = cast<llvm::ConstantInt>(C);
llvm::Constant *Res = llvm::ConstantFoldCastOperand(
destType->isSignedIntegerOrEnumerationType() ? llvm::Instruction::SExt
: llvm::Instruction::ZExt,
CI, LoadStoreTy, CGM.getDataLayout());
if (CGM.getTypes().typeRequiresSplitIntoByteArray(destType, C->getType())) {
// Long _BitInt has array of bytes as in-memory type.
// So, split constant into individual bytes.
llvm::Type *DesiredTy = CGM.getTypes().ConvertTypeForMem(destType);
llvm::APInt Value = cast<llvm::ConstantInt>(Res)->getValue();
Builder.addBits(Value, /*OffsetInBits=*/0, /*AllowOverwrite=*/false);
return Builder.build(DesiredTy, /*AllowOversized*/ false);
}
return Res;
}

return C;
}

Expand Down
7 changes: 4 additions & 3 deletions clang/lib/CodeGen/CGExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -436,9 +436,10 @@ class ScalarExprEmitter

if (Value *Result = ConstantEmitter(CGF).tryEmitConstantExpr(E)) {
if (E->isGLValue())
return CGF.Builder.CreateLoad(Address(
Result, CGF.ConvertTypeForMem(E->getType()),
CGF.getContext().getTypeAlignInChars(E->getType())));
return CGF.EmitLoadOfScalar(
Address(Result, CGF.convertTypeForLoadStore(E->getType()),
CGF.getContext().getTypeAlignInChars(E->getType())),
/*Volatile*/ false, E->getType(), E->getExprLoc());
return Result;
}
return Visit(E->getSubExpr());
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/CodeGen/CGRecordLayoutBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,8 +427,7 @@ CGRecordLowering::accumulateBitFields(bool isNonVirtualBaseType,
continue;
}
uint64_t BitOffset = getFieldBitOffset(*Field);
llvm::Type *Type =
Types.ConvertTypeForMem(Field->getType(), /*ForBitField=*/true);
llvm::Type *Type = Types.ConvertTypeForMem(Field->getType());
// If we don't have a run yet, or don't live within the previous run's
// allocated storage then we allocate some storage and start a new run.
if (Run == FieldEnd || BitOffset >= Tail) {
Expand Down
10 changes: 8 additions & 2 deletions clang/lib/CodeGen/CGStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1537,9 +1537,15 @@ void CodeGenFunction::EmitReturnStmt(const ReturnStmt &S) {
Builder.CreateStore(Result.getScalarVal(), ReturnValue);
} else {
switch (getEvaluationKind(RV->getType())) {
case TEK_Scalar:
Builder.CreateStore(EmitScalarExpr(RV), ReturnValue);
case TEK_Scalar: {
llvm::Value *Ret = EmitScalarExpr(RV);
if (CurFnInfo->getReturnInfo().getKind() == ABIArgInfo::Indirect)
EmitStoreOfScalar(Ret, MakeAddrLValue(ReturnValue, RV->getType()),
/*isInit*/ true);
else
Builder.CreateStore(Ret, ReturnValue);
break;
}
case TEK_Complex:
EmitComplexExprIntoLValue(RV, MakeAddrLValue(ReturnValue, RV->getType()),
/*isInit*/ true);
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,11 @@ llvm::Type *CodeGenFunction::ConvertType(QualType T) {
return CGM.getTypes().ConvertType(T);
}

llvm::Type *CodeGenFunction::convertTypeForLoadStore(QualType ASTTy,
llvm::Type *LLVMTy) {
return CGM.getTypes().convertTypeForLoadStore(ASTTy, LLVMTy);
}

TypeEvaluationKind CodeGenFunction::getEvaluationKind(QualType type) {
type = type.getCanonicalType();
while (true) {
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -2576,6 +2576,8 @@ class CodeGenFunction : public CodeGenTypeCache {

llvm::Type *ConvertTypeForMem(QualType T);
llvm::Type *ConvertType(QualType T);
llvm::Type *convertTypeForLoadStore(QualType ASTTy,
llvm::Type *LLVMTy = nullptr);
llvm::Type *ConvertType(const TypeDecl *T) {
return ConvertType(getContext().getTypeDeclType(T));
}
Expand Down
65 changes: 60 additions & 5 deletions clang/lib/CodeGen/CodeGenTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,14 @@ void CodeGenTypes::addRecordTypeName(const RecordDecl *RD,
/// ConvertType in that it is used to convert to the memory representation for
/// a type. For example, the scalar representation for _Bool is i1, but the
/// memory representation is usually i8 or i32, depending on the target.
llvm::Type *CodeGenTypes::ConvertTypeForMem(QualType T, bool ForBitField) {
///
/// We generally assume that the alloc size of this type under the LLVM
/// data layout is the same as the size of the AST type. The alignment
/// does not have to match: Clang should always use explicit alignments
/// and packed structs as necessary to produce the layout it needs.
/// But the size does need to be exactly right or else things like struct
/// layout will break.
llvm::Type *CodeGenTypes::ConvertTypeForMem(QualType T) {
if (T->isConstantMatrixType()) {
const Type *Ty = Context.getCanonicalType(T).getTypePtr();
const ConstantMatrixType *MT = cast<ConstantMatrixType>(Ty);
Expand All @@ -107,17 +114,65 @@ llvm::Type *CodeGenTypes::ConvertTypeForMem(QualType T, bool ForBitField) {
return llvm::IntegerType::get(FixedVT->getContext(), BytePadded);
}

// If this is a bool type, or a bit-precise integer type in a bitfield
// representation, map this integer to the target-specified size.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep this comment; we just need to update it a little:

  // If T is _Bool or a _BitInt type, ConvertType will produce an IR type
  // with the exact semantic bit-width of the AST type; for example,
  // _BitInt(17) will turn into i17.  In memory, however, we need to store
  // such values extended to their full storage size as decided by AST
  // layout; this is an ABI requirement.  Ideally, we would always use an
  // integer type that's just the bit-size of the AST type; for example, if
  // sizeof(_BitInt(17)) == 4, _BitInt(17) would turn into i32.  That is what's
  // returned by convertTypeForLoadStore.  However, that type does not
  // always satisfy the size requirement on memory representation types
  // describe above.  For example, a 32-bit platform might reasonably set
  // sizeof(_BitInt(65)) == 12, but i96 is likely to have to have an alloc size
  // of 16 bytes in the LLVM data layout.  In these cases, we simply return
  // a byte array of the appropriate size.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added, thanks.

if ((ForBitField && T->isBitIntType()) ||
(!T->isBitIntType() && R->isIntegerTy(1)))
// If T is _Bool or a _BitInt type, ConvertType will produce an IR type
// with the exact semantic bit-width of the AST type; for example,
// _BitInt(17) will turn into i17. In memory, however, we need to store
// such values extended to their full storage size as decided by AST
// layout; this is an ABI requirement. Ideally, we would always use an
// integer type that's just the bit-size of the AST type; for example, if
// sizeof(_BitInt(17)) == 4, _BitInt(17) would turn into i32. That is what's
// returned by convertTypeForLoadStore. However, that type does not
// always satisfy the size requirement on memory representation types
// describe above. For example, a 32-bit platform might reasonably set
// sizeof(_BitInt(65)) == 12, but i96 is likely to have to have an alloc size
// of 16 bytes in the LLVM data layout. In these cases, we simply return
// a byte array of the appropriate size.
if (T->isBitIntType()) {
if (typeRequiresSplitIntoByteArray(T, R))
return llvm::ArrayType::get(CGM.Int8Ty,
Context.getTypeSizeInChars(T).getQuantity());
return llvm::IntegerType::get(getLLVMContext(),
(unsigned)Context.getTypeSize(T));
}

if (R->isIntegerTy(1))
return llvm::IntegerType::get(getLLVMContext(),
(unsigned)Context.getTypeSize(T));

// Else, don't map it.
return R;
}

bool CodeGenTypes::typeRequiresSplitIntoByteArray(QualType ASTTy,
llvm::Type *LLVMTy) {
if (!LLVMTy)
LLVMTy = ConvertType(ASTTy);

CharUnits ASTSize = Context.getTypeSizeInChars(ASTTy);
CharUnits LLVMSize =
CharUnits::fromQuantity(getDataLayout().getTypeAllocSize(LLVMTy));
return ASTSize != LLVMSize;
}

llvm::Type *CodeGenTypes::convertTypeForLoadStore(QualType T,
llvm::Type *LLVMTy) {
if (!LLVMTy)
LLVMTy = ConvertType(T);

if (T->isBitIntType())
return llvm::Type::getIntNTy(
getLLVMContext(), Context.getTypeSizeInChars(T).getQuantity() * 8);

if (LLVMTy->isIntegerTy(1))
return llvm::IntegerType::get(getLLVMContext(),
(unsigned)Context.getTypeSize(T));

if (T->isExtVectorBoolType())
return ConvertTypeForMem(T);

return LLVMTy;
}

/// isRecordLayoutComplete - Return true if the specified type is already
/// completely laid out.
bool CodeGenTypes::isRecordLayoutComplete(const Type *Ty) const {
Expand Down
Loading