Skip to content

Conversation

@AdamGlass
Copy link
Contributor

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

@llvmbot
Copy link
Member

llvmbot commented Jul 3, 2025

@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-llvm-ir

Author: Adam Glass (AdamGlass)

Changes

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


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

3 Files Affected:

  • (modified) llvm/include/llvm/IR/Attributes.td (+1)
  • (modified) llvm/lib/IR/Attributes.cpp (+42)
  • (added) llvm/test/Transforms/Inline/inline-msvc-sections.ll (+157)
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()
+

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.

2 participants