Skip to content

Conversation

pcc
Copy link
Contributor

@pcc pcc commented Jul 18, 2025

No description provided.

Created using spr 1.3.6-beta.1
@llvmbot
Copy link
Member

llvmbot commented Jul 18, 2025

@llvm/pr-subscribers-backend-x86

Author: Peter Collingbourne (pcc)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/149444.diff

2 Files Affected:

  • (modified) llvm/lib/CodeGen/MachineFunction.cpp (+1-2)
  • (added) llvm/test/CodeGen/X86/function-align.ll (+18)
diff --git a/llvm/lib/CodeGen/MachineFunction.cpp b/llvm/lib/CodeGen/MachineFunction.cpp
index 38ad582ba923c..429a17a9113d3 100644
--- a/llvm/lib/CodeGen/MachineFunction.cpp
+++ b/llvm/lib/CodeGen/MachineFunction.cpp
@@ -211,9 +211,8 @@ void MachineFunction::init() {
   ConstantPool = new (Allocator) MachineConstantPool(getDataLayout());
   Alignment = STI->getTargetLowering()->getMinFunctionAlignment();
 
-  // FIXME: Shouldn't use pref alignment if explicit alignment is set on F.
   // FIXME: Use Function::hasOptSize().
-  if (!F.hasFnAttribute(Attribute::OptimizeForSize))
+  if (!F.getAlign() && !F.hasFnAttribute(Attribute::OptimizeForSize))
     Alignment = std::max(Alignment,
                          STI->getTargetLowering()->getPrefFunctionAlignment());
 
diff --git a/llvm/test/CodeGen/X86/function-align.ll b/llvm/test/CodeGen/X86/function-align.ll
new file mode 100644
index 0000000000000..11d0e99929927
--- /dev/null
+++ b/llvm/test/CodeGen/X86/function-align.ll
@@ -0,0 +1,18 @@
+; RUN: llc -function-sections < %s | FileCheck %s
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+; CHECK: .section .text.f1
+; CHECK-NOT: .p2align
+; CHECK: f1:
+define void @f1() align 1 {
+  ret void
+}
+
+; CHECK: .section .text.f2
+; CHECK-NEXT: .globl f2
+; CHECK-NEXT: .p2align 1
+define void @f2() align 2 {
+  ret void
+}

@pcc pcc requested review from arsenm and efriedma-quic July 18, 2025 03:45
// FIXME: Use Function::hasOptSize().
if (!F.hasFnAttribute(Attribute::OptimizeForSize))
if (!F.getAlign() && !F.hasFnAttribute(Attribute::OptimizeForSize))
Copy link
Contributor

Choose a reason for hiding this comment

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

This will also disrespect getMinFunctionAlignment which is probably more of a problem

Copy link
Contributor

Choose a reason for hiding this comment

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

Applying this in the function constructor also seems like the wrong place to do this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't see why it doesn't respect getMinFunctionAlignment. getMinFunctionAlignment is read on line 212 and combined with the preferred alignment with std::max here, so the result will be at least getMinFunctionAlignment.

@pcc pcc merged commit 9878ef3 into main Jul 18, 2025
12 checks passed
@pcc pcc deleted the users/pcc/spr/codegen-respect-function-align-attribute-if-less-than-preferred-alignment branch July 18, 2025 20:33
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Jul 18, 2025
…ferred alignment.

Reviewers: arsenm, efriedma-quic

Reviewed By: arsenm

Pull Request: llvm/llvm-project#149444
@efriedma-quic
Copy link
Collaborator

This has a side-effect you might not want.

Due to the way member function pointers work in the Itanium ABI, all member functions must have at least 2 byte alignment. clang represents this by marking each member function "align 2". This used to allow increasing the alignment if it wasn't specified, but with this patch, all member functions have exactly 2-byte alignment.

@pcc
Copy link
Contributor Author

pcc commented Jul 18, 2025

Interesting. I had not considered that consequence.

I think longer term we will want a way for frontends to query the subtarget's minimum and preferred alignment. Then here clang could query the right one based on whether it's optimizing for size and set the align attribute to the max of that and 2.

Maybe for now we can introduce a min-align attribute and have clang set it to 2 on member functions.

@efriedma-quic
Copy link
Collaborator

The alignment is queried directly by optimizations through getPointerAlignment; I don't think we can decrease it after the fact.

I'd suggest adding an attribute in the opposite direction; for functions where we don't want the alignment to increase, add a function attribute "minimize-alignment", and the backend won't increase alignment for those functions.

@efriedma-quic
Copy link
Collaborator

Oh, also, on the subject of function alignment, we currently still have

// If the function alignment is not specified then assume that it
. We should update the x86 datalayout so that hack isn't necessary, but it hasn't been a priority for me.

@pcc
Copy link
Contributor Author

pcc commented Jul 19, 2025

The alignment is queried directly by optimizations through getPointerAlignment; I don't think we can decrease it after the fact.

The idea is that min-align would only be capable of increasing the function alignment, not decreasing it. For example, given a function with min-align 2, on x86 with -Os we would end up with alignment 2 and x86 without -Os would have alignment 16, and on aarch64 the alignment would still always be at least 4.

@efriedma-quic
Copy link
Collaborator

The idea is that min-align would only be capable of increasing the function alignment, not decreasing it.

Then getPointerAlignment() would be overly conservative, which would prevent us from removing masking operations. Unless you make getPointerAlignment() also check for the attribute, which... technically works, I guess, but is pretty confusing.

Longer-term, I'd prefer to switch Function::getAlign() to return an Align instead of a MaybeAlign.

@pcc
Copy link
Contributor Author

pcc commented Jul 23, 2025

Then getPointerAlignment() would be overly conservative, which would prevent us from removing masking operations. Unless you make getPointerAlignment() also check for the attribute, which... technically works, I guess, but is pretty confusing.

Another consideration is that we may want the object file to have a separate minimum alignment and preferred alignment for a section. This supports the CFI jump table use case that I'm working on, see #149448 and #150151, but I imagine it could also be useful for other section placement techniques. If we did go with the different alignments in the object file I would expect this to be represented using two attributes at the IR level which would be respected with getPointerAlignment().

Longer-term, I'd prefer to switch Function::getAlign() to return an Align instead of a MaybeAlign.

That seems like a good direction to me.

@zmodem
Copy link
Collaborator

zmodem commented Aug 20, 2025

clang represents this by marking each member function "align 2". This used to allow increasing the alignment if it wasn't specified, but with this patch, all member functions have exactly 2-byte alignment.

Interesting!

For reference, this happens here:

// Some C++ ABIs require 2-byte alignment for member functions, in order to
// reserve a bit for differentiating between virtual and non-virtual member
// functions. If the current target's C++ ABI requires this and this is a
// member function, set its alignment accordingly.
if (getTarget().getCXXABI().areMemberFunctionsAligned()) {
if (isa<CXXMethodDecl>(D) && F->getPointerAlignment(getDataLayout()) < 2)
F->setAlignment(std::max(llvm::Align(2), F->getAlign().valueOrOne()));
}
)


We hit an interesting bug triggered by this, due to Windows ASan's interception mechanism implicitly relying on functions having higher alignment. (https://crbug.com/437182411)

As discussed above, I don't think the intention of the Clang code was ever to lower the alignment. Do you have any ideas how to best preserve 16-byte alignment at least on Windows x64 for now?

@efriedma-quic
Copy link
Collaborator

We've never promised any function alignment on x86, beyond the 2-byte alignment restriction for member functions. At -O2, we normally prefer 16-byte alignment, but that's just a heuristic, and we don't align at -Os/-Oz. If asan requires higher alignment for some reason, we should model that in the compiler somewhere (maybe in the asan lowering pass).

But it might make sense to revert this patch until we've worked out the overall approach here.

zmodem added a commit to zmodem/llvm-project that referenced this pull request Aug 21, 2025
Win/ASan relies on the runtime's functions being 16-byte aligned so it
can intercept them with hotpatching. This used to be true (but not
guaranteed) until llvm#149444.

Pass the flag to explicitly request enough alignment.
@zmodem
Copy link
Collaborator

zmodem commented Aug 21, 2025

If asan requires higher alignment for some reason, we should model that in the compiler somewhere (maybe in the asan lowering pass).

In our bug, it's actually not the asan-instrumented code that required 16-byte alignment, but the asan runtime itself. So regardless of this change, we should probably pass -falign-functions` to request that explicitly: #154694

@nikic
Copy link
Contributor

nikic commented Aug 22, 2025

I'd like this PR to be reverted until https://discourse.llvm.org/t/rfc-enhancing-function-alignment-attributes/88019 concludes.

zmodem added a commit that referenced this pull request Aug 22, 2025
Win/ASan relies on the runtime's functions being 16-byte aligned so it
can intercept them with hotpatching. This used to be true (but not
guaranteed) until #149444.

Passing /hotpatch will give us enough alignment and generally ensure
that the functions are hotpatchable.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants