Skip to content

[Clang][CodeGen] Add workaround for old glibc __PTR_ALIGN macro #137851

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 9, 2025

Conversation

dtcxzyw
Copy link
Member

@dtcxzyw dtcxzyw commented Apr 29, 2025

Closes #137833.
This patch is stacked on #137849.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:codegen IR generation bugs: mangling, exceptions, etc. labels Apr 29, 2025
@llvmbot
Copy link
Member

llvmbot commented Apr 29, 2025

@llvm/pr-subscribers-clang-codegen

Author: Yingwei Zheng (dtcxzyw)

Changes

Closes #137833.
This patch is stacked on #137849.


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

4 Files Affected:

  • (modified) clang/lib/CodeGen/CGExpr.cpp (+3)
  • (modified) clang/lib/CodeGen/CGExprScalar.cpp (+2-1)
  • (modified) clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c (+12)
  • (added) clang/test/CodeGen/glibc_ptr_align.c (+22)
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index bba7d1e805f3f..e9e22321e2634 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -4820,6 +4820,9 @@ bool CodeGenFunction::isUnderlyingBasePointerConstantNull(const Expr *E) {
   const Expr *UnderlyingBaseExpr = E->IgnoreParens();
   while (auto *BaseMemberExpr = dyn_cast<MemberExpr>(UnderlyingBaseExpr))
     UnderlyingBaseExpr = BaseMemberExpr->getBase()->IgnoreParens();
+  if (auto *Select = dyn_cast<ConditionalOperator>(UnderlyingBaseExpr))
+    return isUnderlyingBasePointerConstantNull(Select->getTrueExpr()) ||
+           isUnderlyingBasePointerConstantNull(Select->getFalseExpr());
   return getContext().isSentinelNullExpr(UnderlyingBaseExpr);
 }
 
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 8dbbcdaef25d8..d214d2af52563 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -4238,7 +4238,8 @@ static Value *emitPointerArithmetic(CodeGenFunction &CGF,
   else
     elemTy = CGF.ConvertTypeForMem(elementType);
 
-  if (CGF.getLangOpts().PointerOverflowDefined)
+  if (CGF.getLangOpts().PointerOverflowDefined ||
+      CGF.isUnderlyingBasePointerConstantNull(pointerOperand))
     return CGF.Builder.CreateGEP(elemTy, pointer, index, "add.ptr");
 
   return CGF.EmitCheckedInBoundsGEP(
diff --git a/clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c b/clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c
index 63b6db2c2adeb..c5ae3f8bcc368 100644
--- a/clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c
+++ b/clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c
@@ -431,6 +431,18 @@ char *void_ptr(void *base, unsigned long offset) {
   return base + offset;
 }
 
+int *constant_null_add(long offset) {
+  // CHECK: define{{.*}} ptr @constant_null_add(i64 noundef %[[OFFSET:.*]])
+  // CHECK-NEXT: [[ENTRY:.*]]:
+  // CHECK-NEXT:   %[[OFFSET_ADDR:.*]] = alloca i64, align 8
+  // CHECK-NEXT:   store i64 %[[OFFSET]], ptr %[[OFFSET_ADDR]], align 8
+  // CHECK-NEXT:   %[[OFFSET_RELOADED:.*]] = load i64, ptr %[[OFFSET_ADDR]], align 8
+  // CHECK-NEXT:   %[[ADD_PTR:.*]] = getelementptr i32, ptr null, i64 %[[OFFSET_RELOADED]]
+  // CHECK-NEXT:   ret ptr %[[ADD_PTR]]
+#line 1800
+  return (int *)0 + offset;
+}
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/clang/test/CodeGen/glibc_ptr_align.c b/clang/test/CodeGen/glibc_ptr_align.c
new file mode 100644
index 0000000000000..14968a8326509
--- /dev/null
+++ b/clang/test/CodeGen/glibc_ptr_align.c
@@ -0,0 +1,22 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
+// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -O3 -o - -emit-llvm %s | FileCheck %s
+
+// Make sure that we do not set inbounds flag if the base pointer may be a constant null.
+
+// CHECK-LABEL: define dso_local noalias ptr @glibc_ptr_align(
+// CHECK-SAME: ptr noundef readnone captures(none) [[BASE:%.*]], ptr noundef [[POINTER:%.*]], i64 noundef [[ALIGN_MASK:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[POINTER]] to i64
+// CHECK-NEXT:    [[ADD:%.*]] = add nsw i64 [[ALIGN_MASK]], [[SUB_PTR_LHS_CAST]]
+// CHECK-NEXT:    [[NOT:%.*]] = xor i64 [[ALIGN_MASK]], -1
+// CHECK-NEXT:    [[AND:%.*]] = and i64 [[ADD]], [[NOT]]
+// CHECK-NEXT:    [[ADD_PTR:%.*]] = getelementptr i8, ptr null, i64 [[AND]]
+// CHECK-NEXT:    ret ptr [[ADD_PTR]]
+//
+char *glibc_ptr_align(char *base, char *pointer, long align_mask) {
+  return (sizeof(long int) < sizeof(void *) ? (base) : (char *)0) +
+         (((pointer) -
+           (sizeof(long int) < sizeof(void *) ? (base) : (char *)0) +
+           (align_mask)) &
+          ~(align_mask));
+}

@llvmbot
Copy link
Member

llvmbot commented Apr 29, 2025

@llvm/pr-subscribers-clang

Author: Yingwei Zheng (dtcxzyw)

Changes

Closes #137833.
This patch is stacked on #137849.


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

4 Files Affected:

  • (modified) clang/lib/CodeGen/CGExpr.cpp (+3)
  • (modified) clang/lib/CodeGen/CGExprScalar.cpp (+2-1)
  • (modified) clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c (+12)
  • (added) clang/test/CodeGen/glibc_ptr_align.c (+22)
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index bba7d1e805f3f..e9e22321e2634 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -4820,6 +4820,9 @@ bool CodeGenFunction::isUnderlyingBasePointerConstantNull(const Expr *E) {
   const Expr *UnderlyingBaseExpr = E->IgnoreParens();
   while (auto *BaseMemberExpr = dyn_cast<MemberExpr>(UnderlyingBaseExpr))
     UnderlyingBaseExpr = BaseMemberExpr->getBase()->IgnoreParens();
+  if (auto *Select = dyn_cast<ConditionalOperator>(UnderlyingBaseExpr))
+    return isUnderlyingBasePointerConstantNull(Select->getTrueExpr()) ||
+           isUnderlyingBasePointerConstantNull(Select->getFalseExpr());
   return getContext().isSentinelNullExpr(UnderlyingBaseExpr);
 }
 
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 8dbbcdaef25d8..d214d2af52563 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -4238,7 +4238,8 @@ static Value *emitPointerArithmetic(CodeGenFunction &CGF,
   else
     elemTy = CGF.ConvertTypeForMem(elementType);
 
-  if (CGF.getLangOpts().PointerOverflowDefined)
+  if (CGF.getLangOpts().PointerOverflowDefined ||
+      CGF.isUnderlyingBasePointerConstantNull(pointerOperand))
     return CGF.Builder.CreateGEP(elemTy, pointer, index, "add.ptr");
 
   return CGF.EmitCheckedInBoundsGEP(
diff --git a/clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c b/clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c
index 63b6db2c2adeb..c5ae3f8bcc368 100644
--- a/clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c
+++ b/clang/test/CodeGen/catch-nullptr-and-nonzero-offset.c
@@ -431,6 +431,18 @@ char *void_ptr(void *base, unsigned long offset) {
   return base + offset;
 }
 
+int *constant_null_add(long offset) {
+  // CHECK: define{{.*}} ptr @constant_null_add(i64 noundef %[[OFFSET:.*]])
+  // CHECK-NEXT: [[ENTRY:.*]]:
+  // CHECK-NEXT:   %[[OFFSET_ADDR:.*]] = alloca i64, align 8
+  // CHECK-NEXT:   store i64 %[[OFFSET]], ptr %[[OFFSET_ADDR]], align 8
+  // CHECK-NEXT:   %[[OFFSET_RELOADED:.*]] = load i64, ptr %[[OFFSET_ADDR]], align 8
+  // CHECK-NEXT:   %[[ADD_PTR:.*]] = getelementptr i32, ptr null, i64 %[[OFFSET_RELOADED]]
+  // CHECK-NEXT:   ret ptr %[[ADD_PTR]]
+#line 1800
+  return (int *)0 + offset;
+}
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/clang/test/CodeGen/glibc_ptr_align.c b/clang/test/CodeGen/glibc_ptr_align.c
new file mode 100644
index 0000000000000..14968a8326509
--- /dev/null
+++ b/clang/test/CodeGen/glibc_ptr_align.c
@@ -0,0 +1,22 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
+// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -O3 -o - -emit-llvm %s | FileCheck %s
+
+// Make sure that we do not set inbounds flag if the base pointer may be a constant null.
+
+// CHECK-LABEL: define dso_local noalias ptr @glibc_ptr_align(
+// CHECK-SAME: ptr noundef readnone captures(none) [[BASE:%.*]], ptr noundef [[POINTER:%.*]], i64 noundef [[ALIGN_MASK:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[POINTER]] to i64
+// CHECK-NEXT:    [[ADD:%.*]] = add nsw i64 [[ALIGN_MASK]], [[SUB_PTR_LHS_CAST]]
+// CHECK-NEXT:    [[NOT:%.*]] = xor i64 [[ALIGN_MASK]], -1
+// CHECK-NEXT:    [[AND:%.*]] = and i64 [[ADD]], [[NOT]]
+// CHECK-NEXT:    [[ADD_PTR:%.*]] = getelementptr i8, ptr null, i64 [[AND]]
+// CHECK-NEXT:    ret ptr [[ADD_PTR]]
+//
+char *glibc_ptr_align(char *base, char *pointer, long align_mask) {
+  return (sizeof(long int) < sizeof(void *) ? (base) : (char *)0) +
+         (((pointer) -
+           (sizeof(long int) < sizeof(void *) ? (base) : (char *)0) +
+           (align_mask)) &
+          ~(align_mask));
+}

@DavidTruby
Copy link
Member

This fixes an issue I was seeing caused by #130742 that caused GNU m4 and make builds to segfault, which I assume was caused by #137833. I don't understand the code here well enough to give an actual review, but it'd be great to see this merged as m4/make builds generating bad binaries is causing a lot of other issues.
Thanks for the fix 🙂

@llvmbot llvmbot added the clang:frontend Language frontend issues, e.g. anything involving "Sema" label May 6, 2025
Copy link
Collaborator

@efriedma-quic efriedma-quic left a comment

Choose a reason for hiding this comment

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

LGTM

@dtcxzyw dtcxzyw merged commit 61a8da9 into llvm:main May 9, 2025
9 of 10 checks passed
@dtcxzyw dtcxzyw deleted the fix-137833-2 branch May 9, 2025 11:23
@llvm-ci
Copy link
Collaborator

llvm-ci commented May 9, 2025

LLVM Buildbot has detected a new failure on builder llvm-clang-aarch64-darwin running on doug-worker-4 while building clang at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/190/builds/19726

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'Clang-Unit :: ./AllClangUnitTests/11/48' FAILED ********************
Script(shard):
--
GTEST_OUTPUT=json:/Users/buildbot/buildbot-root/aarch64-darwin/build/tools/clang/unittests/./AllClangUnitTests-Clang-Unit-85833-11-48.json GTEST_SHUFFLE=0 GTEST_TOTAL_SHARDS=48 GTEST_SHARD_INDEX=11 /Users/buildbot/buildbot-root/aarch64-darwin/build/tools/clang/unittests/./AllClangUnitTests
--

Note: This is test shard 12 of 48.
[==========] Running 510 tests from 105 test suites.
[----------] Global test environment set-up.
[----------] 1 test from MinimizeSourceToDependencyDirectivesTest
[ RUN      ] MinimizeSourceToDependencyDirectivesTest.DefineHorizontalWhitespace
[       OK ] MinimizeSourceToDependencyDirectivesTest.DefineHorizontalWhitespace (0 ms)
[----------] 1 test from MinimizeSourceToDependencyDirectivesTest (0 ms total)

[----------] 1 test from HeaderSearchTest
[ RUN      ] HeaderSearchTest.NoSearchDir
[       OK ] HeaderSearchTest.NoSearchDir (0 ms)
[----------] 1 test from HeaderSearchTest (0 ms total)

[----------] 1 test from ModuleDeclStateTest
[ RUN      ] ModuleDeclStateTest.ModuleImplementationPartition
[       OK ] ModuleDeclStateTest.ModuleImplementationPartition (0 ms)
[----------] 1 test from ModuleDeclStateTest (0 ms total)

[----------] 1 test from DxcModeTest
[ RUN      ] DxcModeTest.TargetProfileValidation
[       OK ] DxcModeTest.TargetProfileValidation (8 ms)
[----------] 1 test from DxcModeTest (8 ms total)

[----------] 1 test from MultilibTest
[ RUN      ] MultilibTest.SetPushback
[       OK ] MultilibTest.SetPushback (0 ms)
[----------] 1 test from MultilibTest (0 ms total)

[----------] 2 tests from ExprMutationAnalyzerTest
[ RUN      ] ExprMutationAnalyzerTest.CallUnresolved
[       OK ] ExprMutationAnalyzerTest.CallUnresolved (68 ms)
[ RUN      ] ExprMutationAnalyzerTest.RangeForNonArrayByConstRef
input.cc:1:103: warning: expression result unused [-Wunused-value]
    1 | struct V { const int* begin() const; const int* end() const; };void f() { V x; for (const int& e : x) e; }
      |                                                                                                       ^
[       OK ] ExprMutationAnalyzerTest.RangeForNonArrayByConstRef (3 ms)
[----------] 2 tests from ExprMutationAnalyzerTest (71 ms total)

[----------] 1 test from MacroExpansionContextTest
[ RUN      ] MacroExpansionContextTest.StringizingVariadicMacros
[       OK ] MacroExpansionContextTest.StringizingVariadicMacros (0 ms)
[----------] 1 test from MacroExpansionContextTest (0 ms total)

[----------] 1 test from EnvironmentTest
...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:codegen IR generation bugs: mangling, exceptions, etc. clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Missing workaround for old glibc __PTR_ALIGN macro
5 participants