Alignment of globals and target datalayout

Hi,

I’ve run into some interesting behaviour with how globals are aligned - it seems like the alignment preferences specified in the target datalayout are being ignored. Example:

$ cat test.c
void f(const char *);
void g(const char *c) {
const char *b = “jgksj”;
return f(b + 1);
}

$ clang -target arm-none-linux-gnueabi -mcpu=cortex-m0 test.c -S -emit-llvm -o - -O0
; ModuleID = ‘test.c’
target datalayout = “e-m:e-p:32:32-i1:8:32-i8:8:32-i16:16:32-i64:64-v128:64:128-a:0:32-n32-S64”
target triple = “thumbv6m-none-linux-gnueabi”

@.str = private unnamed_addr constant [6 x i8] c"jgksj\00", align 1

(…)

Shouldn’t @.str be aligned to the preferred alignment of i8, i.e. 4?

If I set MinGlobalAlignment for my target to 32, @.str is aligned to 4 bytes (as expected). This leads me to believe that the behaviour is caused by ASTContext::getAlignOfGlobalVar, which in turn calls ASTContext::getTypeAlign - and from there gets the ABI alignment (which of course is 8 bits / 1 in this case).

I imagine a fix would do something like

unsigned ASTContext::getAlignOfGlobalVar(QualType T) const {
return std::max(getPreferredTypeAlign(T.getTypePtr()), getTargetInfo().getMinGlobalAlign());
}

However, I’d then also need to change getPreferredTypeAlign so it actually considers what the target specifies as the preferred alignment, not just the ABI align and type size. Is this the right place to make changes? Or is all this intended behaviour?

Any opinions would be greatly appreciated.

Thanks
Moritz

Hi,

I've run into some interesting behaviour with how globals are aligned - it
seems like the alignment preferences specified in the target datalayout are
being ignored. Example:

$ cat test.c
void f(const char *);
void g(const char *c) {
  const char *b = "jgksj";
  return f(b + 1);
}

$ clang -target arm-none-linux-gnueabi -mcpu=cortex-m0 test.c -S -emit-llvm
-o - -O0
; ModuleID = 'test.c'
target datalayout =
"e-m:e-p:32:32-i1:8:32-i8:8:32-i16:16:32-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "thumbv6m-none-linux-gnueabi"

@.str = private unnamed_addr constant [6 x i8] c"jgksj\00", align 1

(...)

Shouldn't @.str be aligned to the preferred alignment of i8, i.e. 4?

If I set MinGlobalAlignment for my target to 32, @.str is aligned to 4 bytes
(as expected). This leads me to believe that the behaviour is caused by
ASTContext::getAlignOfGlobalVar, which in turn calls
ASTContext::getTypeAlign - and from there gets the ABI alignment (which of
course is 8 bits / 1 in this case).

I imagine a fix would do something like

unsigned ASTContext::getAlignOfGlobalVar(QualType T) const {
  return std::max(getPreferredTypeAlign(T.getTypePtr()),
getTargetInfo().getMinGlobalAlign());
}

However, I'd then also need to change getPreferredTypeAlign so it actually
considers what the target specifies as the preferred alignment, not just the
ABI align and type size. Is this the right place to make changes? Or is all
this intended behaviour?

I think it is safe, but changing getAlignOfGlobalVar to use the
preferred alignment would be a big change for all targets, so it would
need a really large benchmarking run. I think it would also prevent
the linker from merging global strings.

Any opinions would be greatly appreciated.

Thanks
Moritz

Cheers,
Rafael

Hi Rafael,

First of all thanks for your input -

I think it is safe, but changing getAlignOfGlobalVar to use the
preferred alignment would be a big change for all targets, so it would
need a really large benchmarking run.
Agreed, this would be a big change. Perhaps it should be target-specific - i.e modifying ASTContext::get[Preferred]TypeAlign so the behaviour only changes for certain targets - although at the moment the front-end is quite limited in that regard as it only has access to ASTContext::TargetInfo, which doesn’t contain much relevant data.

I think it would also prevent the linker from merging global strings.

Due to the alignment change it would at least be restricted (e.g. in Thumb1, the maximum offset from a global is 127). On the other hand, allowing an increased alignment allows other optimisations in the back-end such as inlining calls to strcpy/memcpy - LDM/IA has 32-bit alignment requirements.

I think this issue is only really relevant to architectures which don’t have extensive support for unaligned memory accesses, and thus a solution shouldn’t change the behaviour on most platforms. For now, it might be best to leave this alone and simply set a minimum alignment for the specific target, and then investigate further from there.

Cheers
Moritz