Skip to content

Commit

Permalink
[clang, SystemZ] Support -munaligned-symbols (#73511)
Browse files Browse the repository at this point in the history
When this option is passed to clang, external (and/or weak) symbols
are not assumed to have the minimum ABI alignment normally required.
Symbols defined locally that are not weak are however still given the
minimum alignment.

This is implemented by passing a new parameter to getMinGlobalAlign()
named HasNonWeakDef that is used to return the right alignment value.

This is needed when external symbols created from a linker script may
not get the ABI minimum alignment and must therefore be treated as
unaligned by the compiler.
  • Loading branch information
JonPsson1 committed Jan 27, 2024
1 parent 718aac9 commit 34dd8ec
Show file tree
Hide file tree
Showing 17 changed files with 195 additions and 23 deletions.
14 changes: 10 additions & 4 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -2413,12 +2413,18 @@ class ASTContext : public RefCountedBase<ASTContext> {
unsigned getTargetDefaultAlignForAttributeAligned() const;

/// Return the alignment in bits that should be given to a
/// global variable with type \p T.
unsigned getAlignOfGlobalVar(QualType T) const;
/// global variable with type \p T. If \p VD is non-null it will be
/// considered specifically for the query.
unsigned getAlignOfGlobalVar(QualType T, const VarDecl *VD) const;

/// Return the alignment in characters that should be given to a
/// global variable with type \p T.
CharUnits getAlignOfGlobalVarInChars(QualType T) const;
/// global variable with type \p T. If \p VD is non-null it will be
/// considered specifically for the query.
CharUnits getAlignOfGlobalVarInChars(QualType T, const VarDecl *VD) const;

/// Return the minimum alignement as specified by the target. If \p VD is
/// non-null it may be used to identify external or weak variables.
unsigned getMinGlobalAlignOfVar(uint64_t Size, const VarDecl *VD) const;

/// Return a conservative estimate of the alignment of the specified
/// decl \p D.
Expand Down
6 changes: 4 additions & 2 deletions clang/include/clang/Basic/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -704,8 +704,10 @@ class TargetInfo : public TransferrableTargetInfo,
}

/// getMinGlobalAlign - Return the minimum alignment of a global variable,
/// unless its alignment is explicitly reduced via attributes.
virtual unsigned getMinGlobalAlign (uint64_t) const {
/// unless its alignment is explicitly reduced via attributes. If \param
/// HasNonWeakDef is true, this concerns a VarDecl which has a definition
/// in current translation unit and that is not weak.
virtual unsigned getMinGlobalAlign(uint64_t Size, bool HasNonWeakDef) const {
return MinGlobalAlign;
}

Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -4619,6 +4619,10 @@ def munaligned_access : Flag<["-"], "munaligned-access">, Group<m_Group>,
HelpText<"Allow memory accesses to be unaligned (AArch32/AArch64/LoongArch/RISC-V only)">;
def mno_unaligned_access : Flag<["-"], "mno-unaligned-access">, Group<m_Group>,
HelpText<"Force all memory accesses to be aligned (AArch32/AArch64/LoongArch/RISC-V only)">;
def munaligned_symbols : Flag<["-"], "munaligned-symbols">, Group<m_Group>,
HelpText<"Expect external char-aligned symbols to be without ABI alignment (SystemZ only)">;
def mno_unaligned_symbols : Flag<["-"], "mno-unaligned-symbols">, Group<m_Group>,
HelpText<"Expect external char-aligned symbols to be without ABI alignment (SystemZ only)">;
} // let Flags = [TargetSpecific]
def mstrict_align : Flag<["-"], "mstrict-align">, Alias<mno_unaligned_access>,
Flags<[HelpHidden]>, Visibility<[ClangOption, CC1Option]>,
Expand Down
19 changes: 14 additions & 5 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1688,7 +1688,7 @@ CharUnits ASTContext::getDeclAlign(const Decl *D, bool ForAlignof) const {
if (VD->hasGlobalStorage() && !ForAlignof) {
uint64_t TypeSize =
!BaseT->isIncompleteType() ? getTypeSize(T.getTypePtr()) : 0;
Align = std::max(Align, getTargetInfo().getMinGlobalAlign(TypeSize));
Align = std::max(Align, getMinGlobalAlignOfVar(TypeSize, VD));
}

// Fields can be subject to extra alignment constraints, like if
Expand Down Expand Up @@ -2511,16 +2511,25 @@ unsigned ASTContext::getTargetDefaultAlignForAttributeAligned() const {

/// getAlignOfGlobalVar - Return the alignment in bits that should be given
/// to a global variable of the specified type.
unsigned ASTContext::getAlignOfGlobalVar(QualType T) const {
unsigned ASTContext::getAlignOfGlobalVar(QualType T, const VarDecl *VD) const {
uint64_t TypeSize = getTypeSize(T.getTypePtr());
return std::max(getPreferredTypeAlign(T),
getTargetInfo().getMinGlobalAlign(TypeSize));
getMinGlobalAlignOfVar(TypeSize, VD));
}

/// getAlignOfGlobalVarInChars - Return the alignment in characters that
/// should be given to a global variable of the specified type.
CharUnits ASTContext::getAlignOfGlobalVarInChars(QualType T) const {
return toCharUnitsFromBits(getAlignOfGlobalVar(T));
CharUnits ASTContext::getAlignOfGlobalVarInChars(QualType T,
const VarDecl *VD) const {
return toCharUnitsFromBits(getAlignOfGlobalVar(T, VD));
}

unsigned ASTContext::getMinGlobalAlignOfVar(uint64_t Size,
const VarDecl *VD) const {
// Make the default handling as that of a non-weak definition in the
// current translation unit.
bool HasNonWeakDef = !VD || (VD->hasDefinition() && !VD->isWeak());
return getTargetInfo().getMinGlobalAlign(Size, HasNonWeakDef);
}

CharUnits ASTContext::getOffsetOfBaseWithVBPtr(const CXXRecordDecl *RD) const {
Expand Down
6 changes: 4 additions & 2 deletions clang/lib/Basic/Targets/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1517,8 +1517,10 @@ MicrosoftARM64TargetInfo::getCallingConvKind(bool ClangABICompat4) const {
return CCK_MicrosoftWin64;
}

unsigned MicrosoftARM64TargetInfo::getMinGlobalAlign(uint64_t TypeSize) const {
unsigned Align = WindowsARM64TargetInfo::getMinGlobalAlign(TypeSize);
unsigned MicrosoftARM64TargetInfo::getMinGlobalAlign(uint64_t TypeSize,
bool HasNonWeakDef) const {
unsigned Align =
WindowsARM64TargetInfo::getMinGlobalAlign(TypeSize, HasNonWeakDef);

// MSVC does size based alignment for arm64 based on alignment section in
// below document, replicate that to keep alignment consistent with object
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Basic/Targets/AArch64.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,8 @@ class LLVM_LIBRARY_VISIBILITY MicrosoftARM64TargetInfo
TargetInfo::CallingConvKind
getCallingConvKind(bool ClangABICompat4) const override;

unsigned getMinGlobalAlign(uint64_t TypeSize) const override;
unsigned getMinGlobalAlign(uint64_t TypeSize,
bool HasNonWeakDef) const override;
};

// ARM64 MinGW target
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Basic/Targets/CSKY.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,8 @@ bool CSKYTargetInfo::validateAsmConstraint(
}
}

unsigned CSKYTargetInfo::getMinGlobalAlign(uint64_t Size) const {
unsigned CSKYTargetInfo::getMinGlobalAlign(uint64_t Size,
bool HasNonWeakDef) const {
if (Size >= 32)
return 32;
return 0;
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Basic/Targets/CSKY.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class LLVM_LIBRARY_VISIBILITY CSKYTargetInfo : public TargetInfo {

bool isValidCPUName(StringRef Name) const override;

unsigned getMinGlobalAlign(uint64_t) const override;
unsigned getMinGlobalAlign(uint64_t, bool HasNonWeakDef) const override;

ArrayRef<Builtin::Info> getTargetBuiltins() const override;

Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Basic/Targets/NVPTX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ NVPTXTargetInfo::NVPTXTargetInfo(const llvm::Triple &Triple,
LongAlign = HostTarget->getLongAlign();
LongLongWidth = HostTarget->getLongLongWidth();
LongLongAlign = HostTarget->getLongLongAlign();
MinGlobalAlign = HostTarget->getMinGlobalAlign(/* TypeSize = */ 0);
MinGlobalAlign = HostTarget->getMinGlobalAlign(/* TypeSize = */ 0,
/* HasNonWeakDef = */ true);
NewAlign = HostTarget->getNewAlign();
DefaultAlignForAttributeAligned =
HostTarget->getDefaultAlignForAttributeAligned();
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Basic/Targets/SPIR.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,9 @@ class LLVM_LIBRARY_VISIBILITY BaseSPIRTargetInfo : public TargetInfo {
LongAlign = HostTarget->getLongAlign();
LongLongWidth = HostTarget->getLongLongWidth();
LongLongAlign = HostTarget->getLongLongAlign();
MinGlobalAlign = HostTarget->getMinGlobalAlign(/* TypeSize = */ 0);
MinGlobalAlign =
HostTarget->getMinGlobalAlign(/* TypeSize = */ 0,
/* HasNonWeakDef = */ true);
NewAlign = HostTarget->getNewAlign();
DefaultAlignForAttributeAligned =
HostTarget->getDefaultAlignForAttributeAligned();
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/Basic/Targets/SystemZ.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,16 @@ bool SystemZTargetInfo::hasFeature(StringRef Feature) const {
.Default(false);
}

unsigned SystemZTargetInfo::getMinGlobalAlign(uint64_t Size,
bool HasNonWeakDef) const {
// Don't enforce the minimum alignment on an external or weak symbol if
// -munaligned-symbols is passed.
if (UnalignedSymbols && !HasNonWeakDef)
return 0;

return MinGlobalAlign;
}

void SystemZTargetInfo::getTargetDefines(const LangOptions &Opts,
MacroBuilder &Builder) const {
Builder.defineMacro("__s390__");
Expand Down
9 changes: 8 additions & 1 deletion clang/lib/Basic/Targets/SystemZ.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ class LLVM_LIBRARY_VISIBILITY SystemZTargetInfo : public TargetInfo {
bool HasTransactionalExecution;
bool HasVector;
bool SoftFloat;
bool UnalignedSymbols;

public:
SystemZTargetInfo(const llvm::Triple &Triple, const TargetOptions &)
: TargetInfo(Triple), CPU("z10"), ISARevision(8),
HasTransactionalExecution(false), HasVector(false), SoftFloat(false) {
HasTransactionalExecution(false), HasVector(false), SoftFloat(false),
UnalignedSymbols(false) {
IntMaxType = SignedLong;
Int64Type = SignedLong;
IntWidth = IntAlign = 32;
Expand Down Expand Up @@ -64,6 +66,8 @@ class LLVM_LIBRARY_VISIBILITY SystemZTargetInfo : public TargetInfo {
HasStrictFP = true;
}

unsigned getMinGlobalAlign(uint64_t Size, bool HasNonWeakDef) const override;

void getTargetDefines(const LangOptions &Opts,
MacroBuilder &Builder) const override;

Expand Down Expand Up @@ -163,13 +167,16 @@ class LLVM_LIBRARY_VISIBILITY SystemZTargetInfo : public TargetInfo {
HasTransactionalExecution = false;
HasVector = false;
SoftFloat = false;
UnalignedSymbols = false;
for (const auto &Feature : Features) {
if (Feature == "+transactional-execution")
HasTransactionalExecution = true;
else if (Feature == "+vector")
HasVector = true;
else if (Feature == "+soft-float")
SoftFloat = true;
else if (Feature == "+unaligned-symbols")
UnalignedSymbols = true;
}
HasVector &= !SoftFloat;

Expand Down
7 changes: 4 additions & 3 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6330,7 +6330,8 @@ GenerateStringLiteral(llvm::Constant *C, llvm::GlobalValue::LinkageTypes LT,
ConstantAddress
CodeGenModule::GetAddrOfConstantStringFromLiteral(const StringLiteral *S,
StringRef Name) {
CharUnits Alignment = getContext().getAlignOfGlobalVarInChars(S->getType());
CharUnits Alignment =
getContext().getAlignOfGlobalVarInChars(S->getType(), /*VD=*/nullptr);

llvm::Constant *C = GetConstantArrayFromStringLiteral(S);
llvm::GlobalVariable **Entry = nullptr;
Expand Down Expand Up @@ -6393,8 +6394,8 @@ CodeGenModule::GetAddrOfConstantStringFromObjCEncode(const ObjCEncodeExpr *E) {
ConstantAddress CodeGenModule::GetAddrOfConstantCString(
const std::string &Str, const char *GlobalName) {
StringRef StrWithNull(Str.c_str(), Str.size() + 1);
CharUnits Alignment =
getContext().getAlignOfGlobalVarInChars(getContext().CharTy);
CharUnits Alignment = getContext().getAlignOfGlobalVarInChars(
getContext().CharTy, /*VD=*/nullptr);

llvm::Constant *C =
llvm::ConstantDataArray::getString(getLLVMContext(), StrWithNull, false);
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/Driver/ToolChains/Arch/SystemZ.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,12 @@ void systemz::getSystemZTargetFeatures(const Driver &D, const ArgList &Args,
systemz::FloatABI FloatABI = systemz::getSystemZFloatABI(D, Args);
if (FloatABI == systemz::FloatABI::Soft)
Features.push_back("+soft-float");

if (const Arg *A = Args.getLastArg(options::OPT_munaligned_symbols,
options::OPT_mno_unaligned_symbols)) {
if (A->getOption().matches(options::OPT_munaligned_symbols))
Features.push_back("+unaligned-symbols");
else
Features.push_back("-unaligned-symbols");
}
}
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2295,7 +2295,7 @@ bool Sema::isOpenMPCapturedByRef(const ValueDecl *D, unsigned Level,
// instead.
if (!IsByRef && (Ctx.getTypeSizeInChars(Ty) >
Ctx.getTypeSizeInChars(Ctx.getUIntPtrType()) ||
Ctx.getAlignOfGlobalVarInChars(Ty) >
Ctx.getAlignOfGlobalVarInChars(Ty, dyn_cast<VarDecl>(D)) >
Ctx.getTypeAlignInChars(Ctx.getUIntPtrType()))) {
IsByRef = true;
}
Expand Down
113 changes: 113 additions & 0 deletions clang/test/CodeGen/SystemZ/unaligned-symbols.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// RUN: %clang -target s390x-linux-gnu %s -o - -emit-llvm -S \
// RUN: | FileCheck %s -check-prefixes=CHECK,ALIGNED

// RUN: %clang -target s390x-linux-gnu %s -o - -emit-llvm -S \
// RUN: -mno-unaligned-symbols | FileCheck %s -check-prefixes=CHECK,ALIGNED

// RUN: %clang -target s390x-linux-gnu %s -o - -emit-llvm -S \
// RUN: -munaligned-symbols | FileCheck %s -check-prefixes=CHECK,UNALIGN

// RUN: %clang -cc1 -triple s390x-linux-gnu %s -o - -emit-llvm \
// RUN: -target-feature +unaligned-symbols | FileCheck %s -check-prefixes=CHECK,UNALIGN


// With -munaligned-symbols, the external and unaligned ("ExtUnal...")
// variable of each test should be treated as unaligned. For the explicitly
// aligned ("ExtExplAlign...") variables and those defined in the translation
// unit ("Aligned..."), the normal ABI alignment of 2 should still be
// in effect.

// ALIGNED: @ExtUnal = external global i8, align 2
// UNALIGN: @ExtUnal = external global i8, align 1
// CHECK: @ExtExplAlign = external global i8, align 2
// CHECK: @Aligned = {{(dso_local )?}}global i8 0, align 2
extern unsigned char ExtUnal;
extern unsigned char ExtExplAlign __attribute__((aligned(2)));
unsigned char Aligned;
unsigned char foo0 () {
return ExtUnal + ExtExplAlign + Aligned;
}

// ALIGNED: @ExtUnal_c2Arr = external global [2 x i8], align 2
// UNALIGN: @ExtUnal_c2Arr = external global [2 x i8], align 1
// CHECK: @ExtExplAlign_c2Arr = external global [2 x i8], align 2
// CHECK: @Aligned_c2Arr = {{(dso_local )?}}global [2 x i8] zeroinitializer, align 2
extern unsigned char ExtUnal_c2Arr[2];
extern unsigned char ExtExplAlign_c2Arr[2] __attribute__((aligned(2)));
unsigned char Aligned_c2Arr[2];
unsigned char foo1 () {
return ExtUnal_c2Arr[0] + ExtExplAlign_c2Arr[0] + Aligned_c2Arr[0];
}

// ALIGNED: @ExtUnal_s1c = external global %struct.s1c, align 2
// UNALIGN: @ExtUnal_s1c = external global %struct.s1c, align 1
// CHECK: @ExtExplAlign_s1c = external global %struct.s1c, align 2
// CHECK: @Aligned_s1c = {{(dso_local )?}}global %struct.s1c zeroinitializer, align 2
struct s1c { char c; };
extern struct s1c ExtUnal_s1c;
extern struct s1c ExtExplAlign_s1c __attribute__((aligned(2)));
struct s1c Aligned_s1c;
unsigned char foo2 () {
return ExtUnal_s1c.c + ExtExplAlign_s1c.c + Aligned_s1c.c;
}

// ALIGNED: @ExtUnal_s2c = external global %struct.s2c, align 2
// UNALIGN: @ExtUnal_s2c = external global %struct.s2c, align 1
// CHECK: @ExtExplAlign_s2c = external global %struct.s2c, align 2
// CHECK: @Aligned_s2c = {{(dso_local )?}}global %struct.s2c zeroinitializer, align 2
struct s2c { char c; char c1;};
extern struct s2c ExtUnal_s2c;
extern struct s2c ExtExplAlign_s2c __attribute__((aligned(2)));
struct s2c Aligned_s2c;
unsigned char foo3 () {
return ExtUnal_s2c.c + ExtExplAlign_s2c.c + Aligned_s2c.c;
}

// ALIGNED: @ExtUnal_s_c2Arr = external global %struct.s_c2Arr, align 2
// UNALIGN: @ExtUnal_s_c2Arr = external global %struct.s_c2Arr, align 1
// CHECK: @ExtExplAlign_s_c2Arr = external global %struct.s_c2Arr, align 2
// CHECK: @Aligned_s_c2Arr = {{(dso_local )?}}global %struct.s_c2Arr zeroinitializer, align 2
struct s_c2Arr { char c[2]; };
extern struct s_c2Arr ExtUnal_s_c2Arr;
extern struct s_c2Arr ExtExplAlign_s_c2Arr __attribute__((aligned(2)));
struct s_c2Arr Aligned_s_c2Arr;
unsigned char foo4 () {
return ExtUnal_s_c2Arr.c[0] + ExtExplAlign_s_c2Arr.c[0] + Aligned_s_c2Arr.c[0];
}

// ALIGNED: @ExtUnal_s_packed = external global %struct.s_packed, align 2
// UNALIGN: @ExtUnal_s_packed = external global %struct.s_packed, align 1
// CHECK: @ExtExplAlign_s_packed = external global %struct.s_packed, align 2
// CHECK: @Aligned_s_packed = {{(dso_local )?}}global %struct.s_packed zeroinitializer, align 2
struct s_packed {
int __attribute__((__packed__)) i;
char c;
};
extern struct s_packed ExtUnal_s_packed;
extern struct s_packed ExtExplAlign_s_packed __attribute__((aligned(2)));
struct s_packed Aligned_s_packed;
unsigned char foo5 () {
return ExtUnal_s_packed.c + ExtExplAlign_s_packed.c + Aligned_s_packed.c;
}

// ALIGNED: @ExtUnAl_s_nested = external global [2 x %struct.s_nested], align 2
// UNALIGN: @ExtUnAl_s_nested = external global [2 x %struct.s_nested], align 1
// CHECK: @ExtExplAlign_s_nested = external global [2 x %struct.s_nested], align 2
// CHECK: @Aligned_s_nested = {{(dso_local )?}}global [2 x %struct.s_nested] zeroinitializer, align 2
struct s_nested { struct s_c2Arr a[2]; };
extern struct s_nested ExtUnAl_s_nested[2];
extern struct s_nested ExtExplAlign_s_nested[2] __attribute__((aligned(2)));
struct s_nested Aligned_s_nested[2];
unsigned char foo6 () {
return ExtUnAl_s_nested[0].a[0].c[0] + ExtExplAlign_s_nested[0].a[0].c[0] +
Aligned_s_nested[0].a[0].c[0];
}

// A weak symbol could be replaced with an unaligned one at link time.
// CHECK-LABEL: foo7
// ALIGNED: %0 = load i8, ptr @Weaksym, align 2
// UNALIGN: %0 = load i8, ptr @Weaksym, align 1
unsigned char __attribute__((weak)) Weaksym = 0;
unsigned char foo7 () {
return Weaksym;
}
5 changes: 5 additions & 0 deletions llvm/lib/Target/SystemZ/SystemZFeatures.td
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ def FeatureBackChain : SystemZFeature<
"Store the address of the caller's frame into the callee's stack frame"
>;

def FeatureUnalignedSymbols : SystemZFeature<
"unaligned-symbols", "UnalignedSymbols", (all_of FeatureUnalignedSymbols),
"Don't apply the ABI minimum alignment to external symbols."
>;

//===----------------------------------------------------------------------===//
//
// New features added in the Ninth Edition of the z/Architecture
Expand Down

0 comments on commit 34dd8ec

Please sign in to comment.