[llvm] [Win][llvm]MSVC inliner section behavior compat (PR #146965)
Adam Glass via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 3 14:43:18 PDT 2025
https://github.com/AdamGlass created https://github.com/llvm/llvm-project/pull/146965
MSVC implements has a compatibility test/rule for inlining between sections. Inlining instances that do not meet these rules are rejected. The rules are:
* Inlining is 'compatible' between functions in the same section
* Inlining is 'incompatible' between functions where one is in a section that starts with "PAGE" and the other function does not.
* Inlining is 'compatible' between functions that are in sections that start with "PAGE" and and the rest of the section name before a possible '$' separator/group are the same. Eg. Section PAGEVRFY$aaa is compatible with PAGEVRFY$bbb but not PAGEINIT or PAGEINIT$foo
* Any other scenarios are 'compatible'
We are at present unaware of any public documentation of these behaviors.
There may be future compatibility rules associated with user-specified 'inline' and 'forceinline'.
This PR implements the above rules/behavior and constrains their application to the MSVC environment.
Test included
Q: Does this PAGE prefix have an implication for the section flags?
A: No
>From 7bf07fb3c9b0d4d3fa9447ac93c8b94e76578aa8 Mon Sep 17 00:00:00 2001
From: Adam Glass <adamglass at microsoft.com>
Date: Mon, 16 Jun 2025 11:09:15 -0700
Subject: [PATCH] MSVC inliner section behavior compat
---
llvm/include/llvm/IR/Attributes.td | 1 +
llvm/lib/IR/Attributes.cpp | 42 +++++
.../Transforms/Inline/inline-msvc-sections.ll | 157 ++++++++++++++++++
3 files changed, 200 insertions(+)
create mode 100644 llvm/test/Transforms/Inline/inline-msvc-sections.ll
diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index 112853965407c..3daf5819d421e 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -455,6 +455,7 @@ def : CompatRule<"isEqual<UseSampleProfileAttr>">;
def : CompatRule<"isEqual<NoProfileAttr>">;
def : CompatRule<"checkDenormMode">;
def : CompatRule<"checkStrictFP">;
+def : CompatRule<"checkSectionsMSVC">;
def : CompatRuleStrAttr<"isEqual", "sign-return-address">;
def : CompatRuleStrAttr<"isEqual", "sign-return-address-key">;
def : CompatRuleStrAttr<"isEqual", "branch-protection-pauth-lr">;
diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp
index d1fbcb9e893a7..8f5e4d2bd301e 100644
--- a/llvm/lib/IR/Attributes.cpp
+++ b/llvm/lib/IR/Attributes.cpp
@@ -2508,6 +2508,48 @@ static bool checkStrictFP(const Function &Caller, const Function &Callee) {
Caller.getAttributes().hasFnAttr(Attribute::StrictFP);
}
+static bool checkSectionsMSVC(const Function &Caller, const Function &Callee) {
+ // Apply inlining section compatibility test only to MSVC environment
+ if (!Caller.getParent()->getTargetTriple().isWindowsMSVCEnvironment())
+ return true;
+
+ StringRef CallerSection = Caller.getSection();
+ StringRef CalleeSection = Callee.getSection();
+
+ // Sections match, inlining ok
+ if (!CallerSection.compare(CalleeSection))
+ return true;
+
+ bool isCallerPaged = CallerSection.starts_with("PAGE");
+ bool isCalleePaged = CalleeSection.starts_with("PAGE");
+
+ // Paged caller & callee
+ if (isCallerPaged && isCalleePaged) {
+ // Compare section names up to '$' separator if present
+ size_t CallerComparable = CallerSection.size();
+ size_t CalleeComparable = CalleeSection.size();
+ size_t CallerSep = CallerSection.find('$');
+ size_t CalleeSep = CalleeSection.find('$');
+
+ if (CallerSep != StringRef::npos)
+ CallerComparable = CallerSep;
+ if (CalleeSep != StringRef::npos)
+ CalleeComparable = CalleeSep;
+ if (CallerComparable != CalleeComparable)
+ return false;
+
+ StringRef CallerComparableSection =
+ CallerSection.substr(0, CallerComparable);
+ StringRef CalleeComparableSection =
+ CalleeSection.substr(0, CallerComparable);
+ return !CallerComparableSection.compare(CalleeComparableSection);
+ } else if (isCalleePaged || isCallerPaged)
+ // Paged and unpaged code must not be mixed
+ return false;
+
+ return true;
+}
+
template<typename AttrClass>
static bool isEqual(const Function &Caller, const Function &Callee) {
return Caller.getFnAttribute(AttrClass::getKind()) ==
diff --git a/llvm/test/Transforms/Inline/inline-msvc-sections.ll b/llvm/test/Transforms/Inline/inline-msvc-sections.ll
new file mode 100644
index 0000000000000..5c4b199f88ee0
--- /dev/null
+++ b/llvm/test/Transforms/Inline/inline-msvc-sections.ll
@@ -0,0 +1,157 @@
+; RUN: opt < %s -passes=inline -inline-threshold=100 -S | FileCheck %s
+; RUN: opt < %s -passes='cgscc(inline)' -inline-threshold=100 -S | FileCheck %s
+; RUN: opt < %s -mtriple=aarch64-windows-msvc -passes=inline -inline-threshold=100 -S | FileCheck %s -check-prefix=MSVC
+; RUN: opt < %s -mtriple=aarch64-windows-msvc -passes='cgscc(inline)' -inline-threshold=100 -S | FileCheck %s -check-prefix=MSVC
+
+define i32 @nosection_callee(i32 %x) {
+ %x1 = add i32 %x, 1
+ %x2 = add i32 %x1, 1
+ %x3 = add i32 %x2, 1
+ call void @extern()
+ ret i32 %x3
+}
+
+define i32 @section_callee(i32 %x) section "FOO" {
+ %x1 = add i32 %x, 1
+ %x2 = add i32 %x1, 1
+ %x3 = add i32 %x2, 1
+ call void @extern()
+ ret i32 %x3
+}
+
+define i32 @sectionpostfix_callee(i32 %x) section "FOO$BBBB" {
+ %x1 = add i32 %x, 1
+ %x2 = add i32 %x1, 1
+ %x3 = add i32 %x2, 1
+ call void @extern()
+ ret i32 %x3
+}
+
+define i32 @paged_callee(i32 %x) section "PAGE" {
+ %x1 = add i32 %x, 1
+ %x2 = add i32 %x1, 1
+ %x3 = add i32 %x2, 1
+ call void @extern()
+ ret i32 %x3
+}
+
+define i32 @pagedpostfix_callee(i32 %x) section "PAGE$aaa" {
+ %x1 = add i32 %x, 1
+ %x2 = add i32 %x1, 1
+ %x3 = add i32 %x2, 1
+ call void @extern()
+ ret i32 %x3
+}
+
+define i32 @nosection_caller(i32 %y1) {
+ %y2 = call i32 @nosection_callee(i32 %y1)
+ %y3 = call i32 @section_callee(i32 %y2)
+ %y4 = call i32 @sectionpostfix_callee(i32 %y3)
+ %y5 = call i32 @paged_callee(i32 %y4)
+ %y6 = call i32 @pagedpostfix_callee(i32 %y5)
+ ret i32 %y6
+}
+
+; CHECK-LABEL: @nosection_caller
+; CHECK-NOT: @nosection_callee
+; CHECK-NOT: @section_callee
+; CHECK-NOT: @sectionpostfix_callee
+; CHECK-NOT: @paged_callee
+; CHECK-NOT: @pagedpostfix_callee
+; MSVC-LABEL: @nosection_caller
+; MSVC-NOT: @nosection_callee
+; MSVC-NOT: @section_callee
+; MSVC-NOT: @sectionpostfix_callee
+; MSVC: @paged_callee
+; MSVC: @pagedpostfix_callee
+
+define i32 @section_caller(i32 %y1) section "FOO" {
+ %y2 = call i32 @nosection_callee(i32 %y1)
+ %y3 = call i32 @section_callee(i32 %y2)
+ %y4 = call i32 @sectionpostfix_callee(i32 %y3)
+ %y5 = call i32 @paged_callee(i32 %y4)
+ %y6 = call i32 @pagedpostfix_callee(i32 %y5)
+ ret i32 %y6
+}
+
+; CHECK-LABEL: @section_caller
+; CHECK-NOT: @nosection_callee
+; CHECK-NOT: @section_callee
+; CHECK-NOT: @sectionpostfix_callee
+; CHECK-NOT: @paged_callee
+; CHECK-NOT: @pagedpostfix_callee
+; MSVC-LABEL: @section_caller
+; MSVC-NOT: @nosection_callee
+; MSVC-NOT: @section_callee
+; MSVC-NOT: @sectionpostfix_callee
+; MSVC: @paged_callee
+; MSVC: @pagedpostfix_callee
+
+define i32 @sectionpostfix_caller(i32 %y1) section "FOO$ZZZ" {
+ %y2 = call i32 @nosection_callee(i32 %y1)
+ %y3 = call i32 @section_callee(i32 %y2)
+ %y4 = call i32 @sectionpostfix_callee(i32 %y3)
+ %y5 = call i32 @paged_callee(i32 %y4)
+ %y6 = call i32 @pagedpostfix_callee(i32 %y5)
+ ret i32 %y6
+}
+
+; CHECK-LABEL: @sectionpostfix_caller
+; CHECK-NOT: @nosection_callee
+; CHECK-NOT: @section_callee
+; CHECK-NOT: @sectionpostfix_callee
+; CHECK-NOT: @paged_callee
+; CHECK-NOT: @pagedpostfix_callee
+; MSVC-LABEL: @sectionpostfix_caller
+; MSVC-NOT: @nosection_callee
+; MSVC-NOT: @section_callee
+; MSVC-NOT: @sectionpostfix_callee
+; MSVC: @paged_callee
+; MSVC: @pagedpostfix_callee
+
+define i32 @paged_caller(i32 %y1) section "PAGE" {
+ %y2 = call i32 @nosection_callee(i32 %y1)
+ %y3 = call i32 @section_callee(i32 %y2)
+ %y4 = call i32 @sectionpostfix_callee(i32 %y3)
+ %y5 = call i32 @paged_callee(i32 %y4)
+ %y6 = call i32 @pagedpostfix_callee(i32 %y5)
+ ret i32 %y6
+}
+
+; CHECK-LABEL: @paged_caller
+; CHECK-NOT: @nosection_callee
+; CHECK-NOT: @section_callee
+; CHECK-NOT: @sectionpostfix_callee
+; CHECK-NOT: @paged_callee
+; CHECK-NOT: @pagedpostfix_callee
+; MSVC-LABEL: @paged_caller
+; MSVC: @nosection_callee
+; MSVC: @section_callee
+; MSVC: @sectionpostfix_callee
+; MSVC-NOT: @paged_callee
+; MSVC-NOT: @pagedpostfix_callee
+
+define i32 @pagedpostfix_caller(i32 %y1) section "PAGE$ZZZ" {
+ %y2 = call i32 @nosection_callee(i32 %y1)
+ %y3 = call i32 @section_callee(i32 %y2)
+ %y4 = call i32 @sectionpostfix_callee(i32 %y3)
+ %y5 = call i32 @paged_callee(i32 %y4)
+ %y6 = call i32 @pagedpostfix_callee(i32 %y5)
+ ret i32 %y6
+}
+
+; CHECK-LABEL: @pagedpostfix_caller
+; CHECK-NOT: @nosection_callee
+; CHECK-NOT: @section_callee
+; CHECK-NOT: @sectionpostfix_callee
+; CHECK-NOT: @paged_callee
+; CHECK-NOT: @pagedpostfix_callee
+; MSVC-LABEL: @pagedpostfix_caller
+; MSVC: @nosection_callee
+; MSVC: @section_callee
+; MSVC: @sectionpostfix_callee
+; MSVC-NOT: @paged_callee
+; MSVC-NOT: @pagedpostfix_callee
+
+declare void @extern()
+
More information about the llvm-commits
mailing list