[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