Skip to content

Conversation

sarnex
Copy link
Member

@sarnex sarnex commented May 1, 2025

When trying to remove the usage of __has_builtin on MSVC CUDA ARM for some builtins, the recommended direction was to universally declare the MSVC builtins on all platforms and require the header providing declarations to be included. This was done here.

However, some MSVC headers already use the MSVC builtins without including the header, so we introduce a warning for anyone compiling with MSVC for this target, so the above change had to be reverted.

The MSVC headers use #pragma intrinsic before the intrinsic uses and that seems to be enough for MSVC, so declare builtins when used in #pragma intrinsic in Clang to prevent the warning.

@sarnex sarnex marked this pull request as ready for review May 2, 2025 14:24
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels May 2, 2025
@llvmbot
Copy link
Member

llvmbot commented May 2, 2025

@llvm/pr-subscribers-clang

Author: Nick Sarnie (sarnex)

Changes

When trying to remove the usage of __has_builtin on MSVC CUDA ARM for some builtins, the recommended direction was to universally declare the MSVC builtins on all platforms and require the header providing declarations to be included. This was done here.

However, some MSVC headers already use the MSVC builtins without including the header, so we introduce a warning for anyone compiling with MSVC for this target, so the above change had to be reverted.

As a workaround, don't warn for implicit uses of library functions if it's inside a system header, unless system header warnings are enabled.

If this PR is accepted I will re-apply the original commit reworking the builtins.


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

3 Files Affected:

  • (modified) clang/lib/Sema/SemaDecl.cpp (+6-1)
  • (added) clang/test/Sema/Inputs/builtin-system-header.h (+1)
  • (added) clang/test/Sema/builtin-system-header.c (+8)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index a3285e8f6f5a2..37008f9eb3235 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2376,9 +2376,14 @@ NamedDecl *Sema::LazilyCreateBuiltin(IdentifierInfo *II, unsigned ID,
     return nullptr;
   }
 
+  // Warn for implicit uses of header dependent libraries,
+  // except in system headers.
   if (!ForRedeclaration &&
       (Context.BuiltinInfo.isPredefinedLibFunction(ID) ||
-       Context.BuiltinInfo.isHeaderDependentFunction(ID))) {
+       Context.BuiltinInfo.isHeaderDependentFunction(ID)) &&
+      (!getDiagnostics().getSuppressSystemWarnings() ||
+       !Context.getSourceManager().isInSystemHeader(
+           Context.getSourceManager().getSpellingLoc(Loc)))) {
     Diag(Loc, LangOpts.C99 ? diag::ext_implicit_lib_function_decl_c99
                            : diag::ext_implicit_lib_function_decl)
         << Context.BuiltinInfo.getName(ID) << R;
diff --git a/clang/test/Sema/Inputs/builtin-system-header.h b/clang/test/Sema/Inputs/builtin-system-header.h
new file mode 100644
index 0000000000000..ebd5655e6f8ef
--- /dev/null
+++ b/clang/test/Sema/Inputs/builtin-system-header.h
@@ -0,0 +1 @@
+#define MACRO(x,y) _InterlockedOr64(x,y);
diff --git a/clang/test/Sema/builtin-system-header.c b/clang/test/Sema/builtin-system-header.c
new file mode 100644
index 0000000000000..83c3c15e314a7
--- /dev/null
+++ b/clang/test/Sema/builtin-system-header.c
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 -fms-extensions -fsyntax-only -verify -triple arm64-windows -isystem %S/Inputs %s
+
+// expected-no-diagnostics
+#include <builtin-system-header.h>
+
+void foo() {
+  MACRO(0,0);
+}

@sarnex sarnex requested review from rnk, Artem-B and mstorsjo May 2, 2025 14:25
@Artem-B
Copy link
Member

Artem-B commented May 2, 2025

Something does not add up here. AFAICT, using builtins w/o explicitly declaring them is something that's done all the time. https://godbolt.org/z/ha47W53dh

In that sense, we should not be needing to filter out the diagnostics coming from the system headers only. There should not be any diagnostics for those builtins at all, from anywhere.

I must be missing something here. @rnk -- is the requirement for builtin declarations a windows-specific quirk?

@sarnex
Copy link
Member Author

sarnex commented May 2, 2025

I think it's because we are explicitly marking these builtins as requiring a header. If they aren't defined that way in the Tablegen file then you won't see this warning and won't need this change.

I made the builtins require a header based on feedback from @rnk in the earlier PR, and his suggestion on my regression fix PR to still require the header but follow what we did for _m_prefetchw, but it seems the same idea was tried there but that was also reverted for what seems to be the same exact problem. See here and revert here.

So if I understand the state of affairs correctly if we want to prevent the winnt.h warning we need to either:

  1. Not require the header so we won't ever warn.
  2. Don't warn in this case, and this PR attempts to implement one idea for that to try to honor rnk's feedback.

Let me know if anyone has suggestions, sorry this change is so drawn out :)

@Artem-B
Copy link
Member

Artem-B commented May 2, 2025

OK. This makes sense.

sorry this change is so drawn out :)

What matters is that you're making progress, and I appreciate your work on getting this issue sorted out the right way.

Comment on lines 2383 to 2386
Context.BuiltinInfo.isHeaderDependentFunction(ID)) &&
(!getDiagnostics().getSuppressSystemWarnings() ||
!Context.getSourceManager().isInSystemHeader(
Context.getSourceManager().getSpellingLoc(Loc)))) {
Copy link
Member

Choose a reason for hiding this comment

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

I think we may want to narrow down the scope of which diagnostics we want to suppress and when.
While we do know that we may need it for these builting for windows on ARM, I can not say whether we want to do that to other intrinsics for other targets.

@rnk
Copy link
Collaborator

rnk commented May 2, 2025

follow what we did for _m_prefetchw, but it seems the same idea was tried there but that was also reverted for what seems to be the same exact problem. See #115099 and revert here.

That change was reverted for different reasons. I shouldn't have changed the prototype of _mm_prefetch there, so I'm relanding it here: #138360


So, winnt.h is supposed to be a system header. We already suppress warnings in system headers. Can you elaborate on why you saw a warning and felt you had to back out the last change? If someone is building with -Wsystem-headers, they should expect to see warnings like this. There are other cases like __cpuidex which are mentioned in winnt.h, but don't break the build:

let Header = "intrin.h", Languages = "ALL_MS_LANGUAGES", Attributes = [NoThrow, RequireDeclaration] in {
def _BitScanForward : X86LibBuiltin<"unsigned char(msuint32_t *, msuint32_t)">;
def _BitScanReverse : X86LibBuiltin<"unsigned char(msuint32_t *, msuint32_t)">;
def _ReadWriteBarrier : X86LibBuiltin<"void()">;
def _ReadBarrier : X86LibBuiltin<"void()">;
def _WriteBarrier : X86LibBuiltin<"void()">;
def __cpuid : X86LibBuiltin<"void(int *, int)">;
def __cpuidex : X86LibBuiltin<"void(int *, int, int)">;

Are you sure we need this extra change to suppress the warning?

@sarnex
Copy link
Member Author

sarnex commented May 5, 2025

That change was reverted for different reasons. I shouldn't have changed the prototype of _mm_prefetch there, so I'm relanding it here: #138360

Thanks, my bad, I should have looked at the problem in more detail.

So, winnt.h is supposed to be a system header. We already suppress warnings in system headers. Can you elaborate on why you saw a warning and felt you had to back out the last change? If someone is building with -Wsystem-headers, they should expect to see warnings like this. There are other cases like __cpuidex which are mentioned in winnt.h, but don't break the build:

You're right, we already have a check here to not throw diagnostics with source locations in system headers. The problem here is the source location is not in the system header, it's the user's call of the system header/builtin function (see repro below). Only the "Spelling" (whatever that means) part of the diagnostic is in the header, and the check doesn't check for that.

Also, it looks like we don't see this problem with __cpuidex because MSVC headers provide a declaration for __cpuidex. If I locally remove that declaration I can reproduce the same problem.

bin\isb.c:4:19: error: call to undeclared library function '__cpuidex' with type 'void (int *, int, int)'; ISO C99 and
      later do not support implicit function declarations [-Wimplicit-function-declaration]
    4 | void func(void) { foo(a,0,0); }
      |                   ^
wat\wat.h:1:20: note: expanded from macro 'foo'
    1 | #define foo(a,b,c) __cpuidex(0,1,2)
      |                    ^
bin\isb.c:4:19: note: include the header <intrin.h> or explicitly provide a declaration for '__cpuidex'
wat\wat.h:1:20: note: expanded from macro 'foo'
    1 | #define foo(a,b,c) __cpuidex(0,1,2)

also if I make a declaration for __isb, the error goes away too.

So to summarize, it seems the root issue is that MSVC headers don't include a declaration for __isb so we warn, and we don't suppress the warning because the source location of the warning is in user code, not system headers.

Maybe that justifies special casing this warning?

Repro (from here)

#include <windows.h>
#include <winnt.h>
void func(void) { _InstructionSynchronizationBarrier(); }
bin/clang-cl --target=aarch64-windows-msvc -c isb.c
isb.c(3,19): error: call to undeclared library function '__isb' with type 'void (unsigned int)'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
    3 | void func(void) { _InstructionSynchronizationBarrier(); }
      |                   ^
/home/martin/msvc2022-17.13/kits/10/include/10.0.22621.0/um/winnt.h(5717,46): note: expanded from macro '_InstructionSynchronizationBarrier'
 5717 | #define _InstructionSynchronizationBarrier() __isb(_ARM64_BARRIER_SY)
      |                                              ^
isb.c(3,19): note: include the header <arm_acle.h> or explicitly provide a declaration for '__isb'
/home/martin/msvc2022-17.13/kits/10/include/10.0.22621.0/um/winnt.h(5717,46): note: expanded from macro '_InstructionSynchronizationBarrier'
 5717 | #define _InstructionSynchronizationBarrier() __isb(_ARM64_BARRIER_SY)

Of course you'll need to revert b60ee39 first.

@sarnex
Copy link
Member Author

sarnex commented May 14, 2025

@rnk Any feedback on my above comment? Hopefully I can merge a solution to the issue soon. Thanks!

@rnk
Copy link
Collaborator

rnk commented May 14, 2025

Sorry for the delay!

I opened up winnt.h and I see this line, which is supposed to make the __isb builtin available:

#pragma intrinsic(__isb)
...
#define _InstructionSynchronizationBarrier() __isb(_ARM64_BARRIER_SY)

We have a handler for that pragma, but it doesn't declare the builtins:
https://github.com/llvm/llvm-project/blob/main/clang/lib/Parse/ParsePragma.cpp#L3777

I think the bug is that it should. Clearly mentioning it this way is enough to make the builtin available for MSVC, so it would make clang-cl more compatible if we did as well. I see that _WriteBarrier. I feel like it would increase scope significantly for me to ask you to implement that, but IMO that would be the highest fidelity implementation. I think that might be less complexity than the diagnostic disabling logic you added, but if it's too hard, we can keep that, and add a FIXME that the disablement could be removed if we properly implemented pragma intrinsic.

Copy link
Collaborator

@rnk rnk left a comment

Choose a reason for hiding this comment

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

Thanks!


/// Map of BuiltinIDs to source locations that have #pragma intrinsic calls
/// that refer to them.
llvm::DenseMap<unsigned, llvm::SmallSetVector<SourceLocation, 4>>
Copy link
Collaborator

Choose a reason for hiding this comment

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

DenseMap of SmallVector values is not an efficient data structure choice, because the DenseMap is always at least 25% empty, and each SmallVector is likely to contain exactly 1 element.

I think we need to serialize these pragmas through the AST. Is it possible to do this by creating an implicit declaration of the builtin at the point of the pragma? You would test for this by making your system header into a module or PCH file in another test case.

Copy link
Member Author

@sarnex sarnex May 16, 2025

Choose a reason for hiding this comment

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

Sorry, I intended to write a comment explaining this commit but I got distracted and you reviewed it too fast :)

I tried creating the declaration just by calling LazilyCreateBuiltin in the pragma handler, but that caused problems when there is a declaration in the header because then we have two real declarations so we get a ambiguous call error. The reason that doesn't happen today with no changes is because we only call LazilyCreateBuiltin when the lookup fails, as in when there is no header providing a declaration and the symbol is totally unknown.

Let me try your idea, thanks for the feedback.

Copy link
Member Author

@sarnex sarnex May 19, 2025

Choose a reason for hiding this comment

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

I tried compiling the system header into a PCH and dumping the AST with llvm-bcanalyzer and I didn't see any implicit declarations, not sure if I was checking for the right thing.

Either way, I tried another solution where we declare the builtin if it wasn't already declared. Fixes the ambiguous call errors I saw when trying to universally declare it and fixes the original issue. Please take a look when you have a sec, thanks.

Signed-off-by: Sarnie, Nick <[email protected]>
Copy link
Collaborator

@rnk rnk left a comment

Choose a reason for hiding this comment

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

Nice, looks good, thanks for implementing this!

@sarnex
Copy link
Member Author

sarnex commented May 19, 2025

Thanks for the guidance and reviews!

@sarnex sarnex requested a review from Artem-B May 19, 2025 20:28
@sarnex sarnex changed the title [clang][Sema] Don't warn for implicit uses of builtins in system headers [clang][Sema] Declare builtins used in #pragma intrinsic May 21, 2025
@sarnex sarnex merged commit 95bd9ee into llvm:main May 21, 2025
11 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented May 21, 2025

LLVM Buildbot has detected a new failure on builder llvm-clang-x86_64-gcc-ubuntu running on sie-linux-worker3 while building clang at step 6 "test-build-unified-tree-check-all".

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

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'AddressSanitizer-x86_64-linux-dynamic :: TestCases/asan_lsan_deadlock.cpp' FAILED ********************
Exit Code: 1

Command Output (stderr):
--
/home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/./bin/clang  --driver-mode=g++ -fsanitize=address -mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-optimize-sibling-calls -gline-tables-only  -m64  -shared-libasan -O0 /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/llvm-project/compiler-rt/test/asan/TestCases/asan_lsan_deadlock.cpp -o /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/runtimes/runtimes-bins/compiler-rt/test/asan/X86_64LinuxDynamicConfig/TestCases/Output/asan_lsan_deadlock.cpp.tmp # RUN: at line 4
+ /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/./bin/clang --driver-mode=g++ -fsanitize=address -mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-optimize-sibling-calls -gline-tables-only -m64 -shared-libasan -O0 /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/llvm-project/compiler-rt/test/asan/TestCases/asan_lsan_deadlock.cpp -o /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/runtimes/runtimes-bins/compiler-rt/test/asan/X86_64LinuxDynamicConfig/TestCases/Output/asan_lsan_deadlock.cpp.tmp
env ASAN_OPTIONS=detect_leaks=1 not  /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/runtimes/runtimes-bins/compiler-rt/test/asan/X86_64LinuxDynamicConfig/TestCases/Output/asan_lsan_deadlock.cpp.tmp 2>&1 | FileCheck /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/llvm-project/compiler-rt/test/asan/TestCases/asan_lsan_deadlock.cpp # RUN: at line 5
+ env ASAN_OPTIONS=detect_leaks=1 not /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/build/runtimes/runtimes-bins/compiler-rt/test/asan/X86_64LinuxDynamicConfig/TestCases/Output/asan_lsan_deadlock.cpp.tmp
+ FileCheck /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/llvm-project/compiler-rt/test/asan/TestCases/asan_lsan_deadlock.cpp
�[1m/home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/llvm-project/compiler-rt/test/asan/TestCases/asan_lsan_deadlock.cpp:58:12: �[0m�[0;1;31merror: �[0m�[1mCHECK: expected string not found in input
�[0m // CHECK: SUMMARY: AddressSanitizer: stack-buffer-overflow
�[0;1;32m           ^
�[0m�[1m<stdin>:1:1: �[0m�[0;1;30mnote: �[0m�[1mscanning from here
�[0m=================================================================
�[0;1;32m^
�[0m�[1m<stdin>:2:10: �[0m�[0;1;30mnote: �[0m�[1mpossible intended match here
�[0m==2860894==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7bf1ee0de034 at pc 0x557d9aa4c220 bp 0x7bf1eb7f4ce0 sp 0x7bf1eb7f4cd8
�[0;1;32m         ^
�[0m
Input file: <stdin>
Check file: /home/buildbot/buildbot-root/llvm-clang-x86_64-gcc-ubuntu/llvm-project/compiler-rt/test/asan/TestCases/asan_lsan_deadlock.cpp

-dump-input=help explains the following input dump.

Input was:
<<<<<<
�[1m�[0m�[0;1;30m            1: �[0m�[1m�[0;1;46m================================================================= �[0m
�[0;1;31mcheck:58'0     X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: no match found
�[0m�[0;1;30m            2: �[0m�[1m�[0;1;46m==2860894==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7bf1ee0de034 at pc 0x557d9aa4c220 bp 0x7bf1eb7f4ce0 sp 0x7bf1eb7f4cd8 �[0m
�[0;1;31mcheck:58'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
�[0m�[0;1;35mcheck:58'1              ?                                                                                                                                    possible intended match
�[0m�[0;1;30m            3: �[0m�[1m�[0;1;46mWRITE of size 4 at 0x7bf1ee0de034 thread T2 �[0m
�[0;1;31mcheck:58'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
�[0m>>>>>>

--

********************


@alanzhao1
Copy link
Contributor

Chrome is seeing some breakages due to this: https://crbug.com/420757016

@sarnex
Copy link
Member Author

sarnex commented May 28, 2025

Investigating, thanks.

@sarnex
Copy link
Member Author

sarnex commented May 29, 2025

It took me all day but I managed to reproduce the issue with Chromium, investigating more.

@sarnex
Copy link
Member Author

sarnex commented May 29, 2025

@alanzhao1 let me know if this is a big problem for Chrome and I can revert it, I'd prefer to try to fix it quickly first and if I can't then revert it, thanks.

@sarnex
Copy link
Member Author

sarnex commented May 29, 2025

Chrome requested I revert

sarnex added a commit that referenced this pull request May 29, 2025
sarnex added a commit that referenced this pull request May 29, 2025
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request May 30, 2025
@nico
Copy link
Contributor

nico commented May 30, 2025

Chrome requested I revert

Thanks for the revert!

I kind of requested that more with my clang hat on than my chrome hat – we try to keep trunk green as much as possible, and since you have a repro, reverting first makes trunk green sooner, and removes time pressure when investigating the problem :)

@sarnex
Copy link
Member Author

sarnex commented May 30, 2025

Sure, I was hoping I could find an easy fix really quickly but just reproing took me a while, I'll revert immediately in the future, thanks.

sarnex added a commit that referenced this pull request Jun 3, 2025
…insic #138205' (#142019)

I had to revert #138205 in
#141994 because it broke the
Chrome build.

The problem came down to the following:

```c++
unsigned __int64 _umul128(unsigned __int64, unsigned __int64,
                          unsigned __int64 *);

namespace {}
#pragma intrinsic(_umul128)

 void foo() {
   unsigned __int64 carry;
   unsigned __int64 low = _umul128(0, 0, &carry);
 }
```

When processing the `#pragma intrinsic` line, we do a name lookup to see
if the builtin was previously declared. In this case the lookup fails
because the current namespace of the parser and sema is the above
namespace scope. The processing of the pragma happens as part of the
namespace close parsing. This is usually fine because most pragmas don't
care about scopes. However, that's not true for this and other MS
pragmas.

To fix this, we change the `#pragma intrinsic` processing to be the same
as other MS pragmas such as "optimize". Those are processed like a
declaration, and because of that we have the correct current scope, so
the lookup succeeds.

I added a test case that locks down the Chrome fix, as well as manually
tested the Chrome build and confirmed it passed.
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Jun 3, 2025
…ragma intrinsic #138205' (#142019)

I had to revert llvm/llvm-project#138205 in
llvm/llvm-project#141994 because it broke the
Chrome build.

The problem came down to the following:

```c++
unsigned __int64 _umul128(unsigned __int64, unsigned __int64,
                          unsigned __int64 *);

namespace {}
#pragma intrinsic(_umul128)

 void foo() {
   unsigned __int64 carry;
   unsigned __int64 low = _umul128(0, 0, &carry);
 }
```

When processing the `#pragma intrinsic` line, we do a name lookup to see
if the builtin was previously declared. In this case the lookup fails
because the current namespace of the parser and sema is the above
namespace scope. The processing of the pragma happens as part of the
namespace close parsing. This is usually fine because most pragmas don't
care about scopes. However, that's not true for this and other MS
pragmas.

To fix this, we change the `#pragma intrinsic` processing to be the same
as other MS pragmas such as "optimize". Those are processed like a
declaration, and because of that we have the correct current scope, so
the lookup succeeds.

I added a test case that locks down the Chrome fix, as well as manually
tested the Chrome build and confirmed it passed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
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.

7 participants