From e24a7bbf4515213f44d410bfc41b3dff27c49c86 Mon Sep 17 00:00:00 2001 From: Brian Gesiak Date: Mon, 29 Apr 2024 18:43:08 -0400 Subject: [PATCH 01/65] [mlir-lsp] Support outgoing requests (#90078) Add support for outgoing requests to `lsp::MessageHandler`. Much like `MessageHandler::outgoingNotification`, this allows for the message handler to send outgoing messages via its JSON transport, but in this case, those messages are requests, not notifications. Requests receive responses (also referred to as "replies" in `MLIRLspServerSupportLib`). These were previously unsupported, and `lsp::MessageHandler` would log an error each time it processed a JSON message that appeared to be a response (something with an "id" field, but no "method" field). However, the `outgoingRequest` method now handles response callbacks: an outgoing request with a given ID is set up such that a callback function is invoked when a response with that ID is received. --- .../mlir/Tools/lsp-server-support/Transport.h | 41 +++++++++++++++++++ .../Tools/lsp-server-support/Transport.cpp | 38 ++++++++++------- .../Tools/lsp-server-support/Transport.cpp | 39 ++++++++++++++++++ 3 files changed, 103 insertions(+), 15 deletions(-) diff --git a/mlir/include/mlir/Tools/lsp-server-support/Transport.h b/mlir/include/mlir/Tools/lsp-server-support/Transport.h index 44c71058cf717c..047d174234df8d 100644 --- a/mlir/include/mlir/Tools/lsp-server-support/Transport.h +++ b/mlir/include/mlir/Tools/lsp-server-support/Transport.h @@ -15,6 +15,7 @@ #ifndef MLIR_TOOLS_LSPSERVERSUPPORT_TRANSPORT_H #define MLIR_TOOLS_LSPSERVERSUPPORT_TRANSPORT_H +#include "mlir/Support/DebugStringHelper.h" #include "mlir/Support/LLVM.h" #include "mlir/Support/LogicalResult.h" #include "mlir/Tools/lsp-server-support/Logging.h" @@ -100,6 +101,18 @@ using Callback = llvm::unique_function)>; template using OutgoingNotification = llvm::unique_function; +/// An OutgoingRequest is a function used for outgoing requests to send to +/// the client. +template +using OutgoingRequest = + llvm::unique_function; + +/// An `OutgoingRequestCallback` is invoked when an outgoing request to the +/// client receives a response in turn. It is passed the original request's ID, +/// as well as the result JSON. +using OutgoingRequestCallback = + std::function)>; + /// A handler used to process the incoming transport messages. class MessageHandler { public: @@ -170,6 +183,26 @@ class MessageHandler { }; } + /// Create an OutgoingRequest function that, when called, sends a request with + /// the given method via the transport. Should the outgoing request be + /// met with a response, the response callback is invoked to handle that + /// response. + template + OutgoingRequest outgoingRequest(llvm::StringLiteral method, + OutgoingRequestCallback callback) { + return [&, method, callback](const T ¶ms, llvm::json::Value id) { + { + std::lock_guard lock(responseHandlersMutex); + responseHandlers.insert( + {debugString(id), std::make_pair(method.str(), callback)}); + } + + std::lock_guard transportLock(transportOutputMutex); + Logger::info("--> {0}({1})", method, id); + transport.call(method, llvm::json::Value(params), id); + }; + } + private: template using HandlerMap = llvm::StringMap>; @@ -178,6 +211,14 @@ class MessageHandler { HandlerMap)> methodHandlers; + /// A pair of (1) the original request's method name, and (2) the callback + /// function to be invoked for responses. + using ResponseHandlerTy = std::pair; + /// A mapping from request/response ID to response handler. + llvm::StringMap responseHandlers; + /// Mutex to guard insertion into the response handler map. + std::mutex responseHandlersMutex; + JSONTransport &transport; /// Mutex to guard sending output messages to the transport. diff --git a/mlir/lib/Tools/lsp-server-support/Transport.cpp b/mlir/lib/Tools/lsp-server-support/Transport.cpp index 339c5f3825165d..1e90ab32281f54 100644 --- a/mlir/lib/Tools/lsp-server-support/Transport.cpp +++ b/mlir/lib/Tools/lsp-server-support/Transport.cpp @@ -117,21 +117,29 @@ bool MessageHandler::onCall(llvm::StringRef method, llvm::json::Value params, bool MessageHandler::onReply(llvm::json::Value id, llvm::Expected result) { - // TODO: Add support for reply callbacks when support for outgoing messages is - // added. For now, we just log an error on any replies received. - Callback replyHandler = - [&id](llvm::Expected result) { - Logger::error( - "received a reply with ID {0}, but there was no such call", id); - if (!result) - llvm::consumeError(result.takeError()); - }; - - // Log and run the reply handler. - if (result) - replyHandler(std::move(result)); - else - replyHandler(result.takeError()); + // Find the response handler in the mapping. If it exists, move it out of the + // mapping and erase it. + ResponseHandlerTy responseHandler; + { + std::lock_guard responseHandlersLock(responseHandlersMutex); + auto it = responseHandlers.find(debugString(id)); + if (it != responseHandlers.end()) { + responseHandler = std::move(it->second); + responseHandlers.erase(it); + } + } + + // If we found a response handler, invoke it. Otherwise, log an error. + if (responseHandler.second) { + Logger::info("--> reply:{0}({1})", responseHandler.first, id); + responseHandler.second(std::move(id), std::move(result)); + } else { + Logger::error( + "received a reply with ID {0}, but there was no such outgoing request", + id); + if (!result) + llvm::consumeError(result.takeError()); + } return true; } diff --git a/mlir/unittests/Tools/lsp-server-support/Transport.cpp b/mlir/unittests/Tools/lsp-server-support/Transport.cpp index a086964cd3660c..fee21840595232 100644 --- a/mlir/unittests/Tools/lsp-server-support/Transport.cpp +++ b/mlir/unittests/Tools/lsp-server-support/Transport.cpp @@ -131,4 +131,43 @@ TEST_F(TransportInputTest, OutgoingNotification) { notifyFn(CompletionList{}); EXPECT_THAT(getOutput(), HasSubstr("\"method\":\"outgoing-notification\"")); } + +TEST_F(TransportInputTest, ResponseHandlerNotFound) { + // Unhandled responses are only reported via error logging. As a result, this + // test can't make any expectations -- but it prints the output anyway, by way + // of demonstration. + Logger::setLogLevel(Logger::Level::Error); + writeInput("{\"jsonrpc\":\"2.0\",\"id\":81,\"result\":null}\n"); + runTransport(); +} + +TEST_F(TransportInputTest, OutgoingRequest) { + // Make some outgoing requests. + int responseCallbackInvoked = 0; + auto callFn = getMessageHandler().outgoingRequest( + "outgoing-request", + [&responseCallbackInvoked](llvm::json::Value id, + llvm::Expected value) { + // Make expectations on the expected response. + EXPECT_EQ(id, 83); + ASSERT_TRUE((bool)value); + EXPECT_EQ(debugString(*value), "{\"foo\":6}"); + responseCallbackInvoked += 1; + llvm::outs() << "here!!!\n"; + }); + callFn({}, 82); + callFn({}, 83); + callFn({}, 84); + EXPECT_THAT(getOutput(), HasSubstr("\"method\":\"outgoing-request\"")); + EXPECT_EQ(responseCallbackInvoked, 0); + + // One of the requests receives a response. The message handler handles this + // response by invoking the callback from above. Subsequent responses with the + // same ID are ignored. + writeInput("{\"jsonrpc\":\"2.0\",\"id\":83,\"result\":{\"foo\":6}}\n" + "// -----\n" + "{\"jsonrpc\":\"2.0\",\"id\":83,\"result\":{\"bar\":8}}\n"); + runTransport(); + EXPECT_EQ(responseCallbackInvoked, 1); +} } // namespace From 1b70580dd867195b0442e582eccd42abc41ee12d Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Mon, 29 Apr 2024 15:56:41 -0700 Subject: [PATCH 02/65] Skip various tests under ASAN on green dragon (#90531) using the macOS version as a proxy. I can't reproduce any of these failures locally, but the tests all use pexpect and probably have bad timeout behavior under high load. --- lldb/packages/Python/lldbsuite/test/decorators.py | 9 ++++++++- lldb/test/API/driver/batch_mode/TestBatchMode.py | 4 ++++ lldb/test/API/driver/job_control/TestJobControl.py | 1 + lldb/test/API/driver/quit_speed/TestQuitWithProcess.py | 2 +- .../iohandler/sigint/TestProcessIOHandlerInterrupt.py | 1 + lldb/test/API/macosx/nslog/TestDarwinNSLogOutput.py | 8 ++++++-- lldb/test/API/terminal/TestSTTYBeforeAndAfter.py | 1 + 7 files changed, 22 insertions(+), 4 deletions(-) diff --git a/lldb/packages/Python/lldbsuite/test/decorators.py b/lldb/packages/Python/lldbsuite/test/decorators.py index 8e13aa6a13882f..7fb88cef165356 100644 --- a/lldb/packages/Python/lldbsuite/test/decorators.py +++ b/lldb/packages/Python/lldbsuite/test/decorators.py @@ -206,6 +206,7 @@ def _decorateTest( remote=None, dwarf_version=None, setting=None, + asan=None, ): def fn(actual_debug_info=None): skip_for_os = _match_decorator_property( @@ -256,6 +257,7 @@ def fn(actual_debug_info=None): ) ) skip_for_setting = (setting is None) or (setting in configuration.settings) + skip_for_asan = (asan is None) or is_running_under_asan() # For the test to be skipped, all specified (e.g. not None) parameters must be True. # An unspecified parameter means "any", so those are marked skip by default. And we skip @@ -273,6 +275,7 @@ def fn(actual_debug_info=None): (remote, skip_for_remote, "platform locality (remote/local)"), (dwarf_version, skip_for_dwarf_version, "dwarf version"), (setting, skip_for_setting, "setting"), + (asan, skip_for_asan, "running under asan"), ] reasons = [] final_skip_result = True @@ -331,6 +334,7 @@ def expectedFailureAll( remote=None, dwarf_version=None, setting=None, + asan=None, ): return _decorateTest( DecorateMode.Xfail, @@ -348,6 +352,7 @@ def expectedFailureAll( remote=remote, dwarf_version=dwarf_version, setting=setting, + asan=asan, ) @@ -356,7 +361,7 @@ def expectedFailureAll( # for example, # @skipIf, skip for all platform/compiler/arch, # @skipIf(compiler='gcc'), skip for gcc on all platform/architecture -# @skipIf(bugnumber, ["linux"], "gcc", ['>=', '4.9'], ['i386']), skip for gcc>=4.9 on linux with i386 +# @skipIf(bugnumber, ["linux"], "gcc", ['>=', '4.9'], ['i386']), skip for gcc>=4.9 on linux with i386 (all conditions must be true) def skipIf( bugnumber=None, oslist=None, @@ -372,6 +377,7 @@ def skipIf( remote=None, dwarf_version=None, setting=None, + asan=None, ): return _decorateTest( DecorateMode.Skip, @@ -389,6 +395,7 @@ def skipIf( remote=remote, dwarf_version=dwarf_version, setting=setting, + asan=asan, ) diff --git a/lldb/test/API/driver/batch_mode/TestBatchMode.py b/lldb/test/API/driver/batch_mode/TestBatchMode.py index 642dd47c6d4586..bc6f2daebbab81 100644 --- a/lldb/test/API/driver/batch_mode/TestBatchMode.py +++ b/lldb/test/API/driver/batch_mode/TestBatchMode.py @@ -13,6 +13,7 @@ class DriverBatchModeTest(PExpectTest): source = "main.c" + @skipIf(macos_version=["<", "14.0"], asan=True) @skipIf(oslist=["linux"], archs=["arm", "aarch64"]) # Randomly fails on buildbot @expectedFlakeyFreeBSD("llvm.org/pr25172 fails rarely on the buildbot") def test_batch_mode_run_crash(self): @@ -50,6 +51,7 @@ def test_batch_mode_run_crash(self): self.expect_prompt() self.expect("frame variable touch_me_not", substrs=["(char *) touch_me_not"]) + @skipIf(macos_version=["<", "14.0"], asan=True) @skipIf(oslist=["linux"], archs=["arm", "aarch64"]) # Randomly fails on buildbot @expectedFlakeyFreeBSD("llvm.org/pr25172 fails rarely on the buildbot") def test_batch_mode_run_exit(self): @@ -86,6 +88,7 @@ def test_batch_mode_run_exit(self): child.expect(pexpect.EOF) + @skipIf(macos_version=["<", "14.0"], asan=True) @skipIf(oslist=["linux"], archs=["arm", "aarch64"]) # Randomly fails on buildbot @expectedFlakeyFreeBSD("llvm.org/pr25172 fails rarely on the buildbot") def test_batch_mode_launch_stop_at_entry(self): @@ -125,6 +128,7 @@ def closeVictim(self): self.victim.close() self.victim = None + @skipIf(macos_version=["<", "14.0"], asan=True) @skipIf(oslist=["linux"], archs=["arm", "aarch64"]) # Randomly fails on buildbot @expectedFlakeyFreeBSD("llvm.org/pr25172 fails rarely on the buildbot") @expectedFailureNetBSD diff --git a/lldb/test/API/driver/job_control/TestJobControl.py b/lldb/test/API/driver/job_control/TestJobControl.py index 1a1739f4cb391d..648acb1d4730bc 100644 --- a/lldb/test/API/driver/job_control/TestJobControl.py +++ b/lldb/test/API/driver/job_control/TestJobControl.py @@ -8,6 +8,7 @@ class JobControlTest(PExpectTest): + @skipIf(macos_version=["<", "14.0"], asan=True) @skipIf(oslist=["linux"], archs=["arm", "aarch64"]) def test_job_control(self): def post_spawn(): diff --git a/lldb/test/API/driver/quit_speed/TestQuitWithProcess.py b/lldb/test/API/driver/quit_speed/TestQuitWithProcess.py index 42527c88b99213..c75ac977ea2094 100644 --- a/lldb/test/API/driver/quit_speed/TestQuitWithProcess.py +++ b/lldb/test/API/driver/quit_speed/TestQuitWithProcess.py @@ -31,4 +31,4 @@ def test_run_quit(self): print("Got launch message") child.sendline("quit") print("sent quit") - child.expect(pexpect.EOF, timeout=15) + child.expect(pexpect.EOF, timeout=60) diff --git a/lldb/test/API/iohandler/sigint/TestProcessIOHandlerInterrupt.py b/lldb/test/API/iohandler/sigint/TestProcessIOHandlerInterrupt.py index 8e19d56cd0c2f9..75ac0f6c0289a4 100644 --- a/lldb/test/API/iohandler/sigint/TestProcessIOHandlerInterrupt.py +++ b/lldb/test/API/iohandler/sigint/TestProcessIOHandlerInterrupt.py @@ -11,6 +11,7 @@ class TestCase(PExpectTest): + @skipIf(macos_version=["<", "14.0"], asan=True) @skipIf(compiler="clang", compiler_version=["<", "11.0"]) @skipIf(oslist=["linux"], archs=["arm", "aarch64"]) def test(self): diff --git a/lldb/test/API/macosx/nslog/TestDarwinNSLogOutput.py b/lldb/test/API/macosx/nslog/TestDarwinNSLogOutput.py index 15d9feb543895a..f6bda16560b962 100644 --- a/lldb/test/API/macosx/nslog/TestDarwinNSLogOutput.py +++ b/lldb/test/API/macosx/nslog/TestDarwinNSLogOutput.py @@ -20,8 +20,6 @@ class DarwinNSLogOutputTestCase(TestBase): NO_DEBUG_INFO_TESTCASE = True - @skipUnlessDarwin - @skipIfRemote # this test is currently written using lldb commands & assumes running on local system def setUp(self): # Call super's setUp(). TestBase.setUp(self) @@ -119,6 +117,9 @@ def do_test(self, expect_regexes=None, settings_commands=None): self.runCmd("process continue") self.expect(expect_regexes) + @skipIf(oslist=["linux"], archs=["arm", "aarch64"]) + @skipUnlessDarwin + @skipIfRemote # this test is currently written using lldb commands & assumes running on local system def test_nslog_output_is_displayed(self): """Test that NSLog() output shows up in the command-line debugger.""" self.do_test( @@ -131,6 +132,9 @@ def test_nslog_output_is_displayed(self): self.assertGreater(len(self.child.match.groups()), 0) self.assertEqual("This is a message from NSLog", self.child.match.group(1)) + @skipIf(oslist=["linux"], archs=["arm", "aarch64"]) + @skipUnlessDarwin + @skipIfRemote # this test is currently written using lldb commands & assumes running on local system def test_nslog_output_is_suppressed_with_env_var(self): """Test that NSLog() output does not show up with the ignore env var.""" # This test will only work properly on macOS 10.12+. Skip it on earlier versions. diff --git a/lldb/test/API/terminal/TestSTTYBeforeAndAfter.py b/lldb/test/API/terminal/TestSTTYBeforeAndAfter.py index 21aca5fc85d5f1..313a265319dbac 100644 --- a/lldb/test/API/terminal/TestSTTYBeforeAndAfter.py +++ b/lldb/test/API/terminal/TestSTTYBeforeAndAfter.py @@ -19,6 +19,7 @@ def classCleanup(cls): cls.RemoveTempFile("child_send2.txt") cls.RemoveTempFile("child_read2.txt") + @skipIf(macos_version=["<", "14.0"], asan=True) @add_test_categories(["pexpect"]) @no_debug_info_test def test_stty_dash_a_before_and_afetr_invoking_lldb_command(self): From c4c4e17c99f8d4f79bb9e1e3819d1d76e5e09ed1 Mon Sep 17 00:00:00 2001 From: Amir Ayupov Date: Tue, 30 Apr 2024 01:18:13 +0200 Subject: [PATCH 03/65] [BOLT] Use heuristic for matching split local functions (#90424) Use known order of BOLT split function symbols: fragment symbols immediately precede the parent fragment symbol. Depends On: https://github.com/llvm/llvm-project/pull/89648 Test Plan: Added register-fragments-bolt-symbols.s --- bolt/lib/Rewrite/RewriteInstance.cpp | 18 +++++++++++ .../X86/register-fragments-bolt-symbols.s | 32 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 bolt/test/X86/register-fragments-bolt-symbols.s diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp index 1c5ce5d0a6cd0b..644d87eeca42e6 100644 --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -1513,6 +1513,23 @@ void RewriteInstance::registerFragments() { StopSymbol = *FSI; uint64_t ParentAddress{0}; + + // BOLT split fragment symbols are emitted just before the main function + // symbol. + for (ELFSymbolRef NextSymbol = Symbol; NextSymbol < StopSymbol; + NextSymbol.moveNext()) { + StringRef Name = cantFail(NextSymbol.getName()); + if (Name == ParentName) { + ParentAddress = cantFail(NextSymbol.getValue()); + goto registerParent; + } + if (Name.starts_with(ParentName)) + // With multi-way splitting, there are multiple fragments with different + // suffixes. Parent follows the last fragment. + continue; + break; + } + // Iterate over local file symbols and check symbol names to match parent. for (ELFSymbolRef Symbol(FSI[-1]); Symbol < StopSymbol; Symbol.moveNext()) { if (cantFail(Symbol.getName()) == ParentName) { @@ -1521,6 +1538,7 @@ void RewriteInstance::registerFragments() { } } +registerParent: // No local parent is found, use global parent function. if (!ParentAddress) if (BinaryData *ParentBD = BC->getBinaryDataByName(ParentName)) diff --git a/bolt/test/X86/register-fragments-bolt-symbols.s b/bolt/test/X86/register-fragments-bolt-symbols.s new file mode 100644 index 00000000000000..fa9b70e0b2d891 --- /dev/null +++ b/bolt/test/X86/register-fragments-bolt-symbols.s @@ -0,0 +1,32 @@ +# Test the heuristics for matching BOLT-added split functions. + +# RUN: llvm-mc --filetype=obj --triple x86_64-unknown-unknown %S/cdsplit-symbol-names.s -o %t.main.o +# RUN: llvm-mc --filetype=obj --triple x86_64-unknown-unknown %s -o %t.chain.o +# RUN: link_fdata %S/cdsplit-symbol-names.s %t.main.o %t.fdata +# RUN: sed -i 's|chain|chain/2|g' %t.fdata +# RUN: llvm-strip --strip-unneeded %t.main.o +# RUN: llvm-objcopy --localize-symbol=chain %t.main.o +# RUN: %clang %cflags %t.chain.o %t.main.o -o %t.exe -Wl,-q +# RUN: llvm-bolt %t.exe -o %t.bolt --split-functions --split-strategy=randomN \ +# RUN: --reorder-blocks=ext-tsp --enable-bat --bolt-seed=7 --data=%t.fdata +# RUN: llvm-objdump --syms %t.bolt | FileCheck %s --check-prefix=CHECK-SYMS + +# RUN: link_fdata %s %t.bolt %t.preagg PREAGG +# PREAGG: B X:0 #chain.cold.0# 1 0 +# RUN: perf2bolt %t.bolt -p %t.preagg --pa -o %t.bat.fdata -w %t.bat.yaml -v=1 \ +# RUN: | FileCheck %s --check-prefix=CHECK-REGISTER + +# CHECK-SYMS: l df *ABS* [[#]] chain.s +# CHECK-SYMS: l F .bolt.org.text [[#]] chain +# CHECK-SYMS: l F .text.cold [[#]] chain.cold.0 +# CHECK-SYMS: l F .text [[#]] chain +# CHECK-SYMS: l df *ABS* [[#]] bolt-pseudo.o + +# CHECK-REGISTER: BOLT-INFO: marking chain.cold.0/1(*2) as a fragment of chain/2(*2) + +.file "chain.s" + .text + .type chain, @function +chain: + ret + .size chain, .-chain From 5e9937d1b3ada9c686505c5f2c1e1b054ad9edc2 Mon Sep 17 00:00:00 2001 From: Michael Flanders Date: Mon, 29 Apr 2024 18:25:45 -0500 Subject: [PATCH 04/65] [libc][math] Adds entrypoint and tests for nearbyintf128,scalbnf128 (#88443) Closes #84689. Adding @lntue for review. I was curious about the implementation of `round_using_current_rounding_mode` used for the `nearbyint` functions. It has one of the rounding modes as unreachable ([here](https://github.com/llvm/llvm-project/blob/main/libc/src/__support/FPUtil/NearestIntegerOperations.h#L243)), and I was wondering if this was okay for the `nearbyint` functions. --------- Co-authored-by: Michael Flanders --- libc/config/linux/aarch64/entrypoints.txt | 2 + libc/config/linux/riscv/entrypoints.txt | 2 + libc/config/linux/x86_64/entrypoints.txt | 2 + libc/docs/math/index.rst | 4 +- libc/spec/stdc.td | 2 + libc/src/math/CMakeLists.txt | 2 + libc/src/math/generic/CMakeLists.txt | 32 +++++- libc/src/math/generic/nearbyintf128.cpp | 19 ++++ libc/src/math/generic/scalbnf128.cpp | 27 +++++ libc/src/math/nearbyintf128.h | 20 ++++ libc/src/math/scalbnf128.h | 20 ++++ libc/test/UnitTest/FPMatcher.h | 21 ++++ libc/test/src/math/CMakeLists.txt | 15 +++ libc/test/src/math/scalbnf128_test.cpp | 13 +++ libc/test/src/math/smoke/CMakeLists.txt | 68 +++++++++++- libc/test/src/math/smoke/NearbyIntTest.h | 100 ++++++++++++++++++ libc/test/src/math/smoke/nearbyint_test.cpp | 13 +++ .../src/math/smoke/nearbyintf128_test.cpp | 13 +++ libc/test/src/math/smoke/nearbyintf_test.cpp | 13 +++ libc/test/src/math/smoke/nearbyintl_test.cpp | 13 +++ libc/test/src/math/smoke/scalbnf128_test.cpp | 13 +++ 21 files changed, 406 insertions(+), 8 deletions(-) create mode 100644 libc/src/math/generic/nearbyintf128.cpp create mode 100644 libc/src/math/generic/scalbnf128.cpp create mode 100644 libc/src/math/nearbyintf128.h create mode 100644 libc/src/math/scalbnf128.h create mode 100644 libc/test/src/math/scalbnf128_test.cpp create mode 100644 libc/test/src/math/smoke/NearbyIntTest.h create mode 100644 libc/test/src/math/smoke/nearbyint_test.cpp create mode 100644 libc/test/src/math/smoke/nearbyintf128_test.cpp create mode 100644 libc/test/src/math/smoke/nearbyintf_test.cpp create mode 100644 libc/test/src/math/smoke/nearbyintl_test.cpp create mode 100644 libc/test/src/math/smoke/scalbnf128_test.cpp diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index 1ac6bd93000082..eedd9342a09f02 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -527,11 +527,13 @@ if(LIBC_TYPES_HAS_FLOAT128) libc.src.math.lroundf128 libc.src.math.modff128 libc.src.math.nanf128 + libc.src.math.nearbyintf128 libc.src.math.nextafterf128 libc.src.math.nextdownf128 libc.src.math.nextupf128 libc.src.math.rintf128 libc.src.math.roundf128 + libc.src.math.scalbnf128 libc.src.math.sqrtf128 libc.src.math.truncf128 libc.src.math.ufromfpf128 diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt index 87e82e5eb9a067..4ddc1fb365e155 100644 --- a/libc/config/linux/riscv/entrypoints.txt +++ b/libc/config/linux/riscv/entrypoints.txt @@ -535,11 +535,13 @@ if(LIBC_TYPES_HAS_FLOAT128) libc.src.math.lroundf128 libc.src.math.modff128 libc.src.math.nanf128 + libc.src.math.nearbyintf128 libc.src.math.nextafterf128 libc.src.math.nextdownf128 libc.src.math.nextupf128 libc.src.math.rintf128 libc.src.math.roundf128 + libc.src.math.scalbnf128 libc.src.math.sqrtf128 libc.src.math.truncf128 libc.src.math.ufromfpf128 diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index a8e28992766712..2576e4a92e85e2 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -560,12 +560,14 @@ if(LIBC_TYPES_HAS_FLOAT128) libc.src.math.lroundf128 libc.src.math.modff128 libc.src.math.nanf128 + libc.src.math.nearbyintf128 libc.src.math.nextafterf128 libc.src.math.nextdownf128 libc.src.math.nextupf128 libc.src.math.rintf128 libc.src.math.roundevenf128 libc.src.math.roundf128 + libc.src.math.scalbnf128 libc.src.math.sqrtf128 libc.src.math.truncf128 libc.src.math.ufromfpf128 diff --git a/libc/docs/math/index.rst b/libc/docs/math/index.rst index 7a7b6c9c8db5de..28503e1d13ab5a 100644 --- a/libc/docs/math/index.rst +++ b/libc/docs/math/index.rst @@ -188,7 +188,7 @@ Basic Operations +------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+ | nan | |check| | |check| | |check| | | |check| | 7.12.11.2 | F.10.8.2 | +------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+ -| nearbyint | |check| | |check| | |check| | | | 7.12.9.3 | F.10.6.3 | +| nearbyint | |check| | |check| | |check| | | |check| | 7.12.9.3 | F.10.6.3 | +------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+ | nextafter | |check| | |check| | |check| | | |check| | 7.12.11.3 | F.10.8.3 | +------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+ @@ -208,7 +208,7 @@ Basic Operations +------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+ | roundeven | |check| | |check| | |check| | | |check| | 7.12.9.8 | F.10.6.8 | +------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+ -| scalbn | |check| | |check| | |check| | | | 7.12.6.19 | F.10.3.19 | +| scalbn | |check| | |check| | |check| | | |check| | 7.12.6.19 | F.10.3.19 | +------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+ | trunc | |check| | |check| | |check| | | |check| | 7.12.9.9 | F.10.6.9 | +------------------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+ diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td index 01aa7c70b3b9df..7a9031a0a330bb 100644 --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -604,6 +604,7 @@ def StdC : StandardSpec<"stdc"> { FunctionSpec<"nearbyint", RetValSpec, [ArgSpec]>, FunctionSpec<"nearbyintf", RetValSpec, [ArgSpec]>, FunctionSpec<"nearbyintl", RetValSpec, [ArgSpec]>, + GuardedFunctionSpec<"nearbyintf128", RetValSpec, [ArgSpec], "LIBC_TYPES_HAS_FLOAT128">, FunctionSpec<"nextafterf", RetValSpec, [ArgSpec, ArgSpec]>, FunctionSpec<"nextafter", RetValSpec, [ArgSpec, ArgSpec]>, @@ -647,6 +648,7 @@ def StdC : StandardSpec<"stdc"> { FunctionSpec<"scalbn", RetValSpec, [ArgSpec, ArgSpec]>, FunctionSpec<"scalbnf", RetValSpec, [ArgSpec, ArgSpec]>, FunctionSpec<"scalbnl", RetValSpec, [ArgSpec, ArgSpec]>, + GuardedFunctionSpec<"scalbnf128", RetValSpec, [ArgSpec, ArgSpec], "LIBC_TYPES_HAS_FLOAT128">, FunctionSpec<"nanf", RetValSpec, [ArgSpec]>, FunctionSpec<"nan", RetValSpec, [ArgSpec]>, diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt index e8f699fabe3655..c34c58575441d3 100644 --- a/libc/src/math/CMakeLists.txt +++ b/libc/src/math/CMakeLists.txt @@ -252,6 +252,7 @@ add_math_entrypoint_object(nanf128) add_math_entrypoint_object(nearbyint) add_math_entrypoint_object(nearbyintf) add_math_entrypoint_object(nearbyintl) +add_math_entrypoint_object(nearbyintf128) add_math_entrypoint_object(nextafter) add_math_entrypoint_object(nextafterf) @@ -301,6 +302,7 @@ add_math_entrypoint_object(roundevenf128) add_math_entrypoint_object(scalbn) add_math_entrypoint_object(scalbnf) add_math_entrypoint_object(scalbnl) +add_math_entrypoint_object(scalbnf128) add_math_entrypoint_object(sincos) add_math_entrypoint_object(sincosf) diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt index 574e000b82a8fc..daaf505008ca11 100644 --- a/libc/src/math/generic/CMakeLists.txt +++ b/libc/src/math/generic/CMakeLists.txt @@ -704,7 +704,7 @@ add_entrypoint_object( DEPENDS libc.src.__support.FPUtil.nearest_integer_operations COMPILE_OPTIONS - -O2 + -O3 ) add_entrypoint_object( @@ -716,7 +716,7 @@ add_entrypoint_object( DEPENDS libc.src.__support.FPUtil.nearest_integer_operations COMPILE_OPTIONS - -O2 + -O3 ) add_entrypoint_object( @@ -728,7 +728,20 @@ add_entrypoint_object( DEPENDS libc.src.__support.FPUtil.nearest_integer_operations COMPILE_OPTIONS - -O2 + -O3 +) + +add_entrypoint_object( + nearbyintf128 + SRCS + nearbyintf128.cpp + HDRS + ../nearbyintf128.h + DEPENDS + libc.src.__support.macros.properties.types + libc.src.__support.FPUtil.nearest_integer_operations + COMPILE_OPTIONS + -O3 ) add_object_library( @@ -2949,6 +2962,19 @@ add_entrypoint_object( -O3 ) +add_entrypoint_object( + scalbnf128 + SRCS + scalbnf128.cpp + HDRS + ../scalbnf128.h + DEPENDS + libc.src.__support.macros.properties.types + libc.src.__support.FPUtil.manipulation_functions + COMPILE_OPTIONS + -O3 +) + add_entrypoint_object( fmaf SRCS diff --git a/libc/src/math/generic/nearbyintf128.cpp b/libc/src/math/generic/nearbyintf128.cpp new file mode 100644 index 00000000000000..fca3587f5b58ad --- /dev/null +++ b/libc/src/math/generic/nearbyintf128.cpp @@ -0,0 +1,19 @@ +//===-- Implementation of nearbyintf128 function --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/math/nearbyintf128.h" +#include "src/__support/FPUtil/NearestIntegerOperations.h" +#include "src/__support/common.h" + +namespace LIBC_NAMESPACE { + +LLVM_LIBC_FUNCTION(float128, nearbyintf128, (float128 x)) { + return fputil::round_using_current_rounding_mode(x); +} + +} // namespace LIBC_NAMESPACE diff --git a/libc/src/math/generic/scalbnf128.cpp b/libc/src/math/generic/scalbnf128.cpp new file mode 100644 index 00000000000000..be3d29ed27e985 --- /dev/null +++ b/libc/src/math/generic/scalbnf128.cpp @@ -0,0 +1,27 @@ +//===-- Implementation of scalbnf128 function -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/math/scalbnf128.h" +#include "src/__support/FPUtil/ManipulationFunctions.h" +#include "src/__support/common.h" + +namespace LIBC_NAMESPACE { + +LLVM_LIBC_FUNCTION(float128, scalbnf128, (float128 x, int n)) { +// TODO: should be switched to use `FLT_RADIX` in hdr/float_macros.h" instead +// see: https://github.com/llvm/llvm-project/issues/90496 +#if !defined(__FLT_RADIX__) +#error __FLT_RADIX__ undefined. +#elif __FLT_RADIX__ != 2 +#error __FLT_RADIX__!=2, unimplemented. +#else + return fputil::ldexp(x, n); +#endif +} + +} // namespace LIBC_NAMESPACE diff --git a/libc/src/math/nearbyintf128.h b/libc/src/math/nearbyintf128.h new file mode 100644 index 00000000000000..d12754a4810092 --- /dev/null +++ b/libc/src/math/nearbyintf128.h @@ -0,0 +1,20 @@ +//===-- Implementation header for nearbyintf128 -----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_MATH_NEARBYINTF128_H +#define LLVM_LIBC_SRC_MATH_NEARBYINTF128_H + +#include "src/__support/macros/properties/types.h" + +namespace LIBC_NAMESPACE { + +float128 nearbyintf128(float128 x); + +} // namespace LIBC_NAMESPACE + +#endif // LLVM_LIBC_SRC_MATH_NEARBYINTF128_H diff --git a/libc/src/math/scalbnf128.h b/libc/src/math/scalbnf128.h new file mode 100644 index 00000000000000..bd3b560fb7cc42 --- /dev/null +++ b/libc/src/math/scalbnf128.h @@ -0,0 +1,20 @@ +//===-- Implementation header for scalbnf128 --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_MATH_SCALBNF128_H +#define LLVM_LIBC_SRC_MATH_SCALBNF128_H + +#include "src/__support/macros/properties/types.h" + +namespace LIBC_NAMESPACE { + +float128 scalbnf128(float128 x, int n); + +} // namespace LIBC_NAMESPACE + +#endif // LLVM_LIBC_SRC_MATH_SCALBNF128_H diff --git a/libc/test/UnitTest/FPMatcher.h b/libc/test/UnitTest/FPMatcher.h index a76e0b8ef6f6f0..c58c322c981e43 100644 --- a/libc/test/UnitTest/FPMatcher.h +++ b/libc/test/UnitTest/FPMatcher.h @@ -219,4 +219,25 @@ template struct FPTest : public Test { } \ } while (0) +#define EXPECT_FP_EQ_ROUNDING_MODE(expected, actual, rounding_mode) \ + do { \ + using namespace LIBC_NAMESPACE::fputil::testing; \ + ForceRoundingMode __r((rounding_mode)); \ + if (__r.success) { \ + EXPECT_FP_EQ((expected), (actual)); \ + } \ + } while (0) + +#define EXPECT_FP_EQ_ROUNDING_NEAREST(expected, actual) \ + EXPECT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Nearest) + +#define EXPECT_FP_EQ_ROUNDING_UPWARD(expected, actual) \ + EXPECT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Upward) + +#define EXPECT_FP_EQ_ROUNDING_DOWNWARD(expected, actual) \ + EXPECT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Downward) + +#define EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO(expected, actual) \ + EXPECT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::TowardZero) + #endif // LLVM_LIBC_TEST_UNITTEST_FPMATCHER_H diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt index 55119868bdaa19..102188c332e408 100644 --- a/libc/test/src/math/CMakeLists.txt +++ b/libc/test/src/math/CMakeLists.txt @@ -1650,6 +1650,21 @@ add_fp_unittest( libc.src.__support.FPUtil.normal_float ) +add_fp_unittest( + scalbnf128_test + NEED_MPFR + SUITE + libc-math-unittests + SRCS + scalbnf128_test.cpp + HDRS + ScalbnTest.h + DEPENDS + libc.src.math.scalbnf128 + libc.src.__support.FPUtil.fp_bits + libc.src.__support.FPUtil.normal_float +) + add_fp_unittest( erff_test NEED_MPFR diff --git a/libc/test/src/math/scalbnf128_test.cpp b/libc/test/src/math/scalbnf128_test.cpp new file mode 100644 index 00000000000000..dc259de211489e --- /dev/null +++ b/libc/test/src/math/scalbnf128_test.cpp @@ -0,0 +1,13 @@ +//===-- Unittests for scalbnf128 ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ScalbnTest.h" + +#include "src/math/scalbnf128.h" + +LIST_SCALBN_TESTS(float128, LIBC_NAMESPACE::scalbnf128) diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt index 22c59c97f6c7fd..112b2985829ca3 100644 --- a/libc/test/src/math/smoke/CMakeLists.txt +++ b/libc/test/src/math/smoke/CMakeLists.txt @@ -2188,6 +2188,58 @@ add_fp_unittest( UNIT_TEST_ONLY ) +add_fp_unittest( + nearbyint_test + SUITE + libc-math-smoke-tests + SRCS + nearbyint_test.cpp + HDRS + NearbyIntTest.h + DEPENDS + libc.hdr.fenv_macros + libc.src.math.nearbyint +) + +add_fp_unittest( + nearbyintf_test + SUITE + libc-math-smoke-tests + SRCS + nearbyintf_test.cpp + HDRS + NearbyIntTest.h + DEPENDS + libc.hdr.fenv_macros + libc.src.math.nearbyintf +) + +add_fp_unittest( + nearbyintl_test + SUITE + libc-math-smoke-tests + SRCS + nearbyintl_test.cpp + HDRS + NearbyIntTest.h + DEPENDS + libc.hdr.fenv_macros + libc.src.math.nearbyintl +) + +add_fp_unittest( + nearbyintf128_test + SUITE + libc-math-smoke-tests + SRCS + nearbyintf128_test.cpp + HDRS + NearbyIntTest.h + DEPENDS + libc.hdr.fenv_macros + libc.src.math.nearbyintf128 +) + add_fp_unittest( nextafter_test SUITE @@ -2739,7 +2791,6 @@ add_fp_unittest( DEPENDS libc.src.math.scalbn libc.src.__support.FPUtil.fp_bits - libc.src.__support.FPUtil.normal_float ) add_fp_unittest( @@ -2753,7 +2804,6 @@ add_fp_unittest( DEPENDS libc.src.math.scalbnf libc.src.__support.FPUtil.fp_bits - libc.src.__support.FPUtil.normal_float ) add_fp_unittest( @@ -2767,7 +2817,19 @@ add_fp_unittest( DEPENDS libc.src.math.scalbnl libc.src.__support.FPUtil.fp_bits - libc.src.__support.FPUtil.normal_float +) + +add_fp_unittest( + scalbnf128_test + SUITE + libc-math-smoke-tests + SRCS + scalbnf128_test.cpp + HDRS + ScalbnTest.h + DEPENDS + libc.src.math.scalbnf128 + libc.src.__support.FPUtil.fp_bits ) add_fp_unittest( diff --git a/libc/test/src/math/smoke/NearbyIntTest.h b/libc/test/src/math/smoke/NearbyIntTest.h new file mode 100644 index 00000000000000..0051ff9447a7ed --- /dev/null +++ b/libc/test/src/math/smoke/NearbyIntTest.h @@ -0,0 +1,100 @@ +//===-- Utility class to test different flavors of nearbyint ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_TEST_SRC_MATH_NEARBYINTTEST_H +#define LLVM_LIBC_TEST_SRC_MATH_NEARBYINTTEST_H + +#include "hdr/fenv_macros.h" +#include "src/__support/FPUtil/FEnvImpl.h" +#include "src/__support/FPUtil/FPBits.h" +#include "test/UnitTest/FPMatcher.h" +#include "test/UnitTest/Test.h" + +static constexpr int ROUNDING_MODES[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, + FE_TONEAREST}; + +template +class NearbyIntTestTemplate : public LIBC_NAMESPACE::testing::Test { + + DECLARE_SPECIAL_CONSTANTS(T) + +public: + typedef T (*NearbyIntFunc)(T); + + void testNaN(NearbyIntFunc func) { + EXPECT_FP_EQ_ALL_ROUNDING(func(aNaN), aNaN); + } + + void testInfinities(NearbyIntFunc func) { + EXPECT_FP_EQ_ALL_ROUNDING(func(inf), inf); + EXPECT_FP_EQ_ALL_ROUNDING(func(neg_inf), neg_inf); + } + + void testZeroes(NearbyIntFunc func) { + EXPECT_FP_EQ_ALL_ROUNDING(func(zero), zero); + EXPECT_FP_EQ_ALL_ROUNDING(func(neg_zero), neg_zero); + } + + void testIntegers(NearbyIntFunc func) { + EXPECT_FP_EQ_ALL_ROUNDING(func(T(1.0)), T(1.0)); + EXPECT_FP_EQ_ALL_ROUNDING(func(T(-1.0)), T(-1.0)); + + EXPECT_FP_EQ_ALL_ROUNDING(func(T(1234.0)), T(1234.0)); + EXPECT_FP_EQ_ALL_ROUNDING(func(T(-1234.0)), T(-1234.0)); + + EXPECT_FP_EQ_ALL_ROUNDING(func(T(10.0)), T(10.0)); + EXPECT_FP_EQ_ALL_ROUNDING(func(T(-10.0)), T(-10.0)); + + FPBits ints_start(T(0)); + ints_start.set_biased_exponent(FPBits::SIG_LEN + FPBits::EXP_BIAS); + T expected = ints_start.get_val(); + EXPECT_FP_EQ_ALL_ROUNDING(func(expected), expected); + } + + void testSubnormalToNearest(NearbyIntFunc func) { + ASSERT_FP_EQ(func(min_denormal), zero); + ASSERT_FP_EQ(func(-min_denormal), neg_zero); + } + + void testSubnormalTowardZero(NearbyIntFunc func) { + EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO(func(min_denormal), zero); + EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO(func(-min_denormal), neg_zero); + } + + void testSubnormalToPosInf(NearbyIntFunc func) { + EXPECT_FP_EQ_ROUNDING_UPWARD(func(min_denormal), FPBits::one().get_val()); + EXPECT_FP_EQ_ROUNDING_UPWARD(func(-min_denormal), neg_zero); + } + + void testSubnormalToNegInf(NearbyIntFunc func) { + T negative_one = FPBits::one(Sign::NEG).get_val(); + EXPECT_FP_EQ_ROUNDING_DOWNWARD(func(min_denormal), zero); + EXPECT_FP_EQ_ROUNDING_DOWNWARD(func(-min_denormal), negative_one); + } +}; + +#define LIST_NEARBYINT_TESTS(T, func) \ + using LlvmLibcNearbyIntTest = NearbyIntTestTemplate; \ + TEST_F(LlvmLibcNearbyIntTest, TestNaN) { testNaN(&func); } \ + TEST_F(LlvmLibcNearbyIntTest, TestInfinities) { testInfinities(&func); } \ + TEST_F(LlvmLibcNearbyIntTest, TestZeroes) { testZeroes(&func); } \ + TEST_F(LlvmLibcNearbyIntTest, TestIntegers) { testIntegers(&func); } \ + TEST_F(LlvmLibcNearbyIntTest, TestSubnormalToNearest) { \ + testSubnormalToNearest(&func); \ + } \ + TEST_F(LlvmLibcNearbyIntTest, TestSubnormalTowardZero) { \ + testSubnormalTowardZero(&func); \ + } \ + TEST_F(LlvmLibcNearbyIntTest, TestSubnormalToPosInf) { \ + testSubnormalToPosInf(&func); \ + } \ + TEST_F(LlvmLibcNearbyIntTest, TestSubnormalToNegInf) { \ + testSubnormalToNegInf(&func); \ + } + +#endif // LLVM_LIBC_TEST_SRC_MATH_NEARBYINTTEST_H diff --git a/libc/test/src/math/smoke/nearbyint_test.cpp b/libc/test/src/math/smoke/nearbyint_test.cpp new file mode 100644 index 00000000000000..11a5c3372e73ea --- /dev/null +++ b/libc/test/src/math/smoke/nearbyint_test.cpp @@ -0,0 +1,13 @@ +//===-- Unittests for nearbyint -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "NearbyIntTest.h" + +#include "src/math/nearbyint.h" + +LIST_NEARBYINT_TESTS(double, LIBC_NAMESPACE::nearbyint) diff --git a/libc/test/src/math/smoke/nearbyintf128_test.cpp b/libc/test/src/math/smoke/nearbyintf128_test.cpp new file mode 100644 index 00000000000000..98fbb2858fafa6 --- /dev/null +++ b/libc/test/src/math/smoke/nearbyintf128_test.cpp @@ -0,0 +1,13 @@ +//===-- Unittests for nearbyintf128 ---------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "NearbyIntTest.h" + +#include "src/math/nearbyintf128.h" + +LIST_NEARBYINT_TESTS(float128, LIBC_NAMESPACE::nearbyintf128) diff --git a/libc/test/src/math/smoke/nearbyintf_test.cpp b/libc/test/src/math/smoke/nearbyintf_test.cpp new file mode 100644 index 00000000000000..fd26153cfffb94 --- /dev/null +++ b/libc/test/src/math/smoke/nearbyintf_test.cpp @@ -0,0 +1,13 @@ +//===-- Unittests for nearbyintf ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "NearbyIntTest.h" + +#include "src/math/nearbyintf.h" + +LIST_NEARBYINT_TESTS(float, LIBC_NAMESPACE::nearbyintf) diff --git a/libc/test/src/math/smoke/nearbyintl_test.cpp b/libc/test/src/math/smoke/nearbyintl_test.cpp new file mode 100644 index 00000000000000..a6d81a1439e17c --- /dev/null +++ b/libc/test/src/math/smoke/nearbyintl_test.cpp @@ -0,0 +1,13 @@ +//===-- Unittests for nearbyintl ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "NearbyIntTest.h" + +#include "src/math/nearbyintl.h" + +LIST_NEARBYINT_TESTS(long double, LIBC_NAMESPACE::nearbyintl) diff --git a/libc/test/src/math/smoke/scalbnf128_test.cpp b/libc/test/src/math/smoke/scalbnf128_test.cpp new file mode 100644 index 00000000000000..dc259de211489e --- /dev/null +++ b/libc/test/src/math/smoke/scalbnf128_test.cpp @@ -0,0 +1,13 @@ +//===-- Unittests for scalbnf128 ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ScalbnTest.h" + +#include "src/math/scalbnf128.h" + +LIST_SCALBN_TESTS(float128, LIBC_NAMESPACE::scalbnf128) From ae7ce1c6e728a262049c998c667b79165b285bd8 Mon Sep 17 00:00:00 2001 From: Kazu Hirata Date: Mon, 29 Apr 2024 16:30:57 -0700 Subject: [PATCH 05/65] [CodeGen] Remove extraneous ArrayRef (NFC) (#90423) We don't need to explicitly create an instance of ArrayRef here because getIndexedOffsetInType takes ArrayRef, and ArrayRef can be implicitly constructed from a C array. --- llvm/lib/CodeGen/InterleavedLoadCombinePass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/CodeGen/InterleavedLoadCombinePass.cpp b/llvm/lib/CodeGen/InterleavedLoadCombinePass.cpp index f2d5c3c867c2dd..e5f164b182723f 100644 --- a/llvm/lib/CodeGen/InterleavedLoadCombinePass.cpp +++ b/llvm/lib/CodeGen/InterleavedLoadCombinePass.cpp @@ -890,7 +890,7 @@ struct VectorInfo { ConstantInt::get(Type::getInt32Ty(LI->getContext()), 0), ConstantInt::get(Type::getInt32Ty(LI->getContext()), i), }; - int64_t Ofs = DL.getIndexedOffsetInType(Result.VTy, ArrayRef(Idx, 2)); + int64_t Ofs = DL.getIndexedOffsetInType(Result.VTy, Idx); Result.EI[i] = ElementInfo(Offset + Ofs, i == 0 ? LI : nullptr); } From 1cb33713910501c6352d0eb2a15b7a15e6e18695 Mon Sep 17 00:00:00 2001 From: David Blaikie Date: Mon, 29 Apr 2024 23:50:18 +0000 Subject: [PATCH 06/65] Ensure test writes objects to test temp dir --- clang/test/CodeGenCoroutines/coro-elide-thinlto.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/test/CodeGenCoroutines/coro-elide-thinlto.cpp b/clang/test/CodeGenCoroutines/coro-elide-thinlto.cpp index 293aef6781677f..790899486ec9d1 100644 --- a/clang/test/CodeGenCoroutines/coro-elide-thinlto.cpp +++ b/clang/test/CodeGenCoroutines/coro-elide-thinlto.cpp @@ -2,10 +2,10 @@ // This test is adapted from coro-elide.cpp and splits functions into two files. // // RUN: split-file %s %t -// RUN: %clang --target=x86_64-linux -std=c++20 -O2 -flto=thin -I %S -c %t/coro-elide-callee.cpp -o coro-elide-callee.o -// RUN: %clang --target=x86_64-linux -std=c++20 -O2 -flto=thin -I %S -c %t/coro-elide-caller.cpp -o coro-elide-caller.o -// RUN: llvm-lto -thinlto coro-elide-callee.o coro-elide-caller.o -o summary -// RUN: %clang_cc1 -O2 -x ir coro-elide-caller.o -fthinlto-index=summary.thinlto.bc -emit-llvm -o - | FileCheck %s +// RUN: %clang --target=x86_64-linux -std=c++20 -O2 -flto=thin -I %S -c %t/coro-elide-callee.cpp -o %t/coro-elide-callee.o +// RUN: %clang --target=x86_64-linux -std=c++20 -O2 -flto=thin -I %S -c %t/coro-elide-caller.cpp -o %t/coro-elide-caller.o +// RUN: llvm-lto -thinlto %t/coro-elide-callee.o %t/coro-elide-caller.o -o summary +// RUN: %clang_cc1 -O2 -x ir %t/coro-elide-caller.o -fthinlto-index=summary.thinlto.bc -emit-llvm -o - | FileCheck %s //--- coro-elide-task.h #pragma once From 6566ffdf8a543f50b75e9b3c66d771a3a9eb1560 Mon Sep 17 00:00:00 2001 From: Kevin Frei Date: Mon, 29 Apr 2024 17:00:19 -0700 Subject: [PATCH 07/65] Clean up the GSym error aggregation code, and pass the aggregator by reference (#89688) There was a problem with `llvm-gsymutil`s error aggregation code not properly collecting aggregate errors. The was that the output aggregator collecting errors from other threads wasn't being passed by reference, so it was merging them into a copy of the app-wide output aggregator. While I was at it, I added a better comment above the "Merge" code and made it a bit more efficient, after learning more details about `emplace` vs. `insert` or `operator[]` on `std::map`'s. Co-authored-by: Kevin Frei --- llvm/include/llvm/DebugInfo/GSYM/OutputAggregator.h | 9 ++++----- llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/llvm/include/llvm/DebugInfo/GSYM/OutputAggregator.h b/llvm/include/llvm/DebugInfo/GSYM/OutputAggregator.h index 8deea3bff1ed76..35ef0a8bc89085 100644 --- a/llvm/include/llvm/DebugInfo/GSYM/OutputAggregator.h +++ b/llvm/include/llvm/DebugInfo/GSYM/OutputAggregator.h @@ -57,13 +57,12 @@ class OutputAggregator { } // For multi-threaded usage, we can collect stuff in another aggregator, - // then merge it in here + // then merge it in here. Note that this is *not* thread safe. It is up to + // the caller to ensure that this is only called from one thread at a time. void Merge(const OutputAggregator &other) { for (auto &&[name, count] : other.Aggregation) { - auto it = Aggregation.find(name); - if (it == Aggregation.end()) - Aggregation.emplace(name, count); - else + auto [it, inserted] = Aggregation.emplace(name, count); + if (!inserted) it->second += count; } } diff --git a/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp b/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp index ff6b560d11726b..601686fdd3dd51 100644 --- a/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp +++ b/llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp @@ -612,7 +612,7 @@ Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) { DWARFDie Die = getDie(*CU); if (Die) { CUInfo CUI(DICtx, dyn_cast(CU.get())); - pool.async([this, CUI, &LogMutex, Out, Die]() mutable { + pool.async([this, CUI, &LogMutex, &Out, Die]() mutable { std::string storage; raw_string_ostream StrStream(storage); OutputAggregator ThreadOut(Out.GetOS() ? &StrStream : nullptr); From ce3485a0cd12b45c725f008a7836e71a1d72df49 Mon Sep 17 00:00:00 2001 From: Max Winkler Date: Mon, 29 Apr 2024 20:29:34 -0400 Subject: [PATCH 08/65] [llvm][GlobalOpt] Remove empty atexit destructors/handlers (#88836) https://godbolt.org/z/frjhqMKqc for an example. Removal of allocations due to empty `__cxa_atexit` destructor calls is done by the following globalopt pass. This pass currently does not look for `atexit` handlers generated for platforms that do not use `__cxa_atexit`. By default Win32 and AIX use `atexit`. I don't see an easy way to only remove `atexit` calls that the compiler generated without looking at the generated mangled name of the atexit handler that is being registered. However we can easily remove all `atexit` calls that register empty handlers since it is trivial to ensure the removed call still returns `0` which is the value for success. --- .../llvm/Analysis/TargetLibraryInfo.def | 5 ++ llvm/lib/Transforms/IPO/GlobalOpt.cpp | 45 +++++++++------- llvm/test/Transforms/GlobalOpt/atexit-dtor.ll | 54 +++++++++++++++++++ .../tools/llvm-tli-checker/ps4-tli-check.yaml | 12 +++-- .../Analysis/TargetLibraryInfoTest.cpp | 2 + 5 files changed, 96 insertions(+), 22 deletions(-) create mode 100644 llvm/test/Transforms/GlobalOpt/atexit-dtor.ll diff --git a/llvm/include/llvm/Analysis/TargetLibraryInfo.def b/llvm/include/llvm/Analysis/TargetLibraryInfo.def index 37221eb9e47115..717693a7cf63c1 100644 --- a/llvm/include/llvm/Analysis/TargetLibraryInfo.def +++ b/llvm/include/llvm/Analysis/TargetLibraryInfo.def @@ -471,6 +471,11 @@ TLI_DEFINE_ENUM_INTERNAL(cxa_atexit) TLI_DEFINE_STRING_INTERNAL("__cxa_atexit") TLI_DEFINE_SIG_INTERNAL(Int, Ptr, Ptr, Ptr) +/// int atexit(void (*f)(void)); +TLI_DEFINE_ENUM_INTERNAL(atexit) +TLI_DEFINE_STRING_INTERNAL("atexit") +TLI_DEFINE_SIG_INTERNAL(Int, Ptr) + /// void __cxa_guard_abort(guard_t *guard); /// guard_t is int64_t in Itanium ABI or int32_t on ARM eabi. TLI_DEFINE_ENUM_INTERNAL(cxa_guard_abort) diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp index fbb83e787f6327..c8c835115a992a 100644 --- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp +++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp @@ -87,6 +87,7 @@ STATISTIC(NumNestRemoved , "Number of nest attributes removed"); STATISTIC(NumAliasesResolved, "Number of global aliases resolved"); STATISTIC(NumAliasesRemoved, "Number of global aliases eliminated"); STATISTIC(NumCXXDtorsRemoved, "Number of global C++ destructors removed"); +STATISTIC(NumAtExitRemoved, "Number of atexit handlers removed"); STATISTIC(NumInternalFunc, "Number of internal functions"); STATISTIC(NumColdCC, "Number of functions marked coldcc"); STATISTIC(NumIFuncsResolved, "Number of statically resolved IFuncs"); @@ -2328,18 +2329,19 @@ OptimizeGlobalAliases(Module &M, } static Function * -FindCXAAtExit(Module &M, function_ref GetTLI) { +FindAtExitLibFunc(Module &M, + function_ref GetTLI, + LibFunc Func) { // Hack to get a default TLI before we have actual Function. auto FuncIter = M.begin(); if (FuncIter == M.end()) return nullptr; auto *TLI = &GetTLI(*FuncIter); - LibFunc F = LibFunc_cxa_atexit; - if (!TLI->has(F)) + if (!TLI->has(Func)) return nullptr; - Function *Fn = M.getFunction(TLI->getName(F)); + Function *Fn = M.getFunction(TLI->getName(Func)); if (!Fn) return nullptr; @@ -2347,17 +2349,18 @@ FindCXAAtExit(Module &M, function_ref GetTLI) { TLI = &GetTLI(*Fn); // Make sure that the function has the correct prototype. - if (!TLI->getLibFunc(*Fn, F) || F != LibFunc_cxa_atexit) + LibFunc F; + if (!TLI->getLibFunc(*Fn, F) || F != Func) return nullptr; return Fn; } -/// Returns whether the given function is an empty C++ destructor and can -/// therefore be eliminated. -/// Note that we assume that other optimization passes have already simplified -/// the code so we simply check for 'ret'. -static bool cxxDtorIsEmpty(const Function &Fn) { +/// Returns whether the given function is an empty C++ destructor or atexit +/// handler and can therefore be eliminated. Note that we assume that other +/// optimization passes have already simplified the code so we simply check for +/// 'ret'. +static bool IsEmptyAtExitFunction(const Function &Fn) { // FIXME: We could eliminate C++ destructors if they're readonly/readnone and // nounwind, but that doesn't seem worth doing. if (Fn.isDeclaration()) @@ -2373,7 +2376,7 @@ static bool cxxDtorIsEmpty(const Function &Fn) { return false; } -static bool OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn) { +static bool OptimizeEmptyGlobalAtExitDtors(Function *CXAAtExitFn, bool isCXX) { /// Itanium C++ ABI p3.3.5: /// /// After constructing a global (or local static) object, that will require @@ -2386,8 +2389,8 @@ static bool OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn) { /// registered before this one. It returns zero if registration is /// successful, nonzero on failure. - // This pass will look for calls to __cxa_atexit where the function is trivial - // and remove them. + // This pass will look for calls to __cxa_atexit or atexit where the function + // is trivial and remove them. bool Changed = false; for (User *U : llvm::make_early_inc_range(CXAAtExitFn->users())) { @@ -2400,14 +2403,17 @@ static bool OptimizeEmptyGlobalCXXDtors(Function *CXAAtExitFn) { Function *DtorFn = dyn_cast(CI->getArgOperand(0)->stripPointerCasts()); - if (!DtorFn || !cxxDtorIsEmpty(*DtorFn)) + if (!DtorFn || !IsEmptyAtExitFunction(*DtorFn)) continue; // Just remove the call. CI->replaceAllUsesWith(Constant::getNullValue(CI->getType())); CI->eraseFromParent(); - ++NumCXXDtorsRemoved; + if (isCXX) + ++NumCXXDtorsRemoved; + else + ++NumAtExitRemoved; Changed |= true; } @@ -2525,9 +2531,12 @@ optimizeGlobalsInModule(Module &M, const DataLayout &DL, // Try to remove trivial global destructors if they are not removed // already. - Function *CXAAtExitFn = FindCXAAtExit(M, GetTLI); - if (CXAAtExitFn) - LocalChange |= OptimizeEmptyGlobalCXXDtors(CXAAtExitFn); + if (Function *CXAAtExitFn = + FindAtExitLibFunc(M, GetTLI, LibFunc_cxa_atexit)) + LocalChange |= OptimizeEmptyGlobalAtExitDtors(CXAAtExitFn, true); + + if (Function *AtExitFn = FindAtExitLibFunc(M, GetTLI, LibFunc_atexit)) + LocalChange |= OptimizeEmptyGlobalAtExitDtors(AtExitFn, false); // Optimize IFuncs whose callee's are statically known. LocalChange |= OptimizeStaticIFuncs(M); diff --git a/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll b/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll new file mode 100644 index 00000000000000..bf78fd3d3ef49b --- /dev/null +++ b/llvm/test/Transforms/GlobalOpt/atexit-dtor.ll @@ -0,0 +1,54 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4 +; RUN: opt < %s -S -passes=globalopt | FileCheck %s + +declare dso_local i32 @atexit(ptr) + +define dso_local void @empty_atexit_handler() { +; CHECK-LABEL: define dso_local void @empty_atexit_handler() local_unnamed_addr { +; CHECK-NEXT: ret void +; + ret void +} + +; Check that `atexit` is removed if the handler is empty. +; Check that a removed `atexit` call returns `0` which is the value that denotes success. +define dso_local noundef i32 @register_atexit_handler() { +; CHECK-LABEL: define dso_local noundef i32 @register_atexit_handler() local_unnamed_addr { +; CHECK-NEXT: ret i32 0 +; + %1 = call i32 @atexit(ptr @empty_atexit_handler) + ret i32 %1 +} + +declare dso_local void @declared_atexit_handler() + +; Check that an atexit handler with only a declaration is not removed. +define dso_local noundef i32 @register_declared_atexit_handler() { +; CHECK-LABEL: define dso_local noundef i32 @register_declared_atexit_handler() local_unnamed_addr { +; CHECK-NEXT: [[TMP1:%.*]] = call i32 @atexit(ptr @declared_atexit_handler) +; CHECK-NEXT: ret i32 [[TMP1]] +; + %1 = call i32 @atexit(ptr @declared_atexit_handler) + ret i32 %1 +} + +declare dso_local void @external_exit_func() + +define dso_local void @nonempty_atexit_handler() { +; CHECK-LABEL: define dso_local void @nonempty_atexit_handler() { +; CHECK-NEXT: call void @external_exit_func() +; CHECK-NEXT: ret void +; + call void @external_exit_func() + ret void +} + +; Check that an atexit handler that consists of any instructions other than `ret` is considered nonempty and not removed. +define dso_local noundef i32 @register_nonempty_atexit_handler() { +; CHECK-LABEL: define dso_local noundef i32 @register_nonempty_atexit_handler() local_unnamed_addr { +; CHECK-NEXT: [[TMP1:%.*]] = call i32 @atexit(ptr @nonempty_atexit_handler) +; CHECK-NEXT: ret i32 [[TMP1]] +; + %1 = call i32 @atexit(ptr @nonempty_atexit_handler) + ret i32 %1 +} diff --git a/llvm/test/tools/llvm-tli-checker/ps4-tli-check.yaml b/llvm/test/tools/llvm-tli-checker/ps4-tli-check.yaml index 46f6a2d0a554ac..95c23007b1a05a 100644 --- a/llvm/test/tools/llvm-tli-checker/ps4-tli-check.yaml +++ b/llvm/test/tools/llvm-tli-checker/ps4-tli-check.yaml @@ -34,21 +34,21 @@ # # CHECK: << Total TLI yes SDK no: 8 # CHECK: >> Total TLI no SDK yes: 0 -# CHECK: == Total TLI yes SDK yes: 238 +# CHECK: == Total TLI yes SDK yes: 239 # # WRONG_DETAIL: << TLI yes SDK no : '_ZdaPv' aka operator delete[](void*) # WRONG_DETAIL: >> TLI no SDK yes: '_ZdaPvj' aka operator delete[](void*, unsigned int) # WRONG_DETAIL-COUNT-8: << TLI yes SDK no : {{.*}}__hot_cold_t # WRONG_SUMMARY: << Total TLI yes SDK no: 9{{$}} # WRONG_SUMMARY: >> Total TLI no SDK yes: 1{{$}} -# WRONG_SUMMARY: == Total TLI yes SDK yes: 237 +# WRONG_SUMMARY: == Total TLI yes SDK yes: 238 # ## The -COUNT suffix doesn't care if there are too many matches, so check ## the exact count first; the two directives should add up to that. ## Yes, this means additions to TLI will fail this test, but the argument ## to -COUNT can't be an expression. -# AVAIL: TLI knows 479 symbols, 246 available -# AVAIL-COUNT-246: {{^}} available +# AVAIL: TLI knows 480 symbols, 247 available +# AVAIL-COUNT-247: {{^}} available # AVAIL-NOT: {{^}} available # UNAVAIL-COUNT-233: not available # UNAVAIL-NOT: not available @@ -263,6 +263,10 @@ DynamicSymbols: Type: STT_FUNC Section: .text Binding: STB_GLOBAL + - Name: atexit + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL - Name: atof Type: STT_FUNC Section: .text diff --git a/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp b/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp index 8e3fe3b44a84a9..1fe94e2aae059e 100644 --- a/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp +++ b/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp @@ -495,6 +495,8 @@ TEST_F(TargetLibraryInfoTest, ValidProto) { "declare i32 @__cxa_guard_acquire(%struct*)\n" "declare void @__cxa_guard_release(%struct*)\n" + "declare i32 @atexit(void ()*)\n" + "declare i32 @__nvvm_reflect(i8*)\n" "declare i8* @__memcpy_chk(i8*, i8*, i64, i64)\n" From 9a1386e5fabb0d2741f2f7c873342eb86e350e9e Mon Sep 17 00:00:00 2001 From: Andreas Jonson Date: Tue, 30 Apr 2024 02:42:31 +0200 Subject: [PATCH 09/65] [NFC] Remove method from FoldingSet that already existed in APInt. (#90486) Noticed that there already was a function in APInt that updated a FoldingSet so there was no need for me to add it in https://github.com/llvm/llvm-project/pull/84617. --- llvm/include/llvm/ADT/FoldingSet.h | 8 -------- llvm/lib/IR/AttributeImpl.h | 4 ++-- llvm/lib/IR/Attributes.cpp | 4 ++-- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/llvm/include/llvm/ADT/FoldingSet.h b/llvm/include/llvm/ADT/FoldingSet.h index 28a84d96588383..f82eabd5044b22 100644 --- a/llvm/include/llvm/ADT/FoldingSet.h +++ b/llvm/include/llvm/ADT/FoldingSet.h @@ -16,7 +16,6 @@ #ifndef LLVM_ADT_FOLDINGSET_H #define LLVM_ADT_FOLDINGSET_H -#include "llvm/ADT/APInt.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/STLForwardCompat.h" #include "llvm/ADT/SmallVector.h" @@ -355,13 +354,6 @@ class FoldingSetNodeID { AddInteger(unsigned(I)); AddInteger(unsigned(I >> 32)); } - void AddInteger(const APInt &Int) { - AddInteger(Int.getBitWidth()); - const auto *Parts = Int.getRawData(); - for (int i = 0, N = Int.getNumWords(); i < N; ++i) { - AddInteger(Parts[i]); - } - } void AddBoolean(bool B) { AddInteger(B ? 1U : 0U); } void AddString(StringRef String); diff --git a/llvm/lib/IR/AttributeImpl.h b/llvm/lib/IR/AttributeImpl.h index 9a6427bbc3d557..58dc14588f41a7 100644 --- a/llvm/lib/IR/AttributeImpl.h +++ b/llvm/lib/IR/AttributeImpl.h @@ -121,8 +121,8 @@ class AttributeImpl : public FoldingSetNode { static void Profile(FoldingSetNodeID &ID, Attribute::AttrKind Kind, const ConstantRange &CR) { ID.AddInteger(Kind); - ID.AddInteger(CR.getLower()); - ID.AddInteger(CR.getUpper()); + CR.getLower().Profile(ID); + CR.getUpper().Profile(ID); } }; diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp index 9c48a481de1ff6..9f27f176b104ec 100644 --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -173,8 +173,8 @@ Attribute Attribute::get(LLVMContext &Context, Attribute::AttrKind Kind, LLVMContextImpl *pImpl = Context.pImpl; FoldingSetNodeID ID; ID.AddInteger(Kind); - ID.AddInteger(CR.getLower()); - ID.AddInteger(CR.getUpper()); + CR.getLower().Profile(ID); + CR.getUpper().Profile(ID); void *InsertPoint; AttributeImpl *PA = pImpl->AttrsSet.FindNodeOrInsertPos(ID, InsertPoint); From 65ee8f10b2017349b7742843fbe4accb172736e9 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Mon, 29 Apr 2024 18:06:07 -0700 Subject: [PATCH 10/65] [mlir][sparse] fold explicit value during sparsification (#90530) This ensures the explicit value is generated (and not a load into the values array). Note that actually not storing values array at all is still TBD, this is just the very first step. --- .../SparseTensor/IR/SparseTensorType.h | 8 +- .../Transforms/Sparsification.cpp | 10 ++- .../Transforms/Utils/CodegenUtils.h | 10 +++ .../SparseTensor/sparse_matmul_one.mlir | 75 +++++++++++++++++++ 4 files changed, 99 insertions(+), 4 deletions(-) create mode 100755 mlir/test/Dialect/SparseTensor/sparse_matmul_one.mlir diff --git a/mlir/include/mlir/Dialect/SparseTensor/IR/SparseTensorType.h b/mlir/include/mlir/Dialect/SparseTensor/IR/SparseTensorType.h index 34d99913fbd51b..ea3d8013b45671 100644 --- a/mlir/include/mlir/Dialect/SparseTensor/IR/SparseTensorType.h +++ b/mlir/include/mlir/Dialect/SparseTensor/IR/SparseTensorType.h @@ -344,10 +344,14 @@ class SparseTensorType { unsigned getPosWidth() const { return enc ? enc.getPosWidth() : 0; } /// Returns the explicit value, defaulting to null Attribute for unset. - Attribute getExplicitVal() const { return enc.getExplicitVal(); } + Attribute getExplicitVal() const { + return enc ? enc.getExplicitVal() : nullptr; + } /// Returns the implicit value, defaulting to null Attribute for 0. - Attribute getImplicitVal() const { return enc.getImplicitVal(); } + Attribute getImplicitVal() const { + return enc ? enc.getImplicitVal() : nullptr; + } /// Returns the coordinate-overhead MLIR type, defaulting to `IndexType`. Type getCrdType() const { return enc.getCrdElemType(); } diff --git a/mlir/lib/Dialect/SparseTensor/Transforms/Sparsification.cpp b/mlir/lib/Dialect/SparseTensor/Transforms/Sparsification.cpp index 0a9bb40b458d68..0c8e431d8c9964 100644 --- a/mlir/lib/Dialect/SparseTensor/Transforms/Sparsification.cpp +++ b/mlir/lib/Dialect/SparseTensor/Transforms/Sparsification.cpp @@ -498,9 +498,15 @@ static Value genTensorLoad(CodegenEnv &env, OpBuilder &builder, ExprId exp) { Value val = env.exp(exp).val; if (val) return val; - // Load during insertion. + // Get tensor operand. linalg::GenericOp op = env.op(); + Location loc = op.getLoc(); OpOperand *t = &op->getOpOperand(env.exp(exp).tensor); + // Fold binary-valued tensor into explicit value. + const auto stt = getSparseTensorType(t->get()); + if (auto explVal = stt.getExplicitVal()) + return genValFromAttr(builder, loc, explVal); + // Load during insertion. if (env.isSparseOutput(t)) { if (env.isCustomReduc()) return genInsertionLoadReduce(env, builder, t); @@ -509,7 +515,7 @@ static Value genTensorLoad(CodegenEnv &env, OpBuilder &builder, ExprId exp) { // Actual load. SmallVector args; Value ptr = genSubscript(env, builder, t, args); - return builder.create(op.getLoc(), ptr, args); + return builder.create(loc, ptr, args); } /// Generates a store on a dense or sparse tensor. diff --git a/mlir/lib/Dialect/SparseTensor/Transforms/Utils/CodegenUtils.h b/mlir/lib/Dialect/SparseTensor/Transforms/Utils/CodegenUtils.h index ce5831d999e9a4..cf3c35f5fa4c78 100644 --- a/mlir/lib/Dialect/SparseTensor/Transforms/Utils/CodegenUtils.h +++ b/mlir/lib/Dialect/SparseTensor/Transforms/Utils/CodegenUtils.h @@ -399,6 +399,16 @@ inline Value constantLevelTypeEncoding(OpBuilder &builder, Location loc, return constantI64(builder, loc, static_cast(lt)); } +// Generates a constant from a validated value carrying attribute. +inline Value genValFromAttr(OpBuilder &builder, Location loc, Attribute attr) { + if (auto arrayAttr = dyn_cast(attr)) { + Type tp = cast(arrayAttr[0]).getType(); + return builder.create(loc, tp, arrayAttr); + } + return builder.create(loc, cast(attr)); +} + +// TODO: is this at the right place? inline bool isZeroRankedTensorOrScalar(Type type) { auto rtp = dyn_cast(type); return !rtp || rtp.getRank() == 0; diff --git a/mlir/test/Dialect/SparseTensor/sparse_matmul_one.mlir b/mlir/test/Dialect/SparseTensor/sparse_matmul_one.mlir new file mode 100755 index 00000000000000..82f3147d3206bd --- /dev/null +++ b/mlir/test/Dialect/SparseTensor/sparse_matmul_one.mlir @@ -0,0 +1,75 @@ +// RUN: mlir-opt %s --linalg-generalize-named-ops \ +// RUN: --sparsification-and-bufferization | FileCheck %s + +#CSR_ones_complex = #sparse_tensor.encoding<{ + map = (d0, d1) -> (d0 : dense, d1 : compressed) +// explicitVal = (1.0, 0.0) : complex, +// implicitVal = (0.0, 0.0) : complex +}> + +#CSR_ones_fp = #sparse_tensor.encoding<{ + map = (d0, d1) -> (d0 : dense, d1 : compressed), + explicitVal = 1.0 : f32, + implicitVal = 0.0 : f32 +}> + +#CSR_ones_int = #sparse_tensor.encoding<{ + map = (d0, d1) -> (d0 : dense, d1 : compressed), + explicitVal = 1 : i32, + implicitVal = 0 : i32 +}> + +// CHECK-LABEL: func.func @matmul_complex +// +// TODO: make this work +// +func.func @matmul_complex(%a: tensor<10x20xcomplex>, + %b: tensor<20x30xcomplex, #CSR_ones_complex>, + %c: tensor<10x30xcomplex>) -> tensor<10x30xcomplex> { + %0 = linalg.matmul + ins(%a, %b: tensor<10x20xcomplex>, tensor<20x30xcomplex,#CSR_ones_complex>) + outs(%c: tensor<10x30xcomplex>) -> tensor<10x30xcomplex> + return %0 : tensor<10x30xcomplex> +} + +// CHECK-LABEL: func.func @matmul_fp +// CHECK: scf.for +// CHECK: scf.for +// CHECK: %[[X:.*]] = memref.load +// CHECK: scf.for +// CHECK: %[[I:.*]] = memref.load +// CHECK: %[[Y:.*]] = memref.load +// CHECK: %[[M:.*]] = arith.addf %[[Y]], %[[X]] : f32 +// CHECK: memref.store %[[M]] +// CHECK: } +// CHECK: } +// CHECK: } +func.func @matmul_fp(%a: tensor<10x20xf32>, + %b: tensor<20x30xf32, #CSR_ones_fp>, + %c: tensor<10x30xf32>) -> tensor<10x30xf32> { + %0 = linalg.matmul + ins(%a, %b: tensor<10x20xf32>, tensor<20x30xf32,#CSR_ones_fp>) + outs(%c: tensor<10x30xf32>) -> tensor<10x30xf32> + return %0 : tensor<10x30xf32> +} + +// CHECK-LABEL: func.func @matmul_int +// CHECK: scf.for +// CHECK: scf.for +// CHECK: %[[X:.*]] = memref.load +// CHECK: scf.for +// CHECK: %[[I:.*]] = memref.load +// CHECK: %[[Y:.*]] = memref.load +// CHECK: %[[M:.*]] = arith.addi %[[Y]], %[[X]] : i32 +// CHECK: memref.store %[[M]] +// CHECK: } +// CHECK: } +// CHECK: } +func.func @matmul_int(%a: tensor<10x20xi32>, + %b: tensor<20x30xi32, #CSR_ones_int>, + %c: tensor<10x30xi32>) -> tensor<10x30xi32> { + %0 = linalg.matmul + ins(%a, %b: tensor<10x20xi32>, tensor<20x30xi32,#CSR_ones_int>) + outs(%c: tensor<10x30xi32>) -> tensor<10x30xi32> + return %0 : tensor<10x30xi32> +} From b1867e18c346e9621e14270bea2d1acb7d2a9ce0 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 29 Apr 2024 18:37:47 -0700 Subject: [PATCH 11/65] [Attributes] Support Attributes being declared as supporting an experimental late parsing mode "extension" (#88596) This patch changes the `LateParsed` field of `Attr` in `Attr.td` to be an instantiation of the new `LateAttrParseKind` class. The instation can be one of the following: * `LateAttrParsingNever` - Corresponds with the false value of `LateParsed` prior to this patch (the default for an attribute). * `LateAttrParseStandard` - Corresponds with the true value of `LateParsed` prior to this patch. * `LateAttrParseExperimentalExt` - A new mode described below. `LateAttrParseExperimentalExt` is an experimental extension to `LateAttrParseStandard`. Essentially this allows `Parser::ParseGNUAttributes(...)` to distinguish between these cases: 1. Only `LateAttrParseExperimentalExt` attributes should be late parsed. 2. Both `LateAttrParseExperimentalExt` and `LateAttrParseStandard` attributes should be late parsed. Callers (and indirect callers) of `Parser::ParseGNUAttributes(...)` indicate the desired behavior by setting a flag in the `LateParsedAttrList` object that is passed to the function. In addition to the above, a new driver and frontend flag (`-fexperimental-late-parse-attributes`) with a corresponding LangOpt (`ExperimentalLateParseAttributes`) is added that changes how `LateAttrParseExperimentalExt` attributes are parsed. * When the flag is disabled (default), in cases where only `LateAttrParsingExperimentalOnly` late parsing is requested, the attribute will be parsed immediately (i.e. **NOT** late parsed). This allows the attribute to act just like a `LateAttrParseStandard` attribute when the flag is disabled. * When the flag is enabled, in cases where only `LateAttrParsingExperimentalOnly` late parsing is requested, the attribute will be late parsed. The motivation behind this change is to allow the new `counted_by` attribute (part of `-fbounds-safety`) to support late parsing but **only** when `-fexperimental-late-parse-attributes` is enabled. This attribute needs to support late parsing to allow it to refer to fields later in a struct definition (or function parameters declared later). However, there isn't a precedent for supporting late attribute parsing in C so this flag allows the new behavior to exist in Clang but not be on by default. This behavior was requested as part of the `-fbounds-safety` RFC process (https://discourse.llvm.org/t/rfc-enforcing-bounds-safety-in-c-fbounds-safety/70854/68). This patch doesn't introduce any uses of `LateAttrParseExperimentalExt`. This will be added for the `counted_by` attribute in a future patch (https://github.com/llvm/llvm-project/pull/87596). A consequence is the new behavior added in this patch is not yet testable. Hence, the lack of tests covering the new behavior. rdar://125400257 --- clang/docs/ReleaseNotes.rst | 4 + clang/include/clang/Basic/Attr.td | 79 +++++++--- clang/include/clang/Basic/LangOptions.def | 1 + clang/include/clang/Driver/Options.td | 7 + clang/include/clang/Parse/Parser.h | 13 +- clang/lib/Driver/ToolChains/Clang.cpp | 3 + clang/lib/Parse/ParseDecl.cpp | 40 ++++- .../experimental-late-parse-attributes.c | 12 ++ clang/utils/TableGen/ClangAttrEmitter.cpp | 137 ++++++++++++++++-- 9 files changed, 254 insertions(+), 42 deletions(-) create mode 100644 clang/test/Driver/experimental-late-parse-attributes.c diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 676f38a8e94c81..4c0fe5bcf6b122 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -256,6 +256,10 @@ New Compiler Flags - ``-fexperimental-modules-reduced-bmi`` enables the Reduced BMI for C++20 named modules. See the document of standard C++ modules for details. +- ``-fexperimental-late-parse-attributes`` enables an experimental feature to + allow late parsing certain attributes in specific contexts where they would + not normally be late parsed. + Deprecated Compiler Flags ------------------------- diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 97e06fe7d2e6aa..0225598cbbe8ad 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -592,6 +592,49 @@ class AttrSubjectMatcherAggregateRule { def SubjectMatcherForNamed : AttrSubjectMatcherAggregateRule; +// Enumeration specifying what kind of behavior should be used for late +// parsing of attributes. +class LateAttrParseKind { + int Kind = val; +} + +// Never late parsed +def LateAttrParseNever : LateAttrParseKind<0>; + +// Standard late attribute parsing +// +// This is language dependent. For example: +// +// * For C++ enables late parsing of a declaration attributes +// * For C does not enable late parsing of attributes +// +def LateAttrParseStandard : LateAttrParseKind<1>; + +// Experimental extension to standard late attribute parsing +// +// This extension behaves like `LateAttrParseStandard` but allows +// late parsing attributes in more contexts. +// +// In contexts where `LateAttrParseStandard` attributes are late +// parsed, `LateAttrParseExperimentalExt` attributes will also +// be late parsed. +// +// In contexts that only late parse `LateAttrParseExperimentalExt` attributes +// (see `LateParsedAttrList::lateAttrParseExperimentalExtOnly()`) +// +// * If `-fexperimental-late-parse-attributes` +// (`LangOpts.ExperimentalLateParseAttributes`) is enabled the attribute +// will be late parsed. +// * If `-fexperimental-late-parse-attributes` +// (`LangOpts.ExperimentalLateParseAttributes`) is disabled the attribute +// will **not** be late parsed (i.e parsed immediately). +// +// The following contexts are supported: +// +// * TODO: Add contexts here when they are implemented. +// +def LateAttrParseExperimentalExt : LateAttrParseKind<2>; + class Attr { // The various ways in which an attribute can be spelled in source list Spellings; @@ -603,8 +646,8 @@ class Attr { list Accessors = []; // Specify targets for spellings. list TargetSpecificSpellings = []; - // Set to true for attributes with arguments which require delayed parsing. - bit LateParsed = 0; + // Specifies the late parsing kind. + LateAttrParseKind LateParsed = LateAttrParseNever; // Set to false to prevent an attribute from being propagated from a template // to the instantiation. bit Clone = 1; @@ -3173,7 +3216,7 @@ def DiagnoseIf : InheritableAttr { BoolArgument<"ArgDependent", 0, /*fake*/ 1>, DeclArgument]; let InheritEvenIfAlreadyPresent = 1; - let LateParsed = 1; + let LateParsed = LateAttrParseStandard; let AdditionalMembers = [{ bool isError() const { return diagnosticType == DT_Error; } bool isWarning() const { return diagnosticType == DT_Warning; } @@ -3472,7 +3515,7 @@ def AssertCapability : InheritableAttr { let Spellings = [Clang<"assert_capability", 0>, Clang<"assert_shared_capability", 0>]; let Subjects = SubjectList<[Function]>; - let LateParsed = 1; + let LateParsed = LateAttrParseStandard; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3488,7 +3531,7 @@ def AcquireCapability : InheritableAttr { GNU<"exclusive_lock_function">, GNU<"shared_lock_function">]; let Subjects = SubjectList<[Function]>; - let LateParsed = 1; + let LateParsed = LateAttrParseStandard; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3504,7 +3547,7 @@ def TryAcquireCapability : InheritableAttr { Clang<"try_acquire_shared_capability", 0>]; let Subjects = SubjectList<[Function], ErrorDiag>; - let LateParsed = 1; + let LateParsed = LateAttrParseStandard; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3520,7 +3563,7 @@ def ReleaseCapability : InheritableAttr { Clang<"release_generic_capability", 0>, Clang<"unlock_function", 0>]; let Subjects = SubjectList<[Function]>; - let LateParsed = 1; + let LateParsed = LateAttrParseStandard; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3539,7 +3582,7 @@ def RequiresCapability : InheritableAttr { Clang<"requires_shared_capability", 0>, Clang<"shared_locks_required", 0>]; let Args = [VariadicExprArgument<"Args">]; - let LateParsed = 1; + let LateParsed = LateAttrParseStandard; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3559,7 +3602,7 @@ def NoThreadSafetyAnalysis : InheritableAttr { def GuardedBy : InheritableAttr { let Spellings = [GNU<"guarded_by">]; let Args = [ExprArgument<"Arg">]; - let LateParsed = 1; + let LateParsed = LateAttrParseStandard; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3570,7 +3613,7 @@ def GuardedBy : InheritableAttr { def PtGuardedBy : InheritableAttr { let Spellings = [GNU<"pt_guarded_by">]; let Args = [ExprArgument<"Arg">]; - let LateParsed = 1; + let LateParsed = LateAttrParseStandard; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3581,7 +3624,7 @@ def PtGuardedBy : InheritableAttr { def AcquiredAfter : InheritableAttr { let Spellings = [GNU<"acquired_after">]; let Args = [VariadicExprArgument<"Args">]; - let LateParsed = 1; + let LateParsed = LateAttrParseStandard; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3592,7 +3635,7 @@ def AcquiredAfter : InheritableAttr { def AcquiredBefore : InheritableAttr { let Spellings = [GNU<"acquired_before">]; let Args = [VariadicExprArgument<"Args">]; - let LateParsed = 1; + let LateParsed = LateAttrParseStandard; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3603,7 +3646,7 @@ def AcquiredBefore : InheritableAttr { def AssertExclusiveLock : InheritableAttr { let Spellings = [GNU<"assert_exclusive_lock">]; let Args = [VariadicExprArgument<"Args">]; - let LateParsed = 1; + let LateParsed = LateAttrParseStandard; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3614,7 +3657,7 @@ def AssertExclusiveLock : InheritableAttr { def AssertSharedLock : InheritableAttr { let Spellings = [GNU<"assert_shared_lock">]; let Args = [VariadicExprArgument<"Args">]; - let LateParsed = 1; + let LateParsed = LateAttrParseStandard; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3627,7 +3670,7 @@ def AssertSharedLock : InheritableAttr { def ExclusiveTrylockFunction : InheritableAttr { let Spellings = [GNU<"exclusive_trylock_function">]; let Args = [ExprArgument<"SuccessValue">, VariadicExprArgument<"Args">]; - let LateParsed = 1; + let LateParsed = LateAttrParseStandard; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3640,7 +3683,7 @@ def ExclusiveTrylockFunction : InheritableAttr { def SharedTrylockFunction : InheritableAttr { let Spellings = [GNU<"shared_trylock_function">]; let Args = [ExprArgument<"SuccessValue">, VariadicExprArgument<"Args">]; - let LateParsed = 1; + let LateParsed = LateAttrParseStandard; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3651,7 +3694,7 @@ def SharedTrylockFunction : InheritableAttr { def LockReturned : InheritableAttr { let Spellings = [GNU<"lock_returned">]; let Args = [ExprArgument<"Arg">]; - let LateParsed = 1; + let LateParsed = LateAttrParseStandard; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let Subjects = SubjectList<[Function]>; @@ -3661,7 +3704,7 @@ def LockReturned : InheritableAttr { def LocksExcluded : InheritableAttr { let Spellings = [GNU<"locks_excluded">]; let Args = [VariadicExprArgument<"Args">]; - let LateParsed = 1; + let LateParsed = LateAttrParseStandard; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 8ef6700ecdc78e..55c81eab1ec150 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -164,6 +164,7 @@ LANGOPT(ExperimentalLibrary, 1, 0, "enable unstable and experimental library fea LANGOPT(PointerAuthIntrinsics, 1, 0, "pointer authentication intrinsics") LANGOPT(DoubleSquareBracketAttributes, 1, 0, "'[[]]' attributes extension for all language standard modes") +LANGOPT(ExperimentalLateParseAttributes, 1, 0, "experimental late parsing of attributes") COMPATIBLE_LANGOPT(RecoveryAST, 1, 1, "Preserve expressions in AST when encountering errors") COMPATIBLE_LANGOPT(RecoveryASTType, 1, 1, "Preserve the type in recovery expressions") diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 25f479dccc3c80..864da4e1157f7d 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1608,6 +1608,13 @@ defm double_square_bracket_attributes : BoolFOption<"double-square-bracket-attri LangOpts<"DoubleSquareBracketAttributes">, DefaultTrue, PosFlag, NegFlag>; +defm experimental_late_parse_attributes : BoolFOption<"experimental-late-parse-attributes", + LangOpts<"ExperimentalLateParseAttributes">, DefaultFalse, + PosFlag, + NegFlag, + BothFlags<[], [ClangOption, CC1Option], + " experimental late parsing of attributes">>; + defm autolink : BoolFOption<"autolink", CodeGenOpts<"Autolink">, DefaultTrue, NegFlag { public: - LateParsedAttrList(bool PSoon = false) : ParseSoon(PSoon) { } + LateParsedAttrList(bool PSoon = false, + bool LateAttrParseExperimentalExtOnly = false) + : ParseSoon(PSoon), + LateAttrParseExperimentalExtOnly(LateAttrParseExperimentalExtOnly) {} bool parseSoon() { return ParseSoon; } + /// returns true iff the attribute to be parsed should only be late parsed + /// if it is annotated with `LateAttrParseExperimentalExt` + bool lateAttrParseExperimentalExtOnly() { + return LateAttrParseExperimentalExtOnly; + } private: - bool ParseSoon; // Are we planning to parse these shortly after creation? + bool ParseSoon; // Are we planning to parse these shortly after creation? + bool LateAttrParseExperimentalExtOnly; }; /// Contains the lexed tokens of a member function definition diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 250dc078edf242..1988a90a48ae6b 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -7490,6 +7490,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.addOptInFlag(CmdArgs, options::OPT_fsafe_buffer_usage_suggestions, options::OPT_fno_safe_buffer_usage_suggestions); + Args.addOptInFlag(CmdArgs, options::OPT_fexperimental_late_parse_attributes, + options::OPT_fno_experimental_late_parse_attributes); + // Setup statistics file output. SmallString<128> StatsFile = getStatsFileName(Args, Output, Input, D); if (!StatsFile.empty()) { diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index a7846e102a43c7..7431c256d2c135 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -91,13 +91,23 @@ static StringRef normalizeAttrName(StringRef Name) { return Name; } -/// isAttributeLateParsed - Return true if the attribute has arguments that -/// require late parsing. -static bool isAttributeLateParsed(const IdentifierInfo &II) { +/// returns true iff attribute is annotated with `LateAttrParseExperimentalExt` +/// in `Attr.td`. +static bool IsAttributeLateParsedExperimentalExt(const IdentifierInfo &II) { +#define CLANG_ATTR_LATE_PARSED_EXPERIMENTAL_EXT_LIST + return llvm::StringSwitch(normalizeAttrName(II.getName())) +#include "clang/Parse/AttrParserStringSwitches.inc" + .Default(false); +#undef CLANG_ATTR_LATE_PARSED_EXPERIMENTAL_EXT_LIST +} + +/// returns true iff attribute is annotated with `LateAttrParseStandard` in +/// `Attr.td`. +static bool IsAttributeLateParsedStandard(const IdentifierInfo &II) { #define CLANG_ATTR_LATE_PARSED_LIST - return llvm::StringSwitch(normalizeAttrName(II.getName())) + return llvm::StringSwitch(normalizeAttrName(II.getName())) #include "clang/Parse/AttrParserStringSwitches.inc" - .Default(false); + .Default(false); #undef CLANG_ATTR_LATE_PARSED_LIST } @@ -222,8 +232,26 @@ void Parser::ParseGNUAttributes(ParsedAttributes &Attrs, continue; } + bool LateParse = false; + if (!LateAttrs) + LateParse = false; + else if (LateAttrs->lateAttrParseExperimentalExtOnly()) { + // The caller requested that this attribute **only** be late + // parsed for `LateAttrParseExperimentalExt` attributes. This will + // only be late parsed if the experimental language option is enabled. + LateParse = getLangOpts().ExperimentalLateParseAttributes && + IsAttributeLateParsedExperimentalExt(*AttrName); + } else { + // The caller did not restrict late parsing to only + // `LateAttrParseExperimentalExt` attributes so late parse + // both `LateAttrParseStandard` and `LateAttrParseExperimentalExt` + // attributes. + LateParse = IsAttributeLateParsedExperimentalExt(*AttrName) || + IsAttributeLateParsedStandard(*AttrName); + } + // Handle "parameterized" attributes - if (!LateAttrs || !isAttributeLateParsed(*AttrName)) { + if (!LateParse) { ParseGNUAttributeArgs(AttrName, AttrNameLoc, Attrs, &EndLoc, nullptr, SourceLocation(), ParsedAttr::Form::GNU(), D); continue; diff --git a/clang/test/Driver/experimental-late-parse-attributes.c b/clang/test/Driver/experimental-late-parse-attributes.c new file mode 100644 index 00000000000000..6b54b898afa749 --- /dev/null +++ b/clang/test/Driver/experimental-late-parse-attributes.c @@ -0,0 +1,12 @@ +// RUN: %clang %s -c -fexperimental-late-parse-attributes 2>&1 -### | FileCheck %s -check-prefix=CHECK-ON +// RUN: %clang %s -c -fno-experimental-late-parse-attributes -fexperimental-late-parse-attributes 2>&1 -### | FileCheck %s -check-prefix=CHECK-ON + +// CHECK-ON: -cc1 +// CHECK-ON: -fexperimental-late-parse-attributes + +// RUN: %clang %s -c 2>&1 -### | FileCheck %s -check-prefix=CHECK-OFF +// RUN: %clang %s -c -fno-experimental-late-parse-attributes 2>&1 -### | FileCheck %s -check-prefix=CHECK-OFF +// RUN: %clang %s -c -fexperimental-late-parse-attributes -fno-experimental-late-parse-attributes 2>&1 -### | FileCheck %s -check-prefix=CHECK-OFF + +// CHECK-OFF: -cc1 +// CHECK-OFF-NOT: -fexperimental-late-parse-attributes diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index 0d1365f09291e0..aafbf1f40949a2 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -1822,28 +1822,101 @@ void WriteSemanticSpellingSwitch(const std::string &VarName, OS << " }\n"; } +// Note: these values need to match the values used by LateAttrParseKind in +// `Attr.td` +enum class LateAttrParseKind { Never = 0, Standard = 1, ExperimentalExt = 2 }; + +static LateAttrParseKind getLateAttrParseKind(const Record *Attr) { + // This function basically does + // `Attr->getValueAsDef("LateParsed")->getValueAsInt("Kind")` but does a bunch + // of sanity checking to ensure that `LateAttrParseMode` in `Attr.td` is in + // sync with the `LateAttrParseKind` enum in this source file. + + static constexpr StringRef LateParsedStr = "LateParsed"; + static constexpr StringRef LateAttrParseKindStr = "LateAttrParseKind"; + static constexpr StringRef KindFieldStr = "Kind"; + + auto *LAPK = Attr->getValueAsDef(LateParsedStr); + + // Typecheck the `LateParsed` field. + SmallVector SuperClasses; + LAPK->getDirectSuperClasses(SuperClasses); + if (SuperClasses.size() != 1) + PrintFatalError(Attr, "Field `" + llvm::Twine(LateParsedStr) + + "`should only have one super class"); + + if (SuperClasses[0]->getName().compare(LateAttrParseKindStr) != 0) + PrintFatalError(Attr, "Field `" + llvm::Twine(LateParsedStr) + + "`should only have type `" + + llvm::Twine(LateAttrParseKindStr) + + "` but found type `" + + SuperClasses[0]->getName() + "`"); + + // Get Kind and verify the enum name matches the name in `Attr.td`. + unsigned Kind = LAPK->getValueAsInt(KindFieldStr); + switch (LateAttrParseKind(Kind)) { +#define CASE(X) \ + case LateAttrParseKind::X: \ + if (LAPK->getName().compare("LateAttrParse" #X) != 0) { \ + PrintFatalError(Attr, \ + "Field `" + llvm::Twine(LateParsedStr) + "` set to `" + \ + LAPK->getName() + \ + "` but this converts to `LateAttrParseKind::" + \ + llvm::Twine(#X) + "`"); \ + } \ + return LateAttrParseKind::X; + + CASE(Never) + CASE(Standard) + CASE(ExperimentalExt) +#undef CASE + } + + // The Kind value is completely invalid + auto KindValueStr = llvm::utostr(Kind); + PrintFatalError(Attr, "Field `" + llvm::Twine(LateParsedStr) + "` set to `" + + LAPK->getName() + "` has unexpected `" + + llvm::Twine(KindFieldStr) + "` value of " + + KindValueStr); +} + // Emits the LateParsed property for attributes. -static void emitClangAttrLateParsedList(RecordKeeper &Records, raw_ostream &OS) { - OS << "#if defined(CLANG_ATTR_LATE_PARSED_LIST)\n"; - std::vector Attrs = Records.getAllDerivedDefinitions("Attr"); +static void emitClangAttrLateParsedListImpl(RecordKeeper &Records, + raw_ostream &OS, + LateAttrParseKind LateParseMode) { + std::vector Attrs = Records.getAllDerivedDefinitions("Attr"); for (const auto *Attr : Attrs) { - bool LateParsed = Attr->getValueAsBit("LateParsed"); + if (LateAttrParseKind LateParsed = getLateAttrParseKind(Attr); + LateParsed != LateParseMode) + continue; - if (LateParsed) { - std::vector Spellings = GetFlattenedSpellings(*Attr); + std::vector Spellings = GetFlattenedSpellings(*Attr); - // FIXME: Handle non-GNU attributes - for (const auto &I : Spellings) { - if (I.variety() != "GNU") - continue; - OS << ".Case(\"" << I.name() << "\", " << LateParsed << ")\n"; - } + // FIXME: Handle non-GNU attributes + for (const auto &I : Spellings) { + if (I.variety() != "GNU") + continue; + OS << ".Case(\"" << I.name() << "\", 1)\n"; } } +} + +static void emitClangAttrLateParsedList(RecordKeeper &Records, + raw_ostream &OS) { + OS << "#if defined(CLANG_ATTR_LATE_PARSED_LIST)\n"; + emitClangAttrLateParsedListImpl(Records, OS, LateAttrParseKind::Standard); OS << "#endif // CLANG_ATTR_LATE_PARSED_LIST\n\n"; } +static void emitClangAttrLateParsedExperimentalList(RecordKeeper &Records, + raw_ostream &OS) { + OS << "#if defined(CLANG_ATTR_LATE_PARSED_EXPERIMENTAL_EXT_LIST)\n"; + emitClangAttrLateParsedListImpl(Records, OS, + LateAttrParseKind::ExperimentalExt); + OS << "#endif // CLANG_ATTR_LATE_PARSED_EXPERIMENTAL_EXT_LIST\n\n"; +} + static bool hasGNUorCXX11Spelling(const Record &Attribute) { std::vector Spellings = GetFlattenedSpellings(Attribute); for (const auto &I : Spellings) { @@ -2101,9 +2174,21 @@ bool PragmaClangAttributeSupport::isAttributedSupported( return SpecifiedResult; // Opt-out rules: - // An attribute requires delayed parsing (LateParsed is on) - if (Attribute.getValueAsBit("LateParsed")) + + // An attribute requires delayed parsing (LateParsed is on). + switch (getLateAttrParseKind(&Attribute)) { + case LateAttrParseKind::Never: + break; + case LateAttrParseKind::Standard: + return false; + case LateAttrParseKind::ExperimentalExt: + // This is only late parsed in certain parsing contexts when + // `LangOpts.ExperimentalLateParseAttributes` is true. Information about the + // parsing context and `LangOpts` is not available in this method so just + // opt this attribute out. return false; + } + // An attribute has no GNU/CXX11 spelling if (!hasGNUorCXX11Spelling(Attribute)) return false; @@ -2885,8 +2970,27 @@ static void emitAttributes(RecordKeeper &Records, raw_ostream &OS, return; } OS << "\n : " << SuperName << "(Ctx, CommonInfo, "; - OS << "attr::" << R.getName() << ", " - << (R.getValueAsBit("LateParsed") ? "true" : "false"); + OS << "attr::" << R.getName() << ", "; + + // Handle different late parsing modes. + OS << "/*IsLateParsed=*/"; + switch (getLateAttrParseKind(&R)) { + case LateAttrParseKind::Never: + OS << "false"; + break; + case LateAttrParseKind::ExperimentalExt: + // Currently no clients need to know the distinction between `Standard` + // and `ExperimentalExt` so treat `ExperimentalExt` just like + // `Standard` for now. + case LateAttrParseKind::Standard: + // Note: This is misleading. `IsLateParsed` doesn't mean the + // attribute was actually late parsed. Instead it means the attribute in + // `Attr.td` is marked as being late parsed. Maybe it should be called + // `IsLateParseable`? + OS << "true"; + break; + } + if (Inheritable) { OS << ", " << (R.getValueAsBit("InheritEvenIfAlreadyPresent") ? "true" @@ -4843,6 +4947,7 @@ void EmitClangAttrParserStringSwitches(RecordKeeper &Records, raw_ostream &OS) { emitClangAttrAcceptsExprPack(Records, OS); emitClangAttrTypeArgList(Records, OS); emitClangAttrLateParsedList(Records, OS); + emitClangAttrLateParsedExperimentalList(Records, OS); } void EmitClangAttrSubjectMatchRulesParserStringSwitches(RecordKeeper &Records, From 6ea0c0a28343b2676baf480db490b5a27fa11d7c Mon Sep 17 00:00:00 2001 From: paperchalice Date: Tue, 30 Apr 2024 09:54:48 +0800 Subject: [PATCH 12/65] [NewPM][CodeGen] Add `MachineFunctionAnalysis` (#88610) In new pass system, `MachineFunction` could be an analysis result again, machine module pass can now fetch them from analysis manager. `MachineModuleInfo` no longer owns them. Remove `FreeMachineFunctionPass`, replaced by `InvalidateAnalysisPass`. Now `FreeMachineFunction` is replaced by `InvalidateAnalysisPass`, the workaround in `MachineFunctionPassManager` is no longer needed, there is no difference between `unittests/MIR/PassBuilderCallbacksTest.cpp` and `unittests/IR/PassBuilderCallbacksTest.cpp`. --- .../llvm/CodeGen/FreeMachineFunction.h | 24 - .../llvm/CodeGen/MIRParser/MIRParser.h | 12 + .../llvm/CodeGen/MachineFunctionAnalysis.h | 50 ++ llvm/include/llvm/CodeGen/MachineModuleInfo.h | 4 + .../include/llvm/CodeGen/MachinePassManager.h | 41 +- llvm/include/llvm/IR/LLVMContext.h | 6 +- llvm/include/llvm/Passes/CodeGenPassBuilder.h | 18 +- .../llvm/Passes/MachinePassRegistry.def | 1 - llvm/lib/CodeGen/CMakeLists.txt | 2 +- .../CodeGen/DeadMachineInstructionElim.cpp | 2 +- llvm/lib/CodeGen/FreeMachineFunction.cpp | 22 - llvm/lib/CodeGen/MIRParser/MIRParser.cpp | 48 +- llvm/lib/CodeGen/MachineFunctionAnalysis.cpp | 46 ++ llvm/lib/CodeGen/MachinePassManager.cpp | 103 ++-- llvm/lib/IR/LLVMContext.cpp | 7 + llvm/lib/IR/LLVMContextImpl.h | 4 + llvm/lib/Passes/PassBuilder.cpp | 22 +- llvm/lib/Passes/PassRegistry.def | 3 + llvm/lib/Passes/StandardInstrumentations.cpp | 8 - llvm/test/tools/llc/new-pm/pipeline.ll | 2 +- llvm/test/tools/llc/new-pm/pipeline.mir | 2 +- llvm/test/tools/llc/new-pm/start-stop.ll | 2 +- llvm/tools/llc/NewPMDriver.cpp | 10 +- llvm/unittests/CodeGen/PassManagerTest.cpp | 14 +- llvm/unittests/MIR/CMakeLists.txt | 1 - .../MIR/PassBuilderCallbacksTest.cpp | 575 ------------------ 26 files changed, 298 insertions(+), 731 deletions(-) delete mode 100644 llvm/include/llvm/CodeGen/FreeMachineFunction.h create mode 100644 llvm/include/llvm/CodeGen/MachineFunctionAnalysis.h delete mode 100644 llvm/lib/CodeGen/FreeMachineFunction.cpp create mode 100644 llvm/lib/CodeGen/MachineFunctionAnalysis.cpp delete mode 100644 llvm/unittests/MIR/PassBuilderCallbacksTest.cpp diff --git a/llvm/include/llvm/CodeGen/FreeMachineFunction.h b/llvm/include/llvm/CodeGen/FreeMachineFunction.h deleted file mode 100644 index 5f21c6720350bc..00000000000000 --- a/llvm/include/llvm/CodeGen/FreeMachineFunction.h +++ /dev/null @@ -1,24 +0,0 @@ -//===- llvm/CodeGen/FreeMachineFunction.h -----------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CODEGEN_FREEMACHINEFUNCTION_H -#define LLVM_CODEGEN_FREEMACHINEFUNCTION_H - -#include "llvm/CodeGen/MachinePassManager.h" - -namespace llvm { - -class FreeMachineFunctionPass : public PassInfoMixin { -public: - PreservedAnalyses run(MachineFunction &MF, - MachineFunctionAnalysisManager &MFAM); -}; - -} // namespace llvm - -#endif // LLVM_CODEGEN_FREEMACHINEFUNCTION_H diff --git a/llvm/include/llvm/CodeGen/MIRParser/MIRParser.h b/llvm/include/llvm/CodeGen/MIRParser/MIRParser.h index e1606e7c0ea72d..ae0938a48a7110 100644 --- a/llvm/include/llvm/CodeGen/MIRParser/MIRParser.h +++ b/llvm/include/llvm/CodeGen/MIRParser/MIRParser.h @@ -34,6 +34,9 @@ class MachineModuleInfo; class SMDiagnostic; class StringRef; +template class AnalysisManager; +using ModuleAnalysisManager = AnalysisManager; + typedef llvm::function_ref(StringRef, StringRef)> DataLayoutCallbackTy; @@ -60,6 +63,15 @@ class MIRParser { /// /// \returns true if an error occurred. bool parseMachineFunctions(Module &M, MachineModuleInfo &MMI); + + /// Parses MachineFunctions in the MIR file and add them as the result + /// of MachineFunctionAnalysis in ModulePassManager \p MAM. + /// User should register at least MachineFunctionAnalysis, + /// MachineModuleAnalysis, FunctionAnalysisManagerModuleProxy and + /// PassInstrumentationAnalysis in \p MAM before parsing MIR. + /// + /// \returns true if an error occurred. + bool parseMachineFunctions(Module &M, ModuleAnalysisManager &MAM); }; /// This function is the main interface to the MIR serialization format parser. diff --git a/llvm/include/llvm/CodeGen/MachineFunctionAnalysis.h b/llvm/include/llvm/CodeGen/MachineFunctionAnalysis.h new file mode 100644 index 00000000000000..f54d455292da03 --- /dev/null +++ b/llvm/include/llvm/CodeGen/MachineFunctionAnalysis.h @@ -0,0 +1,50 @@ +//===- llvm/CodeGen/MachineFunctionAnalysis.h -------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares the MachineFunctionAnalysis class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_MACHINEFUNCTIONANALYSIS +#define LLVM_CODEGEN_MACHINEFUNCTIONANALYSIS + +#include "llvm/IR/PassManager.h" + +namespace llvm { + +class MachineFunction; +class LLVMTargetMachine; + +/// This analysis create MachineFunction for given Function. +/// To release the MachineFunction, users should invalidate it explicitly. +class MachineFunctionAnalysis + : public AnalysisInfoMixin { + friend AnalysisInfoMixin; + + static AnalysisKey Key; + + const LLVMTargetMachine *TM; + +public: + class Result { + std::unique_ptr MF; + + public: + Result(std::unique_ptr MF) : MF(std::move(MF)) {} + MachineFunction &getMF() { return *MF; }; + bool invalidate(Function &, const PreservedAnalyses &PA, + FunctionAnalysisManager::Invalidator &); + }; + + MachineFunctionAnalysis(const LLVMTargetMachine *TM) : TM(TM){}; + Result run(Function &F, FunctionAnalysisManager &FAM); +}; + +} // namespace llvm + +#endif // LLVM_CODEGEN_MachineFunctionAnalysis diff --git a/llvm/include/llvm/CodeGen/MachineModuleInfo.h b/llvm/include/llvm/CodeGen/MachineModuleInfo.h index 5f66d1ada0d082..92ea3c902ce95e 100644 --- a/llvm/include/llvm/CodeGen/MachineModuleInfo.h +++ b/llvm/include/llvm/CodeGen/MachineModuleInfo.h @@ -147,10 +147,14 @@ class MachineModuleInfo { /// Returns the MachineFunction constructed for the IR function \p F. /// Creates a new MachineFunction if none exists yet. + /// NOTE: New pass manager clients shall not use this method to get + /// the `MachineFunction`, use `MachineFunctionAnalysis` instead. MachineFunction &getOrCreateMachineFunction(Function &F); /// \brief Returns the MachineFunction associated to IR function \p F if there /// is one, otherwise nullptr. + /// NOTE: New pass manager clients shall not use this method to get + /// the `MachineFunction`, use `MachineFunctionAnalysis` instead. MachineFunction *getMachineFunction(const Function &F) const; /// Delete the MachineFunction \p MF and reset the link in the IR Function to diff --git a/llvm/include/llvm/CodeGen/MachinePassManager.h b/llvm/include/llvm/CodeGen/MachinePassManager.h index 4f0b6ba2b1e738..852b1a0f416139 100644 --- a/llvm/include/llvm/CodeGen/MachinePassManager.h +++ b/llvm/include/llvm/CodeGen/MachinePassManager.h @@ -108,6 +108,15 @@ bool MachineFunctionAnalysisManagerModuleProxy::Result::invalidate( ModuleAnalysisManager::Invalidator &Inv); extern template class InnerAnalysisManagerProxy; +using MachineFunctionAnalysisManagerFunctionProxy = + InnerAnalysisManagerProxy; + +template <> +bool MachineFunctionAnalysisManagerFunctionProxy::Result::invalidate( + Function &F, const PreservedAnalyses &PA, + FunctionAnalysisManager::Invalidator &Inv); +extern template class InnerAnalysisManagerProxy; extern template class OuterAnalysisManagerProxy; @@ -129,16 +138,6 @@ class FunctionAnalysisManagerMachineFunctionProxy Arg.FAM = nullptr; } - ~Result() { - // FAM is cleared in a moved from state where there is nothing to do. - if (!FAM) - return; - - // Clear out the analysis manager if we're being destroyed -- it means we - // didn't even see an invalidate call when we got invalidated. - FAM->clear(); - } - Result &operator=(Result &&RHS) { FAM = RHS.FAM; // We have to null out the analysis manager in the moved-from state @@ -187,18 +186,18 @@ class FunctionAnalysisManagerMachineFunctionProxy FunctionAnalysisManager *FAM; }; -class ModuleToMachineFunctionPassAdaptor - : public PassInfoMixin { +class FunctionToMachineFunctionPassAdaptor + : public PassInfoMixin { public: using PassConceptT = detail::PassConcept; - explicit ModuleToMachineFunctionPassAdaptor( + explicit FunctionToMachineFunctionPassAdaptor( std::unique_ptr Pass) : Pass(std::move(Pass)) {} - /// Runs the function pass across every function in the module. - PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); + /// Runs the function pass across every function in the function. + PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM); void printPipeline(raw_ostream &OS, function_ref MapClassName2PassName); @@ -209,14 +208,14 @@ class ModuleToMachineFunctionPassAdaptor }; template -ModuleToMachineFunctionPassAdaptor -createModuleToMachineFunctionPassAdaptor(MachineFunctionPassT &&Pass) { +FunctionToMachineFunctionPassAdaptor +createFunctionToMachineFunctionPassAdaptor(MachineFunctionPassT &&Pass) { using PassModelT = detail::PassModel; // Do not use make_unique, it causes too many template instantiations, // causing terrible compile times. - return ModuleToMachineFunctionPassAdaptor( - std::unique_ptr( + return FunctionToMachineFunctionPassAdaptor( + std::unique_ptr( new PassModelT(std::forward(Pass)))); } @@ -244,6 +243,10 @@ extern template class PassManager; /// Convenience typedef for a pass manager over functions. using MachineFunctionPassManager = PassManager; +/// Returns the minimum set of Analyses that all machine function passes must +/// preserve. +PreservedAnalyses getMachineFunctionPassPreservedAnalyses(); + } // end namespace llvm #endif // LLVM_CODEGEN_MACHINEPASSMANAGER_H diff --git a/llvm/include/llvm/IR/LLVMContext.h b/llvm/include/llvm/IR/LLVMContext.h index e5786afd72214b..89ad6f1572c679 100644 --- a/llvm/include/llvm/IR/LLVMContext.h +++ b/llvm/include/llvm/IR/LLVMContext.h @@ -155,6 +155,10 @@ class LLVMContext { void enableDebugTypeODRUniquing(); void disableDebugTypeODRUniquing(); + /// generateMachineFunctionNum - Get a unique number for MachineFunction + /// that associated with the given Function. + unsigned generateMachineFunctionNum(Function &); + /// Defines the type of a yield callback. /// \see LLVMContext::setYieldCallback. using YieldCallbackTy = void (*)(LLVMContext *Context, void *OpaqueHandle); @@ -332,7 +336,7 @@ class LLVMContext { void addModule(Module*); /// removeModule - Unregister a module from this context. - void removeModule(Module*); + void removeModule(Module *); }; // Create wrappers for C Binding types (see CBindingWrapping.h). diff --git a/llvm/include/llvm/Passes/CodeGenPassBuilder.h b/llvm/include/llvm/Passes/CodeGenPassBuilder.h index 2e94a19502131a..17bea5da48ce14 100644 --- a/llvm/include/llvm/Passes/CodeGenPassBuilder.h +++ b/llvm/include/llvm/Passes/CodeGenPassBuilder.h @@ -29,7 +29,6 @@ #include "llvm/CodeGen/DwarfEHPrepare.h" #include "llvm/CodeGen/ExpandMemCmp.h" #include "llvm/CodeGen/ExpandReductions.h" -#include "llvm/CodeGen/FreeMachineFunction.h" #include "llvm/CodeGen/GCMetadata.h" #include "llvm/CodeGen/GlobalMerge.h" #include "llvm/CodeGen/IndirectBrExpand.h" @@ -38,6 +37,8 @@ #include "llvm/CodeGen/JMCInstrumenter.h" #include "llvm/CodeGen/LowerEmuTLS.h" #include "llvm/CodeGen/MIRPrinter.h" +#include "llvm/CodeGen/MachineFunctionAnalysis.h" +#include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/MachinePassManager.h" #include "llvm/CodeGen/PreISelIntrinsicLowering.h" #include "llvm/CodeGen/ReplaceWithVeclib.h" @@ -199,8 +200,13 @@ template class CodeGenPassBuilder { AddMachinePass(ModulePassManager &MPM, const DerivedT &PB) : MPM(MPM), PB(PB) {} ~AddMachinePass() { - if (!MFPM.isEmpty()) - MPM.addPass(createModuleToMachineFunctionPassAdaptor(std::move(MFPM))); + if (!MFPM.isEmpty()) { + FunctionPassManager FPM; + FPM.addPass( + createFunctionToMachineFunctionPassAdaptor(std::move(MFPM))); + FPM.addPass(InvalidateAnalysisPass()); + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); + } } template @@ -219,8 +225,8 @@ template class CodeGenPassBuilder { } else { // Add Module Pass if (!MFPM.isEmpty()) { - MPM.addPass( - createModuleToMachineFunctionPassAdaptor(std::move(MFPM))); + MPM.addPass(createModuleToFunctionPassAdaptor( + createFunctionToMachineFunctionPassAdaptor(std::move(MFPM)))); MFPM = MachineFunctionPassManager(); } @@ -512,6 +518,7 @@ Error CodeGenPassBuilder::buildPipeline( { AddIRPass addIRPass(MPM, derived()); + addIRPass(RequireAnalysisPass()); addIRPass(RequireAnalysisPass()); addIRPass(RequireAnalysisPass()); addISelPasses(addIRPass); @@ -538,7 +545,6 @@ Error CodeGenPassBuilder::buildPipeline( if (PrintMIR) addPass(PrintMIRPass(Out), /*Force=*/true); - addPass(FreeMachineFunctionPass()); return verifyStartStop(*StartStopInfo); } diff --git a/llvm/include/llvm/Passes/MachinePassRegistry.def b/llvm/include/llvm/Passes/MachinePassRegistry.def index 2f77ae655d9b22..5a14d619ea076f 100644 --- a/llvm/include/llvm/Passes/MachinePassRegistry.def +++ b/llvm/include/llvm/Passes/MachinePassRegistry.def @@ -124,7 +124,6 @@ MACHINE_FUNCTION_ANALYSIS("pass-instrumentation", PassInstrumentationAnalysis(PI #define MACHINE_FUNCTION_PASS(NAME, CREATE_PASS) #endif MACHINE_FUNCTION_PASS("dead-mi-elimination", DeadMachineInstructionElimPass()) -// MACHINE_FUNCTION_PASS("free-machine-function", FreeMachineFunctionPass()) MACHINE_FUNCTION_PASS("no-op-machine-function", NoOpMachineFunctionPass()) MACHINE_FUNCTION_PASS("print", PrintMIRPass()) MACHINE_FUNCTION_PASS("require-all-machine-function-properties", diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt index 2c24de60edd43e..77bf1b165d0cf7 100644 --- a/llvm/lib/CodeGen/CMakeLists.txt +++ b/llvm/lib/CodeGen/CMakeLists.txt @@ -65,8 +65,8 @@ add_llvm_component_library(LLVMCodeGen FEntryInserter.cpp FinalizeISel.cpp FixupStatepointCallerSaved.cpp - FreeMachineFunction.cpp FuncletLayout.cpp + MachineFunctionAnalysis.cpp GCMetadata.cpp GCMetadataPrinter.cpp GCRootLowering.cpp diff --git a/llvm/lib/CodeGen/DeadMachineInstructionElim.cpp b/llvm/lib/CodeGen/DeadMachineInstructionElim.cpp index facc01452d2f12..578854cdb4a5dc 100644 --- a/llvm/lib/CodeGen/DeadMachineInstructionElim.cpp +++ b/llvm/lib/CodeGen/DeadMachineInstructionElim.cpp @@ -68,7 +68,7 @@ DeadMachineInstructionElimPass::run(MachineFunction &MF, MachineFunctionAnalysisManager &) { if (!DeadMachineInstructionElimImpl().runImpl(MF)) return PreservedAnalyses::all(); - PreservedAnalyses PA; + PreservedAnalyses PA = getMachineFunctionPassPreservedAnalyses(); PA.preserveSet(); return PA; } diff --git a/llvm/lib/CodeGen/FreeMachineFunction.cpp b/llvm/lib/CodeGen/FreeMachineFunction.cpp deleted file mode 100644 index f38f3e63a3f37a..00000000000000 --- a/llvm/lib/CodeGen/FreeMachineFunction.cpp +++ /dev/null @@ -1,22 +0,0 @@ -//===- FreeMachineFunction.cpp --------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "llvm/CodeGen/FreeMachineFunction.h" -#include "llvm/CodeGen/MachineFunction.h" -#include "llvm/CodeGen/MachineModuleInfo.h" - -using namespace llvm; - -PreservedAnalyses -FreeMachineFunctionPass::run(MachineFunction &MF, - MachineFunctionAnalysisManager &MFAM) { - auto &MMI = MF.getMMI(); - MFAM.invalidate(MF, PreservedAnalyses::none()); - MMI.deleteMachineFunctionFor(MF.getFunction()); // MF is dangling now. - return PreservedAnalyses::none(); -} diff --git a/llvm/lib/CodeGen/MIRParser/MIRParser.cpp b/llvm/lib/CodeGen/MIRParser/MIRParser.cpp index 4d9a8dc5602ba6..b65fc8cf5099b8 100644 --- a/llvm/lib/CodeGen/MIRParser/MIRParser.cpp +++ b/llvm/lib/CodeGen/MIRParser/MIRParser.cpp @@ -21,6 +21,7 @@ #include "llvm/CodeGen/MachineConstantPool.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionAnalysis.h" #include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/TargetFrameLowering.h" @@ -97,13 +98,15 @@ class MIRParserImpl { /// Create an empty function with the given name. Function *createDummyFunction(StringRef Name, Module &M); - bool parseMachineFunctions(Module &M, MachineModuleInfo &MMI); + bool parseMachineFunctions(Module &M, MachineModuleInfo &MMI, + ModuleAnalysisManager *FAM = nullptr); /// Parse the machine function in the current YAML document. /// /// /// Return true if an error occurred. - bool parseMachineFunction(Module &M, MachineModuleInfo &MMI); + bool parseMachineFunction(Module &M, MachineModuleInfo &MMI, + ModuleAnalysisManager *FAM); /// Initialize the machine function to the state that's described in the MIR /// file. @@ -275,13 +278,14 @@ MIRParserImpl::parseIRModule(DataLayoutCallbackTy DataLayoutCallback) { return M; } -bool MIRParserImpl::parseMachineFunctions(Module &M, MachineModuleInfo &MMI) { +bool MIRParserImpl::parseMachineFunctions(Module &M, MachineModuleInfo &MMI, + ModuleAnalysisManager *MAM) { if (NoMIRDocuments) return false; // Parse the machine functions. do { - if (parseMachineFunction(M, MMI)) + if (parseMachineFunction(M, MMI, MAM)) return true; In.nextDocument(); } while (In.setCurrentDocument()); @@ -303,7 +307,8 @@ Function *MIRParserImpl::createDummyFunction(StringRef Name, Module &M) { return F; } -bool MIRParserImpl::parseMachineFunction(Module &M, MachineModuleInfo &MMI) { +bool MIRParserImpl::parseMachineFunction(Module &M, MachineModuleInfo &MMI, + ModuleAnalysisManager *MAM) { // Parse the yaml. yaml::MachineFunction YamlMF; yaml::EmptyContext Ctx; @@ -327,14 +332,28 @@ bool MIRParserImpl::parseMachineFunction(Module &M, MachineModuleInfo &MMI) { "' isn't defined in the provided LLVM IR"); } } - if (MMI.getMachineFunction(*F) != nullptr) - return error(Twine("redefinition of machine function '") + FunctionName + - "'"); - // Create the MachineFunction. - MachineFunction &MF = MMI.getOrCreateMachineFunction(*F); - if (initializeMachineFunction(YamlMF, MF)) - return true; + if (!MAM) { + if (MMI.getMachineFunction(*F) != nullptr) + return error(Twine("redefinition of machine function '") + FunctionName + + "'"); + + // Create the MachineFunction. + MachineFunction &MF = MMI.getOrCreateMachineFunction(*F); + if (initializeMachineFunction(YamlMF, MF)) + return true; + } else { + auto &FAM = + MAM->getResult(M).getManager(); + if (FAM.getCachedResult(*F)) + return error(Twine("redefinition of machine function '") + FunctionName + + "'"); + + // Create the MachineFunction. + MachineFunction &MF = FAM.getResult(*F).getMF(); + if (initializeMachineFunction(YamlMF, MF)) + return true; + } return false; } @@ -1101,6 +1120,11 @@ bool MIRParser::parseMachineFunctions(Module &M, MachineModuleInfo &MMI) { return Impl->parseMachineFunctions(M, MMI); } +bool MIRParser::parseMachineFunctions(Module &M, ModuleAnalysisManager &MAM) { + auto &MMI = MAM.getResult(M).getMMI(); + return Impl->parseMachineFunctions(M, MMI, &MAM); +} + std::unique_ptr llvm::createMIRParserFromFile( StringRef Filename, SMDiagnostic &Error, LLVMContext &Context, std::function ProcessIRFunction) { diff --git a/llvm/lib/CodeGen/MachineFunctionAnalysis.cpp b/llvm/lib/CodeGen/MachineFunctionAnalysis.cpp new file mode 100644 index 00000000000000..519a77c44acf84 --- /dev/null +++ b/llvm/lib/CodeGen/MachineFunctionAnalysis.cpp @@ -0,0 +1,46 @@ +//===- MachineFunctionAnalysis.cpp ----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the definitions of the MachineFunctionAnalysis +// members. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/MachineFunctionAnalysis.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/Target/TargetMachine.h" + +using namespace llvm; + +AnalysisKey MachineFunctionAnalysis::Key; + +bool MachineFunctionAnalysis::Result::invalidate( + Function &, const PreservedAnalyses &PA, + FunctionAnalysisManager::Invalidator &) { + // Unless it is invalidated explicitly, it should remain preserved. + auto PAC = PA.getChecker(); + return !PAC.preservedWhenStateless(); +} + +MachineFunctionAnalysis::Result +MachineFunctionAnalysis::run(Function &F, FunctionAnalysisManager &FAM) { + auto &Context = F.getContext(); + const TargetSubtargetInfo &STI = *TM->getSubtargetImpl(F); + auto &MMI = FAM.getResult(F) + .getCachedResult(*F.getParent()) + ->getMMI(); + auto MF = std::make_unique( + F, *TM, STI, Context.generateMachineFunctionNum(F), MMI); + MF->initTargetMachineFunctionInfo(STI); + + // MRI callback for target specific initializations. + TM->registerMachineRegisterInfoCallback(*MF); + + return Result(std::move(MF)); +} diff --git a/llvm/lib/CodeGen/MachinePassManager.cpp b/llvm/lib/CodeGen/MachinePassManager.cpp index 2763193b2c306b..6d540808d4cc98 100644 --- a/llvm/lib/CodeGen/MachinePassManager.cpp +++ b/llvm/lib/CodeGen/MachinePassManager.cpp @@ -12,21 +12,24 @@ #include "llvm/CodeGen/MachinePassManager.h" #include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionAnalysis.h" #include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/IR/PassManagerImpl.h" using namespace llvm; -namespace llvm { - AnalysisKey FunctionAnalysisManagerMachineFunctionProxy::Key; +namespace llvm { template class AnalysisManager; template class PassManager; template class InnerAnalysisManagerProxy; +template class InnerAnalysisManagerProxy; template class OuterAnalysisManagerProxy; +} // namespace llvm bool FunctionAnalysisManagerMachineFunctionProxy::Result::invalidate( MachineFunction &IR, const PreservedAnalyses &PA, @@ -69,38 +72,65 @@ bool MachineFunctionAnalysisManagerModuleProxy::Result::invalidate( return false; } +template <> +bool MachineFunctionAnalysisManagerFunctionProxy::Result::invalidate( + Function &F, const PreservedAnalyses &PA, + FunctionAnalysisManager::Invalidator &Inv) { + // If literally everything is preserved, we're done. + if (PA.areAllPreserved()) + return false; // This is still a valid proxy. + + // If this proxy isn't marked as preserved, then even if the result remains + // valid, the key itself may no longer be valid, so we clear everything. + // + // Note that in order to preserve this proxy, a module pass must ensure that + // the MFAM has been completely updated to handle the deletion of functions. + // Specifically, any MFAM-cached results for those functions need to have been + // forcibly cleared. When preserved, this proxy will only invalidate results + // cached on functions *still in the module* at the end of the module pass. + auto PAC = PA.getChecker(); + if (!PAC.preserved() && !PAC.preservedSet>()) { + InnerAM->clear(); + return true; + } + + // FIXME: be more precise, see + // FunctionAnalysisManagerModuleProxy::Result::invalidate. + if (!PA.allAnalysesInSetPreserved>()) { + InnerAM->clear(); + return true; + } + + // Return false to indicate that this result is still a valid proxy. + return false; +} + PreservedAnalyses -ModuleToMachineFunctionPassAdaptor::run(Module &M, ModuleAnalysisManager &AM) { - auto &MMI = AM.getResult(M).getMMI(); +FunctionToMachineFunctionPassAdaptor::run(Function &F, + FunctionAnalysisManager &FAM) { MachineFunctionAnalysisManager &MFAM = - AM.getResult(M).getManager(); - PassInstrumentation PI = AM.getResult(M); + FAM.getResult(F) + .getManager(); + PassInstrumentation PI = FAM.getResult(F); PreservedAnalyses PA = PreservedAnalyses::all(); - for (Function &F : M) { - // Do not codegen any 'available_externally' functions at all, they have - // definitions outside the translation unit. - if (F.isDeclaration() || F.hasAvailableExternallyLinkage()) - continue; + // Do not codegen any 'available_externally' functions at all, they have + // definitions outside the translation unit. + if (F.isDeclaration() || F.hasAvailableExternallyLinkage()) + return PreservedAnalyses::all(); - MachineFunction &MF = MMI.getOrCreateMachineFunction(F); + MachineFunction &MF = FAM.getResult(F).getMF(); - if (!PI.runBeforePass(*Pass, MF)) - continue; - PreservedAnalyses PassPA = Pass->run(MF, MFAM); - if (MMI.getMachineFunction(F)) { - MFAM.invalidate(MF, PassPA); - PI.runAfterPass(*Pass, MF, PassPA); - } else { - MFAM.clear(MF, F.getName()); - PI.runAfterPassInvalidated(*Pass, PassPA); - } - PA.intersect(std::move(PassPA)); - } + if (!PI.runBeforePass(*Pass, MF)) + return PreservedAnalyses::all(); + PreservedAnalyses PassPA = Pass->run(MF, MFAM); + MFAM.invalidate(MF, PassPA); + PI.runAfterPass(*Pass, MF, PassPA); + PA.intersect(std::move(PassPA)); return PA; } -void ModuleToMachineFunctionPassAdaptor::printPipeline( +void FunctionToMachineFunctionPassAdaptor::printPipeline( raw_ostream &OS, function_ref MapClassName2PassName) { OS << "machine-function("; Pass->printPipeline(OS, MapClassName2PassName); @@ -112,27 +142,24 @@ PreservedAnalyses PassManager::run(MachineFunction &MF, AnalysisManager &MFAM) { PassInstrumentation PI = MFAM.getResult(MF); - Function &F = MF.getFunction(); - MachineModuleInfo &MMI = - MFAM.getResult(MF) - .getCachedResult(*F.getParent()) - ->getMMI(); PreservedAnalyses PA = PreservedAnalyses::all(); for (auto &Pass : Passes) { if (!PI.runBeforePass(*Pass, MF)) continue; PreservedAnalyses PassPA = Pass->run(MF, MFAM); - if (MMI.getMachineFunction(F)) { - MFAM.invalidate(MF, PassPA); - PI.runAfterPass(*Pass, MF, PassPA); - } else { - MFAM.clear(MF, F.getName()); - PI.runAfterPassInvalidated(*Pass, PassPA); - } + MFAM.invalidate(MF, PassPA); + PI.runAfterPass(*Pass, MF, PassPA); PA.intersect(std::move(PassPA)); } return PA; } -} // namespace llvm +PreservedAnalyses llvm::getMachineFunctionPassPreservedAnalyses() { + PreservedAnalyses PA; + // Machine function passes are not allowed to modify the LLVM + // representation, therefore we should preserve all IR analyses. + PA.template preserveSet>(); + PA.template preserveSet>(); + return PA; +} diff --git a/llvm/lib/IR/LLVMContext.cpp b/llvm/lib/IR/LLVMContext.cpp index 57077e786efc26..8120cccace40b5 100644 --- a/llvm/lib/IR/LLVMContext.cpp +++ b/llvm/lib/IR/LLVMContext.cpp @@ -118,6 +118,13 @@ void LLVMContext::addModule(Module *M) { void LLVMContext::removeModule(Module *M) { pImpl->OwnedModules.erase(M); + pImpl->MachineFunctionNums.erase(M); +} + +unsigned LLVMContext::generateMachineFunctionNum(Function &F) { + Module *M = F.getParent(); + assert(pImpl->OwnedModules.contains(M) && "Unexpected module!"); + return pImpl->MachineFunctionNums[M]++; } //===----------------------------------------------------------------------===// diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h index 7c67e191348eaf..2713015c266c7e 100644 --- a/llvm/lib/IR/LLVMContextImpl.h +++ b/llvm/lib/IR/LLVMContextImpl.h @@ -1450,6 +1450,10 @@ class LLVMContextImpl { /// will be automatically deleted if this context is deleted. SmallPtrSet OwnedModules; + /// MachineFunctionNums - Keep the next available unique number available for + /// a MachineFunction in given module. Module must in OwnedModules. + DenseMap MachineFunctionNums; + /// The main remark streamer used by all the other streamers (e.g. IR, MIR, /// frontends, etc.). This should only be used by the specific streamers, and /// never directly. diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index 8d408ca2363a98..30d3e7a1ec05b8 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -82,7 +82,6 @@ #include "llvm/CodeGen/ExpandLargeDivRem.h" #include "llvm/CodeGen/ExpandLargeFpConvert.h" #include "llvm/CodeGen/ExpandMemCmp.h" -#include "llvm/CodeGen/FreeMachineFunction.h" #include "llvm/CodeGen/GCMetadata.h" #include "llvm/CodeGen/GlobalMerge.h" #include "llvm/CodeGen/HardwareLoops.h" @@ -92,6 +91,7 @@ #include "llvm/CodeGen/JMCInstrumenter.h" #include "llvm/CodeGen/LowerEmuTLS.h" #include "llvm/CodeGen/MIRPrinter.h" +#include "llvm/CodeGen/MachineFunctionAnalysis.h" #include "llvm/CodeGen/MachinePassManager.h" #include "llvm/CodeGen/SafeStack.h" #include "llvm/CodeGen/SelectOptimize.h" @@ -1230,7 +1230,7 @@ static bool isFunctionPassName(StringRef Name, CallbacksT &Callbacks) { StringRef NameNoBracket = Name.take_until([](char C) { return C == '<'; }); if (NameNoBracket == "function") return true; - if (Name == "loop" || Name == "loop-mssa") + if (Name == "loop" || Name == "loop-mssa" || Name == "machine-function") return true; // Explicitly handle custom-parsed pass names. @@ -1408,13 +1408,6 @@ Error PassBuilder::parseModulePass(ModulePassManager &MPM, MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM))); return Error::success(); } - if (Name == "machine-function") { - MachineFunctionPassManager MFPM; - if (auto Err = parseMachinePassPipeline(MFPM, InnerPipeline)) - return Err; - MPM.addPass(createModuleToMachineFunctionPassAdaptor(std::move(MFPM))); - return Error::success(); - } if (auto Params = parseFunctionPipelineName(Name)) { if (Params->second) return make_error( @@ -1732,6 +1725,13 @@ Error PassBuilder::parseFunctionPass(FunctionPassManager &FPM, FPM.addPass(createRepeatedPass(*Count, std::move(NestedFPM))); return Error::success(); } + if (Name == "machine-function") { + MachineFunctionPassManager MFPM; + if (auto Err = parseMachinePassPipeline(MFPM, InnerPipeline)) + return Err; + FPM.addPass(createFunctionToMachineFunctionPassAdaptor(std::move(MFPM))); + return Error::success(); + } for (auto &C : FunctionPipelineParsingCallbacks) if (C(Name, FPM, InnerPipeline)) @@ -1975,6 +1975,8 @@ void PassBuilder::crossRegisterProxies(LoopAnalysisManager &LAM, if (MFAM) { MAM.registerPass( [&] { return MachineFunctionAnalysisManagerModuleProxy(*MFAM); }); + FAM.registerPass( + [&] { return MachineFunctionAnalysisManagerFunctionProxy(*MFAM); }); MFAM->registerPass( [&] { return ModuleAnalysisManagerMachineFunctionProxy(MAM); }); MFAM->registerPass( @@ -2023,7 +2025,7 @@ Error PassBuilder::parsePassPipeline(ModulePassManager &MPM, std::move(*Pipeline)}}}}; } else if (isMachineFunctionPassName( FirstName, MachineFunctionPipelineParsingCallbacks)) { - Pipeline = {{"machine-function", std::move(*Pipeline)}}; + Pipeline = {{"function", {{"machine-function", std::move(*Pipeline)}}}}; } else { for (auto &C : TopLevelPipelineParsingCallbacks) if (C(MPM, *Pipeline)) diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index 2fbc7f7d88ba39..9b670e4e3a44bb 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -254,6 +254,9 @@ FUNCTION_ANALYSIS("demanded-bits", DemandedBitsAnalysis()) FUNCTION_ANALYSIS("domfrontier", DominanceFrontierAnalysis()) FUNCTION_ANALYSIS("domtree", DominatorTreeAnalysis()) FUNCTION_ANALYSIS("func-properties", FunctionPropertiesAnalysis()) +FUNCTION_ANALYSIS( + "machine-function-info", + MachineFunctionAnalysis(static_cast(TM))) FUNCTION_ANALYSIS("gc-function", GCFunctionAnalysis()) FUNCTION_ANALYSIS("inliner-size-estimator", InlineSizeEstimatorAnalysis()) FUNCTION_ANALYSIS("lazy-value-info", LazyValueAnalysis()) diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp index c18b462258623d..63490c83e85f05 100644 --- a/llvm/lib/Passes/StandardInstrumentations.cpp +++ b/llvm/lib/Passes/StandardInstrumentations.cpp @@ -297,14 +297,6 @@ void unwrapAndPrint(raw_ostream &OS, Any IR) { auto *M = unwrapModule(IR); assert(M && "should have unwrapped module"); printIR(OS, M); - - if (const auto *MF = unwrapIR(IR)) { - auto &MMI = MF->getMMI(); - for (const auto &F : *M) { - if (auto *MF = MMI.getMachineFunction(F)) - MF->print(OS); - } - } return; } diff --git a/llvm/test/tools/llc/new-pm/pipeline.ll b/llvm/test/tools/llc/new-pm/pipeline.ll index 1ace5963e4ef8c..d1a50642ea311b 100644 --- a/llvm/test/tools/llc/new-pm/pipeline.ll +++ b/llvm/test/tools/llc/new-pm/pipeline.ll @@ -1,5 +1,5 @@ ; RUN: llc -mtriple=x86_64-pc-linux-gnu -enable-new-pm -print-pipeline-passes -filetype=null %s | FileCheck %s ; CHECK: require,require -; CHECK: MachineSanitizerBinaryMetadata,FreeMachineFunctionPass +; CHECK: MachineSanitizerBinaryMetadata diff --git a/llvm/test/tools/llc/new-pm/pipeline.mir b/llvm/test/tools/llc/new-pm/pipeline.mir index fcc7d4f8f02e38..6baa710060f055 100644 --- a/llvm/test/tools/llc/new-pm/pipeline.mir +++ b/llvm/test/tools/llc/new-pm/pipeline.mir @@ -1,6 +1,6 @@ # RUN: llc -mtriple=x86_64-pc-linux-gnu -x mir -passes=no-op-machine-function --print-pipeline-passes -filetype=null < %s | FileCheck %s --match-full-lines -# CHECK: machine-function(no-op-machine-function),PrintMIRPreparePass,machine-function(print,FreeMachineFunctionPass) +# CHECK: function(machine-function(no-op-machine-function)),PrintMIRPreparePass,function(machine-function(print)) --- name: f diff --git a/llvm/test/tools/llc/new-pm/start-stop.ll b/llvm/test/tools/llc/new-pm/start-stop.ll index 8c795a7a70f810..ba225d227d4ca8 100644 --- a/llvm/test/tools/llc/new-pm/start-stop.ll +++ b/llvm/test/tools/llc/new-pm/start-stop.ll @@ -2,4 +2,4 @@ ; RUN: llc -mtriple=x86_64-pc-linux-gnu -enable-new-pm -print-pipeline-passes -start-before=mergeicmps -stop-after=gc-lowering -o /dev/null %s | FileCheck --match-full-lines %s --check-prefix=OBJ ; NULL: function(mergeicmps,expand-memcmp,gc-lowering) -; OBJ: function(mergeicmps,expand-memcmp,gc-lowering),PrintMIRPreparePass,machine-function(print) +; OBJ: function(mergeicmps,expand-memcmp,gc-lowering),PrintMIRPreparePass,function(machine-function(print),invalidate) diff --git a/llvm/tools/llc/NewPMDriver.cpp b/llvm/tools/llc/NewPMDriver.cpp index 6ae1b8db5e115c..6d9956ea07d356 100644 --- a/llvm/tools/llc/NewPMDriver.cpp +++ b/llvm/tools/llc/NewPMDriver.cpp @@ -16,7 +16,6 @@ #include "llvm/Analysis/CGSCCPassManager.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/CodeGen/CommandFlags.h" -#include "llvm/CodeGen/FreeMachineFunction.h" #include "llvm/CodeGen/MIRParser/MIRParser.h" #include "llvm/CodeGen/MIRPrinter.h" #include "llvm/CodeGen/MachineModuleInfo.h" @@ -120,11 +119,11 @@ int llvm::compileModuleWithNewPM( SI.registerCallbacks(PIC); registerCodeGenCallback(PIC, LLVMTM); + MachineFunctionAnalysisManager MFAM; LoopAnalysisManager LAM; FunctionAnalysisManager FAM; CGSCCAnalysisManager CGAM; ModuleAnalysisManager MAM; - MachineFunctionAnalysisManager MFAM; PassBuilder PB(Target.get(), PipelineTuningOptions(), std::nullopt, &PIC); PB.registerModuleAnalyses(MAM); PB.registerCGSCCAnalyses(CGAM); @@ -137,6 +136,7 @@ int llvm::compileModuleWithNewPM( MAM.registerPass([&] { return MachineModuleAnalysis(MMI); }); ModulePassManager MPM; + FunctionPassManager FPM; if (!PassPipeline.empty()) { // Construct a custom pass pipeline that starts after instruction @@ -152,10 +152,10 @@ int llvm::compileModuleWithNewPM( MPM.addPass(PrintMIRPreparePass(*OS)); MachineFunctionPassManager MFPM; MFPM.addPass(PrintMIRPass(*OS)); - MFPM.addPass(FreeMachineFunctionPass()); - MPM.addPass(createModuleToMachineFunctionPassAdaptor(std::move(MFPM))); + FPM.addPass(createFunctionToMachineFunctionPassAdaptor(std::move(MFPM))); + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); - if (MIR->parseMachineFunctions(*M, MMI)) + if (MIR->parseMachineFunctions(*M, MAM)) return 1; } else { ExitOnErr(LLVMTM.buildCodeGenPipeline( diff --git a/llvm/unittests/CodeGen/PassManagerTest.cpp b/llvm/unittests/CodeGen/PassManagerTest.cpp index 71f8832d436564..d3a410f5450cc6 100644 --- a/llvm/unittests/CodeGen/PassManagerTest.cpp +++ b/llvm/unittests/CodeGen/PassManagerTest.cpp @@ -184,8 +184,8 @@ TEST_F(PassManagerTest, Basic) { MachineModuleInfo MMI(LLVMTM); - LoopAnalysisManager LAM; MachineFunctionAnalysisManager MFAM; + LoopAnalysisManager LAM; FunctionAnalysisManager FAM; CGSCCAnalysisManager CGAM; ModuleAnalysisManager MAM; @@ -205,13 +205,17 @@ TEST_F(PassManagerTest, Basic) { std::vector Counts; ModulePassManager MPM; + FunctionPassManager FPM; MachineFunctionPassManager MFPM; MPM.addPass(TestMachineModulePass(Count, Counts)); - MPM.addPass(createModuleToMachineFunctionPassAdaptor( + FPM.addPass(createFunctionToMachineFunctionPassAdaptor( TestMachineFunctionPass(Count, Counts))); + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); MPM.addPass(TestMachineModulePass(Count, Counts)); MFPM.addPass(TestMachineFunctionPass(Count, Counts)); - MPM.addPass(createModuleToMachineFunctionPassAdaptor(std::move(MFPM))); + FPM = FunctionPassManager(); + FPM.addPass(createFunctionToMachineFunctionPassAdaptor(std::move(MFPM))); + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); testing::internal::CaptureStderr(); MPM.run(*M, MAM); @@ -248,8 +252,10 @@ TEST_F(PassManagerTest, DiagnosticHandler) { ModulePassManager MPM; FunctionPassManager FPM; MachineFunctionPassManager MFPM; + MPM.addPass(RequireAnalysisPass()); MFPM.addPass(ReportWarningPass()); - MPM.addPass(createModuleToMachineFunctionPassAdaptor(std::move(MFPM))); + FPM.addPass(createFunctionToMachineFunctionPassAdaptor(std::move(MFPM))); + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); testing::internal::CaptureStderr(); MPM.run(*M, MAM); std::string Output = testing::internal::GetCapturedStderr(); diff --git a/llvm/unittests/MIR/CMakeLists.txt b/llvm/unittests/MIR/CMakeLists.txt index 0ad52134a34da2..d6aff46eff07b7 100644 --- a/llvm/unittests/MIR/CMakeLists.txt +++ b/llvm/unittests/MIR/CMakeLists.txt @@ -15,7 +15,6 @@ set(LLVM_LINK_COMPONENTS add_llvm_unittest(MIRTests MachineMetadata.cpp - PassBuilderCallbacksTest.cpp ) target_link_libraries(MIRTests PRIVATE LLVMTestingSupport) diff --git a/llvm/unittests/MIR/PassBuilderCallbacksTest.cpp b/llvm/unittests/MIR/PassBuilderCallbacksTest.cpp deleted file mode 100644 index 6fd4e54a929f40..00000000000000 --- a/llvm/unittests/MIR/PassBuilderCallbacksTest.cpp +++ /dev/null @@ -1,575 +0,0 @@ -//===- unittests/MIR/PassBuilderCallbacksTest.cpp - PB Callback Tests -----===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "llvm/Analysis/CGSCCPassManager.h" -#include "llvm/Analysis/LoopAnalysisManager.h" -#include "llvm/CodeGen/FreeMachineFunction.h" -#include "llvm/MC/TargetRegistry.h" -#include "llvm/Target/TargetMachine.h" -#include "llvm/Testing/Support/Error.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace llvm; - -namespace { -using testing::_; -using testing::AnyNumber; -using testing::DoAll; -using testing::Not; -using testing::Return; -using testing::WithArgs; - -StringRef MIRString = R"MIR( ---- | - define void @test() { - ret void - } -... ---- -name: test -body: | - bb.0 (%ir-block.0): - RET64 -... -)MIR"; - -/// Helper for HasName matcher that returns getName both for IRUnit and -/// for IRUnit pointer wrapper into llvm::Any (wrapped by PassInstrumentation). -template std::string getName(const IRUnitT &IR) { - return std::string(IR.getName()); -} - -template <> std::string getName(const StringRef &name) { - return std::string(name); -} - -template <> std::string getName(const Any &WrappedIR) { - if (const auto *const *M = llvm::any_cast(&WrappedIR)) - return (*M)->getName().str(); - if (const auto *const *F = llvm::any_cast(&WrappedIR)) - return (*F)->getName().str(); - if (const auto *const *MF = - llvm::any_cast(&WrappedIR)) - return (*MF)->getName().str(); - return ""; -} -/// Define a custom matcher for objects which support a 'getName' method. -/// -/// LLVM often has IR objects or analysis objects which expose a name -/// and in tests it is convenient to match these by name for readability. -/// Usually, this name is either a StringRef or a plain std::string. This -/// matcher supports any type exposing a getName() method of this form whose -/// return value is compatible with an std::ostream. For StringRef, this uses -/// the shift operator defined above. -/// -/// It should be used as: -/// -/// HasName("my_function") -/// -/// No namespace or other qualification is required. -MATCHER_P(HasName, Name, "") { - *result_listener << "has name '" << getName(arg) << "'"; - return Name == getName(arg); -} - -MATCHER_P(HasNameRegex, Name, "") { - *result_listener << "has name '" << getName(arg) << "'"; - llvm::Regex r(Name); - return r.match(getName(arg)); -} - -struct MockPassInstrumentationCallbacks { - MockPassInstrumentationCallbacks() { - ON_CALL(*this, runBeforePass(_, _)).WillByDefault(Return(true)); - } - MOCK_METHOD2(runBeforePass, bool(StringRef PassID, llvm::Any)); - MOCK_METHOD2(runBeforeSkippedPass, void(StringRef PassID, llvm::Any)); - MOCK_METHOD2(runBeforeNonSkippedPass, void(StringRef PassID, llvm::Any)); - MOCK_METHOD3(runAfterPass, - void(StringRef PassID, llvm::Any, const PreservedAnalyses &PA)); - MOCK_METHOD2(runAfterPassInvalidated, - void(StringRef PassID, const PreservedAnalyses &PA)); - MOCK_METHOD2(runBeforeAnalysis, void(StringRef PassID, llvm::Any)); - MOCK_METHOD2(runAfterAnalysis, void(StringRef PassID, llvm::Any)); - - void registerPassInstrumentation(PassInstrumentationCallbacks &Callbacks) { - Callbacks.registerShouldRunOptionalPassCallback( - [this](StringRef P, llvm::Any IR) { - return this->runBeforePass(P, IR); - }); - Callbacks.registerBeforeSkippedPassCallback( - [this](StringRef P, llvm::Any IR) { - this->runBeforeSkippedPass(P, IR); - }); - Callbacks.registerBeforeNonSkippedPassCallback( - [this](StringRef P, llvm::Any IR) { - this->runBeforeNonSkippedPass(P, IR); - }); - Callbacks.registerAfterPassCallback( - [this](StringRef P, llvm::Any IR, const PreservedAnalyses &PA) { - this->runAfterPass(P, IR, PA); - }); - Callbacks.registerAfterPassInvalidatedCallback( - [this](StringRef P, const PreservedAnalyses &PA) { - this->runAfterPassInvalidated(P, PA); - }); - Callbacks.registerBeforeAnalysisCallback([this](StringRef P, llvm::Any IR) { - return this->runBeforeAnalysis(P, IR); - }); - Callbacks.registerAfterAnalysisCallback( - [this](StringRef P, llvm::Any IR) { this->runAfterAnalysis(P, IR); }); - } - - void ignoreNonMockPassInstrumentation(StringRef IRName) { - // Generic EXPECT_CALLs are needed to match instrumentation on unimportant - // parts of a pipeline that we do not care about (e.g. various passes added - // by default by PassBuilder - Verifier pass etc). - // Make sure to avoid ignoring Mock passes/analysis, we definitely want - // to check these explicitly. - EXPECT_CALL(*this, - runBeforePass(Not(HasNameRegex("Mock")), HasName(IRName))) - .Times(AnyNumber()) - .WillRepeatedly(Return(false)); - EXPECT_CALL( - *this, runBeforeSkippedPass(Not(HasNameRegex("Mock")), HasName(IRName))) - .Times(AnyNumber()); - EXPECT_CALL(*this, runBeforeNonSkippedPass(Not(HasNameRegex("Mock")), - HasName(IRName))) - .Times(AnyNumber()); - EXPECT_CALL(*this, - runAfterPass(Not(HasNameRegex("Mock")), HasName(IRName), _)) - .Times(AnyNumber()); - EXPECT_CALL(*this, - runBeforeAnalysis(Not(HasNameRegex("Mock")), HasName(IRName))) - .Times(AnyNumber()); - EXPECT_CALL(*this, - runAfterAnalysis(Not(HasNameRegex("Mock")), HasName(IRName))) - .Times(AnyNumber()); - } -}; - -template class MockAnalysisHandleBase { -public: - class Analysis : public AnalysisInfoMixin { - friend AnalysisInfoMixin; - friend MockAnalysisHandleBase; - static AnalysisKey Key; - - DerivedT *Handle; - - Analysis(DerivedT &Handle) : Handle(&Handle) { - static_assert(std::is_base_of::value, - "Must pass the derived type to this template!"); - } - - public: - class Result { - friend MockAnalysisHandleBase; - - DerivedT *Handle; - - Result(DerivedT &Handle) : Handle(&Handle) {} - - public: - // Forward invalidation events to the mock handle. - bool invalidate(MachineFunction &IR, const PreservedAnalyses &PA, - MachineFunctionAnalysisManager::Invalidator &Inv) { - return Handle->invalidate(IR, PA, Inv); - } - }; - - Result run(MachineFunction &IR, MachineFunctionAnalysisManager &AM) { - return Handle->run(IR, AM); - } - }; - - Analysis getAnalysis() { return Analysis(static_cast(*this)); } - typename Analysis::Result getResult() { - return typename Analysis::Result(static_cast(*this)); - } - static StringRef getName() { return llvm::getTypeName(); } - -protected: - // FIXME: MSVC seems unable to handle a lambda argument to Invoke from within - // the template, so we use a boring static function. - static bool - invalidateCallback(MachineFunction &IR, const PreservedAnalyses &PA, - MachineFunctionAnalysisManager::Invalidator &Inv) { - auto PAC = PA.template getChecker(); - return !PAC.preserved() && - !PAC.template preservedSet>(); - } - - /// Derived classes should call this in their constructor to set up default - /// mock actions. (We can't do this in our constructor because this has to - /// run after the DerivedT is constructed.) - void setDefaults() { - ON_CALL(static_cast(*this), run(_, _)) - .WillByDefault(Return(this->getResult())); - ON_CALL(static_cast(*this), invalidate(_, _, _)) - .WillByDefault(&invalidateCallback); - } -}; - -template class MockPassHandleBase { -public: - class Pass : public PassInfoMixin { - friend MockPassHandleBase; - - DerivedT *Handle; - - Pass(DerivedT &Handle) : Handle(&Handle) { - static_assert(std::is_base_of::value, - "Must pass the derived type to this template!"); - } - - public: - PreservedAnalyses run(MachineFunction &IR, - MachineFunctionAnalysisManager &AM) { - return Handle->run(IR, AM); - } - }; - - static StringRef getName() { return llvm::getTypeName(); } - - Pass getPass() { return Pass(static_cast(*this)); } - -protected: - /// Derived classes should call this in their constructor to set up default - /// mock actions. (We can't do this in our constructor because this has to - /// run after the DerivedT is constructed.) - void setDefaults() { - ON_CALL(static_cast(*this), run(_, _)) - .WillByDefault(Return(PreservedAnalyses::all())); - } -}; - -struct MockAnalysisHandle : public MockAnalysisHandleBase { - MOCK_METHOD2(run, Analysis::Result(MachineFunction &, - MachineFunctionAnalysisManager &)); - - MOCK_METHOD3(invalidate, bool(MachineFunction &, const PreservedAnalyses &, - MachineFunctionAnalysisManager::Invalidator &)); - - MockAnalysisHandle() { setDefaults(); } -}; - -template -AnalysisKey MockAnalysisHandleBase::Analysis::Key; - -class MockPassHandle : public MockPassHandleBase { -public: - MOCK_METHOD2(run, PreservedAnalyses(MachineFunction &, - MachineFunctionAnalysisManager &)); - - MockPassHandle() { setDefaults(); } -}; - -class MachineFunctionCallbacksTest : public testing::Test { -protected: - static void SetUpTestCase() { - InitializeAllTargetInfos(); - InitializeAllTargets(); - InitializeAllTargetMCs(); - } - - LLVMContext Context; - - std::unique_ptr TM; - std::unique_ptr MMI; - - std::unique_ptr M; - - PassInstrumentationCallbacks PIC; - std::unique_ptr PB; - ModulePassManager MPM; - MachineFunctionAnalysisManager MFAM; - LoopAnalysisManager LAM; - FunctionAnalysisManager FAM; - CGSCCAnalysisManager CGAM; - ModuleAnalysisManager MAM; - - MockPassInstrumentationCallbacks CallbacksHandle; - MockPassHandle PassHandle; - MockAnalysisHandle AnalysisHandle; - - static std::unique_ptr parseMIR(StringRef MIRCode, - LLVMContext &Context, - TargetMachine &TM, - MachineModuleInfo &MMI) { - SMDiagnostic Diagnostic; - std::unique_ptr MBuffer = MemoryBuffer::getMemBuffer(MIRCode); - std::unique_ptr MIR = - createMIRParser(std::move(MBuffer), Context); - assert(MIR); - - std::unique_ptr Mod = MIR->parseIRModule(); - assert(Mod); - - // Module identifier is used in tests below. - Mod->setModuleIdentifier("module"); - Mod->setDataLayout(TM.createDataLayout()); - - [[maybe_unused]] bool Ret = MIR->parseMachineFunctions(*Mod, MMI); - assert(!Ret); - - return Mod; - } - - static PreservedAnalyses - getAnalysisResult(MachineFunction &U, MachineFunctionAnalysisManager &MFAM) { - MFAM.getResult(U); - return PreservedAnalyses::all(); - } - - void SetUp() override { - std::string Error; - auto TripleName = "x86_64-pc-linux-gnu"; - auto *T = TargetRegistry::lookupTarget(TripleName, Error); - if (!T) - GTEST_SKIP(); - TM = std::unique_ptr( - static_cast(T->createTargetMachine( - TripleName, "", "", TargetOptions(), std::nullopt))); - if (!TM) - GTEST_SKIP(); - - MMI = std::make_unique(TM.get()); - M = parseMIR(MIRString, Context, *TM, *MMI); - PB = std::make_unique(TM.get(), PipelineTuningOptions(), - std::nullopt, &PIC); - - /// Register a callback for analysis registration. - /// - /// The callback is a function taking a reference to an AnalyisManager - /// object. When called, the callee gets to register its own analyses with - /// this PassBuilder instance. - PB->registerAnalysisRegistrationCallback( - [this](MachineFunctionAnalysisManager &AM) { - // Register our mock analysis - AM.registerPass([this] { return AnalysisHandle.getAnalysis(); }); - }); - - /// Register a callback for pipeline parsing. - /// - /// During parsing of a textual pipeline, the PassBuilder will call these - /// callbacks for each encountered pass name that it does not know. This - /// includes both simple pass names as well as names of sub-pipelines. In - /// the latter case, the InnerPipeline is not empty. - PB->registerPipelineParsingCallback( - [this](StringRef Name, MachineFunctionPassManager &PM, - ArrayRef InnerPipeline) { - if (parseAnalysisUtilityPasses( - "test-analysis", Name, PM)) - return true; - - /// Parse the name of our pass mock handle - if (Name == "test-transform") { - PM.addPass(PassHandle.getPass()); - return true; - } - return false; - }); - - /// Register builtin analyses and cross-register the analysis proxies - PB->registerModuleAnalyses(MAM); - PB->registerCGSCCAnalyses(CGAM); - PB->registerFunctionAnalyses(FAM); - PB->registerLoopAnalyses(LAM); - PB->registerMachineFunctionAnalyses(MFAM); - PB->crossRegisterProxies(LAM, FAM, CGAM, MAM, &MFAM); - MAM.registerPass([&] { return MachineModuleAnalysis(*MMI); }); - } -}; - -TEST_F(MachineFunctionCallbacksTest, Passes) { - EXPECT_CALL(AnalysisHandle, run(HasName("test"), _)); - EXPECT_CALL(PassHandle, run(HasName("test"), _)).WillOnce(&getAnalysisResult); - - StringRef PipelineText = "test-transform"; - ASSERT_THAT_ERROR(PB->parsePassPipeline(MPM, PipelineText), Succeeded()) - << "Pipeline was: " << PipelineText; - MPM.run(*M, MAM); -} - -TEST_F(MachineFunctionCallbacksTest, InstrumentedPasses) { - CallbacksHandle.registerPassInstrumentation(PIC); - // Non-mock instrumentation not specifically mentioned below can be ignored. - CallbacksHandle.ignoreNonMockPassInstrumentation("test"); - CallbacksHandle.ignoreNonMockPassInstrumentation("module"); - - // PassInstrumentation calls should happen in-sequence, in the same order - // as passes/analyses are scheduled. - ::testing::Sequence PISequence; - EXPECT_CALL(CallbacksHandle, - runBeforePass(HasNameRegex("MockPassHandle"), HasName("test"))) - .InSequence(PISequence) - .WillOnce(Return(true)); - EXPECT_CALL( - CallbacksHandle, - runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), HasName("test"))) - .InSequence(PISequence); - EXPECT_CALL( - CallbacksHandle, - runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("test"))) - .InSequence(PISequence); - EXPECT_CALL( - CallbacksHandle, - runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("test"))) - .InSequence(PISequence); - EXPECT_CALL(CallbacksHandle, - runAfterPass(HasNameRegex("MockPassHandle"), HasName("test"), _)) - .InSequence(PISequence); - EXPECT_CALL( - CallbacksHandle, - runBeforeSkippedPass(HasNameRegex("MockPassHandle"), HasName("test"))) - .Times(0); - - EXPECT_CALL(AnalysisHandle, run(HasName("test"), _)); - EXPECT_CALL(PassHandle, run(HasName("test"), _)).WillOnce(&getAnalysisResult); - - StringRef PipelineText = "test-transform"; - ASSERT_THAT_ERROR(PB->parsePassPipeline(MPM, PipelineText), Succeeded()) - << "Pipeline was: " << PipelineText; - MPM.run(*M, MAM); -} - -TEST_F(MachineFunctionCallbacksTest, InstrumentedSkippedPasses) { - CallbacksHandle.registerPassInstrumentation(PIC); - // Non-mock instrumentation run here can safely be ignored. - CallbacksHandle.ignoreNonMockPassInstrumentation("test"); - CallbacksHandle.ignoreNonMockPassInstrumentation("module"); - - // Skip the pass by returning false. - EXPECT_CALL(CallbacksHandle, - runBeforePass(HasNameRegex("MockPassHandle"), HasName("test"))) - .WillOnce(Return(false)); - - EXPECT_CALL( - CallbacksHandle, - runBeforeSkippedPass(HasNameRegex("MockPassHandle"), HasName("test"))) - .Times(1); - - EXPECT_CALL(AnalysisHandle, run(HasName("test"), _)).Times(0); - EXPECT_CALL(PassHandle, run(HasName("test"), _)).Times(0); - - // As the pass is skipped there is no afterPass, beforeAnalysis/afterAnalysis - // as well. - EXPECT_CALL(CallbacksHandle, - runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), _)) - .Times(0); - EXPECT_CALL(CallbacksHandle, - runAfterPass(HasNameRegex("MockPassHandle"), _, _)) - .Times(0); - EXPECT_CALL(CallbacksHandle, - runAfterPassInvalidated(HasNameRegex("MockPassHandle"), _)) - .Times(0); - EXPECT_CALL(CallbacksHandle, - runAfterPass(HasNameRegex("MockPassHandle"), _, _)) - .Times(0); - EXPECT_CALL(CallbacksHandle, - runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), _)) - .Times(0); - EXPECT_CALL(CallbacksHandle, - runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), _)) - .Times(0); - - StringRef PipelineText = "test-transform"; - ASSERT_THAT_ERROR(PB->parsePassPipeline(MPM, PipelineText), Succeeded()) - << "Pipeline was: " << PipelineText; - MPM.run(*M, MAM); -} - -// Check that the Module -> MachineFunction adaptor properly calls -// runAfterPassInvalidated. -TEST_F(MachineFunctionCallbacksTest, InstrumentedFreeMFPass) { - CallbacksHandle.registerPassInstrumentation(PIC); - // Non-mock instrumentation run here can safely be ignored. - CallbacksHandle.ignoreNonMockPassInstrumentation("test"); - CallbacksHandle.ignoreNonMockPassInstrumentation("module"); - - ::testing::Sequence PISequence; - EXPECT_CALL( - CallbacksHandle, - runBeforePass(HasNameRegex("FreeMachineFunctionPass"), HasName("test"))) - .InSequence(PISequence) - .WillOnce(Return(true)); - EXPECT_CALL(CallbacksHandle, - runBeforeNonSkippedPass(HasNameRegex("FreeMachineFunctionPass"), - HasName("test"))) - .InSequence(PISequence); - EXPECT_CALL(CallbacksHandle, runAfterPassInvalidated( - HasNameRegex("FreeMachineFunctionPass"), _)) - .InSequence(PISequence); - - // runAfterPass should not be called since the MachineFunction is no longer - // valid after FreeMachineFunctionPass. - EXPECT_CALL(CallbacksHandle, - runAfterPass(HasNameRegex("FreeMachineFunctionPass"), _, _)) - .Times(0); - - MPM.addPass( - createModuleToMachineFunctionPassAdaptor(FreeMachineFunctionPass())); - MPM.run(*M, MAM); -} - -// Check that the Module -> MachineFunction adaptor and MachineFunction pass -// manager properly call runAfterPassInvalidated. -TEST_F(MachineFunctionCallbacksTest, InstrumentedFreeMFPass2) { - CallbacksHandle.registerPassInstrumentation(PIC); - // Non-mock instrumentation run here can safely be ignored. - CallbacksHandle.ignoreNonMockPassInstrumentation("test"); - CallbacksHandle.ignoreNonMockPassInstrumentation("module"); - - ::testing::Sequence PISequence; - EXPECT_CALL( - CallbacksHandle, - runBeforePass(HasNameRegex("FreeMachineFunctionPass"), HasName("test"))) - .InSequence(PISequence) - .WillOnce(Return(true)); - EXPECT_CALL(CallbacksHandle, - runBeforeNonSkippedPass(HasNameRegex("FreeMachineFunctionPass"), - HasName("test"))) - .InSequence(PISequence); - EXPECT_CALL(CallbacksHandle, runAfterPassInvalidated( - HasNameRegex("FreeMachineFunctionPass"), _)) - .InSequence(PISequence); - EXPECT_CALL(CallbacksHandle, - runAfterPassInvalidated(HasNameRegex("PassManager"), _)) - .InSequence(PISequence); - - // runAfterPass should not be called since the MachineFunction is no longer - // valid after FreeMachineFunctionPass. - EXPECT_CALL(CallbacksHandle, - runAfterPass(HasNameRegex("FreeMachineFunctionPass"), _, _)) - .Times(0); - EXPECT_CALL(CallbacksHandle, runAfterPass(HasNameRegex("PassManager"), _, _)) - .Times(0); - - MachineFunctionPassManager MFPM; - MFPM.addPass(FreeMachineFunctionPass()); - MPM.addPass(createModuleToMachineFunctionPassAdaptor(std::move(MFPM))); - MPM.run(*M, MAM); -} - -} // end anonymous namespace From b3291793f11924a3b62601aabebebdcfbb12a9a1 Mon Sep 17 00:00:00 2001 From: Phoebe Wang Date: Tue, 30 Apr 2024 10:09:41 +0800 Subject: [PATCH 13/65] [X86] Enable EVEX512 when host CPU has AVX512 (#90479) This is used when -march=native run on an unknown CPU to old version of LLVM. --- llvm/lib/TargetParser/Host.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/llvm/lib/TargetParser/Host.cpp b/llvm/lib/TargetParser/Host.cpp index 0a93b06f40c248..e911d15498545c 100644 --- a/llvm/lib/TargetParser/Host.cpp +++ b/llvm/lib/TargetParser/Host.cpp @@ -1287,8 +1287,10 @@ static void getAvailableFeatures(unsigned ECX, unsigned EDX, unsigned MaxLeaf, setFeature(X86::FEATURE_AVX2); if (HasLeaf7 && ((EBX >> 8) & 1)) setFeature(X86::FEATURE_BMI2); - if (HasLeaf7 && ((EBX >> 16) & 1) && HasAVX512Save) + if (HasLeaf7 && ((EBX >> 16) & 1) && HasAVX512Save) { setFeature(X86::FEATURE_AVX512F); + setFeature(X86::FEATURE_EVEX512); + } if (HasLeaf7 && ((EBX >> 17) & 1) && HasAVX512Save) setFeature(X86::FEATURE_AVX512DQ); if (HasLeaf7 && ((EBX >> 19) & 1)) @@ -1799,6 +1801,7 @@ bool sys::getHostCPUFeatures(StringMap &Features) { Features["rtm"] = HasLeaf7 && ((EBX >> 11) & 1); // AVX512 is only supported if the OS supports the context save for it. Features["avx512f"] = HasLeaf7 && ((EBX >> 16) & 1) && HasAVX512Save; + Features["evex512"] = Features["avx512f"]; Features["avx512dq"] = HasLeaf7 && ((EBX >> 17) & 1) && HasAVX512Save; Features["rdseed"] = HasLeaf7 && ((EBX >> 18) & 1); Features["adx"] = HasLeaf7 && ((EBX >> 19) & 1); From 9d5411ffba0d94b60050cc873773935addca9533 Mon Sep 17 00:00:00 2001 From: sinan Date: Tue, 30 Apr 2024 11:27:18 +0800 Subject: [PATCH 14/65] [BOLT] Avoid reference updates for non-JT symbol operands (#88838) Skip updating references for operands that do not directly refer to jump table symbols but fall within a jump table's address range to prevent unintended modifications. --- bolt/lib/Passes/ValidateMemRefs.cpp | 8 +-- bolt/test/X86/jt-symbol-disambiguation-4.s | 60 ++++++++++++++++++++++ 2 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 bolt/test/X86/jt-symbol-disambiguation-4.s diff --git a/bolt/lib/Passes/ValidateMemRefs.cpp b/bolt/lib/Passes/ValidateMemRefs.cpp index f29a97c43f497c..ca58493b279c9e 100644 --- a/bolt/lib/Passes/ValidateMemRefs.cpp +++ b/bolt/lib/Passes/ValidateMemRefs.cpp @@ -29,8 +29,7 @@ bool ValidateMemRefs::checkAndFixJTReference(BinaryFunction &BF, MCInst &Inst, if (!BD) return false; - const uint64_t TargetAddress = BD->getAddress() + Offset; - JumpTable *JT = BC.getJumpTableContainingAddress(TargetAddress); + JumpTable *JT = BC.getJumpTableContainingAddress(BD->getAddress()); if (!JT) return false; @@ -43,8 +42,9 @@ bool ValidateMemRefs::checkAndFixJTReference(BinaryFunction &BF, MCInst &Inst, // the jump table label with a regular rodata reference. Get a // non-JT reference by fetching the symbol 1 byte before the JT // label. - MCSymbol *NewSym = BC.getOrCreateGlobalSymbol(TargetAddress - 1, "DATAat"); - BC.MIB->setOperandToSymbolRef(Inst, OperandNum, NewSym, 1, &*BC.Ctx, 0); + MCSymbol *NewSym = BC.getOrCreateGlobalSymbol(BD->getAddress() - 1, "DATAat"); + BC.MIB->setOperandToSymbolRef(Inst, OperandNum, NewSym, Offset + 1, &*BC.Ctx, + 0); LLVM_DEBUG(dbgs() << "BOLT-DEBUG: replaced reference @" << BF.getPrintName() << " from " << BD->getName() << " to " << NewSym->getName() << " + 1\n"); diff --git a/bolt/test/X86/jt-symbol-disambiguation-4.s b/bolt/test/X86/jt-symbol-disambiguation-4.s new file mode 100644 index 00000000000000..816b0fb501a590 --- /dev/null +++ b/bolt/test/X86/jt-symbol-disambiguation-4.s @@ -0,0 +1,60 @@ +# If the operand references a symbol that differs from the jump table label, +# no reference updating is required even if its target address resides within +# the jump table's range. +# In this test case, consider the second instruction within the main function, +# where the address resulting from 'c + 17' corresponds to one byte beyond the +# address of the .LJTI2_0 jump table label. However, this operand represents +# an offset calculation related to the global variable 'c' and should remain +# unaffected by the jump table. + +# REQUIRES: system-linux + + +# RUN: %clang -no-pie %s -o %t.exe -Wl,-q + +# RUN: %t.exe +# RUN: llvm-bolt -funcs=main,foo/1 %t.exe -o %t.exe.bolt -jump-tables=move +# RUN: %t.exe.bolt + + .text + .globl main + .type main,@function +main: + pushq %rbp + movq %rsp, %rbp + movq $-16, %rax + movl c+17(%rax), %edx + cmpl $255, %edx + je .LCorrect + movl $1, %eax + popq %rbp + ret +.LCorrect: + movl $0, %eax + popq %rbp + ret + .p2align 4, 0x90 + .type foo,@function +foo: + movq $0, %rax + jmpq *.LJTI2_0(,%rax,8) + addl $-36, %eax +.LBB2_2: + addl $-16, %eax + retq + .section .rodata,"a",@progbits + .type c,@object + .data + .globl c + .p2align 4, 0x0 +c: + .byte 1 + .byte 0xff + .zero 14 + .size c, 16 +.LJTI2_0: + .quad .LBB2_2 + .quad .LBB2_2 + .quad .LBB2_2 + .quad .LBB2_2 + From 38067c50a9459caed2892e38b2ae5026a8bff8e2 Mon Sep 17 00:00:00 2001 From: Chuanqi Xu Date: Tue, 30 Apr 2024 10:59:37 +0800 Subject: [PATCH 15/65] [C++20] [Modules] [Reduced BMI] Avoid force writing static declarations within module purview Close https://github.com/llvm/llvm-project/issues/90259 Technically, the static declarations shouldn't be leaked from the module interface, otherwise it is an illegal program according to the spec. So we can get rid of the static declarations from the reduced BMI technically. Then we can close the above issue. However, there are too many `static inline` codes in existing headers. So it will be a pretty big breaking change if we do this globally. --- clang/lib/Serialization/ASTWriter.cpp | 36 ++++++++++++++++++++-- clang/test/Modules/pr90259.cppm | 44 +++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 clang/test/Modules/pr90259.cppm diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 0408eeb6a95b00..7db60c67d71234 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -3205,6 +3205,17 @@ void ASTWriter::WriteType(QualType T) { // Declaration Serialization //===----------------------------------------------------------------------===// +static bool IsInternalDeclFromFileContext(const Decl *D) { + auto *ND = dyn_cast(D); + if (!ND) + return false; + + if (!D->getDeclContext()->getRedeclContext()->isFileContext()) + return false; + + return ND->getFormalLinkage() == Linkage::Internal; +} + /// Write the block containing all of the declaration IDs /// lexically declared within the given DeclContext. /// @@ -3225,6 +3236,15 @@ uint64_t ASTWriter::WriteDeclContextLexicalBlock(ASTContext &Context, if (DoneWritingDeclsAndTypes && !wasDeclEmitted(D)) continue; + // We don't need to write decls with internal linkage into reduced BMI. + // If such decls gets emitted due to it get used from inline functions, + // the program illegal. However, there are too many use of static inline + // functions in the global module fragment and it will be breaking change + // to forbid that. So we have to allow to emit such declarations from GMF. + if (GeneratingReducedBMI && !D->isFromExplicitGlobalModule() && + IsInternalDeclFromFileContext(D)) + continue; + KindDeclPairs.push_back(D->getKind()); KindDeclPairs.push_back(GetDeclRef(D).get()); } @@ -3886,6 +3906,13 @@ class ASTDeclContextNameLookupTrait { !Writer.wasDeclEmitted(DeclForLocalLookup)) continue; + // Try to avoid writing internal decls to reduced BMI. + // See comments in ASTWriter::WriteDeclContextLexicalBlock for details. + if (Writer.isGeneratingReducedBMI() && + !DeclForLocalLookup->isFromExplicitGlobalModule() && + IsInternalDeclFromFileContext(DeclForLocalLookup)) + continue; + DeclIDs.push_back(Writer.GetDeclRef(DeclForLocalLookup)); } return std::make_pair(Start, DeclIDs.size()); @@ -4257,6 +4284,12 @@ uint64_t ASTWriter::WriteDeclContextVisibleBlock(ASTContext &Context, if (DoneWritingDeclsAndTypes && !wasDeclEmitted(ND)) continue; + // We don't need to force emitting internal decls into reduced BMI. + // See comments in ASTWriter::WriteDeclContextLexicalBlock for details. + if (GeneratingReducedBMI && !ND->isFromExplicitGlobalModule() && + IsInternalDeclFromFileContext(ND)) + continue; + GetDeclRef(ND); } } @@ -4917,8 +4950,7 @@ void ASTWriter::PrepareWritingSpecialDecls(Sema &SemaRef) { // is ill-formed. However, in practice, there are a lot of projects // uses `static inline` in the headers. So we can't get rid of all // static entities in reduced BMI now. - if (auto *ND = dyn_cast(D); - ND && ND->getFormalLinkage() == Linkage::Internal) + if (IsInternalDeclFromFileContext(D)) continue; } diff --git a/clang/test/Modules/pr90259.cppm b/clang/test/Modules/pr90259.cppm new file mode 100644 index 00000000000000..17786998a2a729 --- /dev/null +++ b/clang/test/Modules/pr90259.cppm @@ -0,0 +1,44 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/mod1.cppm -emit-reduced-module-interface -o %t/mod-mod1.pcm +// RUN: %clang_cc1 -std=c++20 %t/mod.cppm -fprebuilt-module-path=%t \ +// RUN: -emit-reduced-module-interface -o %t/mod.pcm +// RUN: %clang_cc1 -std=c++20 %t/use.cpp -fprebuilt-module-path=%t -verify -fsyntax-only + +//--- mod1.cppm +export module mod:mod1; +namespace { + int abc = 43; +} +namespace mod { + static int def = 44; +} +export int f() { + return abc + mod::def; +} + +//--- mod.cppm +// expected-no-diagnostics +export module mod; +import :mod1; + +namespace { + double abc = 43.0; +} + +namespace mod { + static double def = 44.0; +} + +export double func() { + return (double)f() + abc + mod::def; +} + +//--- use.cpp +// expected-no-diagnostics +import mod; +double use() { + return func(); +} From 62d6560471f0e1151e34c0a56357423350f7a6af Mon Sep 17 00:00:00 2001 From: thetruestblue <92476612+thetruestblue@users.noreply.github.com> Date: Mon, 29 Apr 2024 21:50:57 -0700 Subject: [PATCH 16/65] Disable test for lsan and x86_64h (#90483) Disable this test on x86_64h for LSan. This test is failing with malformed object only on x86_64h. Disabling for now. rdar://125052424 --- .../TestCases/sanitizer_coverage_trace_pc_guard.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler-rt/test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard.cpp b/compiler-rt/test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard.cpp index ed817961c688cc..e46c2edac4cea1 100644 --- a/compiler-rt/test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard.cpp +++ b/compiler-rt/test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard.cpp @@ -2,6 +2,8 @@ // REQUIRES: has_sancovcc // UNSUPPORTED: ubsan,i386-darwin,target={{(powerpc64|s390x|thumb).*}} +// This test is failing for lsan on darwin on x86_64h. +// UNSUPPORTED: x86_64h && lsan && darwin // XFAIL: tsan // XFAIL: android && asan From 326667d727546dad0ce9315aa93a3da698c7c71c Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Mon, 29 Apr 2024 17:45:50 -0700 Subject: [PATCH 17/65] [RISCV] Merge variable declaration with first assignment. NFC --- llvm/lib/TargetParser/RISCVISAInfo.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/llvm/lib/TargetParser/RISCVISAInfo.cpp b/llvm/lib/TargetParser/RISCVISAInfo.cpp index 20182fb06037c2..24b774314fea84 100644 --- a/llvm/lib/TargetParser/RISCVISAInfo.cpp +++ b/llvm/lib/TargetParser/RISCVISAInfo.cpp @@ -398,11 +398,10 @@ RISCVISAInfo::parseFeatures(unsigned XLen, for (auto &Feature : Features) { StringRef ExtName = Feature; - bool Experimental = false; assert(ExtName.size() > 1 && (ExtName[0] == '+' || ExtName[0] == '-')); bool Add = ExtName[0] == '+'; ExtName = ExtName.drop_front(1); // Drop '+' or '-' - Experimental = stripExperimentalPrefix(ExtName); + bool Experimental = stripExperimentalPrefix(ExtName); auto ExtensionInfos = Experimental ? ArrayRef(SupportedExperimentalExtensions) : ArrayRef(SupportedExtensions); From 79095b4079e8d4f8176bcc53fdacd2765f310cdb Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Mon, 29 Apr 2024 22:05:35 -0700 Subject: [PATCH 18/65] [ELF] --compress-debug-sections=zstd: replace ZSTD_c_nbWorkers parallelism with multi-frame parallelism https://reviews.llvm.org/D133679 utilizes zstd's multithread API to create one single frame. This provides a higher compression ratio but is significantly slower than concatenating multiple frames. With manual parallelism, it is easier to parallelize memcpy in OutputSection::writeTo for parallel memcpy. In addition, as the individual allocated decompression buffers are much smaller, we can make a wild guess (compressed_size/4) without worrying about a resize (due to wrong guess) would waste memory. --- lld/ELF/OutputSections.cpp | 133 ++++++++++++++++--------------------- 1 file changed, 59 insertions(+), 74 deletions(-) diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index eadab9d745d687..3e58ed4bda2d3c 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -364,50 +364,43 @@ template void OutputSection::maybeCompress() { // useful when there are many compressed output sections. addralign = 1; + // Split input into 1-MiB shards. + [[maybe_unused]] constexpr size_t shardSize = 1 << 20; + auto shardsIn = split(ArrayRef(buf.get(), size), shardSize); + const size_t numShards = shardsIn.size(); + compressed.numShards = numShards; + auto shardsOut = std::make_unique[]>(numShards); + #if LLVM_ENABLE_ZSTD // Use ZSTD's streaming compression API which permits parallel workers working // on the stream. See http://facebook.github.io/zstd/zstd_manual.html // "Streaming compression - HowTo". if (ctype == DebugCompressionType::Zstd) { - // Allocate a buffer of half of the input size, and grow it by 1.5x if - // insufficient. - compressed.type = ELFCOMPRESS_ZSTD; - compressed.shards = std::make_unique[]>(1); - SmallVector &out = compressed.shards[0]; - out.resize_for_overwrite(std::max(size / 2, 32)); - size_t pos = 0; - - ZSTD_CCtx *cctx = ZSTD_createCCtx(); - // Ignore error if zstd was not built with ZSTD_MULTITHREAD. - (void)ZSTD_CCtx_setParameter(cctx, ZSTD_c_nbWorkers, - parallel::strategy.compute_thread_count()); - ZSTD_outBuffer zob = {out.data(), out.size(), 0}; - ZSTD_EndDirective directive = ZSTD_e_continue; - const size_t blockSize = ZSTD_CStreamInSize(); - do { - const size_t n = std::min(static_cast(size - pos), blockSize); - if (n == size - pos) - directive = ZSTD_e_end; - ZSTD_inBuffer zib = {buf.get() + pos, n, 0}; - size_t bytesRemaining = 0; - while (zib.pos != zib.size || - (directive == ZSTD_e_end && bytesRemaining != 0)) { + parallelFor(0, numShards, [&](size_t i) { + SmallVector out; + ZSTD_CCtx *cctx = ZSTD_createCCtx(); + ZSTD_inBuffer zib = {shardsIn[i].data(), shardsIn[i].size(), 0}; + ZSTD_outBuffer zob = {nullptr, 0, 0}; + size_t size; + do { + // Allocate a buffer of half of the input size, and grow it by 1.5x if + // insufficient. if (zob.pos == zob.size) { - out.resize_for_overwrite(out.size() * 3 / 2); - zob.dst = out.data(); - zob.size = out.size(); + out.resize_for_overwrite( + zob.size ? zob.size * 3 / 2 : std::max(zib.size / 4, 64)); + zob = {out.data(), out.size(), zob.pos}; } - bytesRemaining = ZSTD_compressStream2(cctx, &zob, &zib, directive); - assert(!ZSTD_isError(bytesRemaining)); - } - pos += n; - } while (directive != ZSTD_e_end); - out.resize(zob.pos); - ZSTD_freeCCtx(cctx); - - size = sizeof(Elf_Chdr) + out.size(); - flags |= SHF_COMPRESSED; - return; + size = ZSTD_compressStream2(cctx, &zob, &zib, ZSTD_e_end); + assert(!ZSTD_isError(size)); + } while (size != 0); + out.truncate(zob.pos); + ZSTD_freeCCtx(cctx); + shardsOut[i] = std::move(out); + }); + compressed.type = ELFCOMPRESS_ZSTD; + size = sizeof(Elf_Chdr); + for (size_t i = 0; i != numShards; ++i) + size += shardsOut[i].size(); } #endif @@ -417,37 +410,32 @@ template void OutputSection::maybeCompress() { // ~15%. We found that level 7 to 9 doesn't make much difference (~1% more // compression) while they take significant amount of time (~2x), so level 6 // seems enough. - const int level = config->optimize >= 2 ? 6 : Z_BEST_SPEED; - - // Split input into 1-MiB shards. - constexpr size_t shardSize = 1 << 20; - auto shardsIn = split(ArrayRef(buf.get(), size), shardSize); - const size_t numShards = shardsIn.size(); - - // Compress shards and compute Alder-32 checksums. Use Z_SYNC_FLUSH for all - // shards but the last to flush the output to a byte boundary to be - // concatenated with the next shard. - auto shardsOut = std::make_unique[]>(numShards); - auto shardsAdler = std::make_unique(numShards); - parallelFor(0, numShards, [&](size_t i) { - shardsOut[i] = deflateShard(shardsIn[i], level, - i != numShards - 1 ? Z_SYNC_FLUSH : Z_FINISH); - shardsAdler[i] = adler32(1, shardsIn[i].data(), shardsIn[i].size()); - }); + if (ctype == DebugCompressionType::Zlib) { + const int level = config->optimize >= 2 ? 6 : Z_BEST_SPEED; + + // Compress shards and compute Alder-32 checksums. Use Z_SYNC_FLUSH for all + // shards but the last to flush the output to a byte boundary to be + // concatenated with the next shard. + auto shardsAdler = std::make_unique(numShards); + parallelFor(0, numShards, [&](size_t i) { + shardsOut[i] = deflateShard(shardsIn[i], level, + i != numShards - 1 ? Z_SYNC_FLUSH : Z_FINISH); + shardsAdler[i] = adler32(1, shardsIn[i].data(), shardsIn[i].size()); + }); - // Update section size and combine Alder-32 checksums. - uint32_t checksum = 1; // Initial Adler-32 value - size = sizeof(Elf_Chdr) + 2; // Elf_Chdir and zlib header - for (size_t i = 0; i != numShards; ++i) { - size += shardsOut[i].size(); - checksum = adler32_combine(checksum, shardsAdler[i], shardsIn[i].size()); + // Update section size and combine Alder-32 checksums. + uint32_t checksum = 1; // Initial Adler-32 value + size = sizeof(Elf_Chdr) + 2; // Elf_Chdir and zlib header + for (size_t i = 0; i != numShards; ++i) { + size += shardsOut[i].size(); + checksum = adler32_combine(checksum, shardsAdler[i], shardsIn[i].size()); + } + size += 4; // checksum + compressed.type = ELFCOMPRESS_ZLIB; + compressed.checksum = checksum; } - size += 4; // checksum - compressed.type = ELFCOMPRESS_ZLIB; compressed.shards = std::move(shardsOut); - compressed.numShards = numShards; - compressed.checksum = checksum; flags |= SHF_COMPRESSED; #endif } @@ -479,25 +467,22 @@ void OutputSection::writeTo(uint8_t *buf, parallel::TaskGroup &tg) { chdr->ch_size = compressed.uncompressedSize; chdr->ch_addralign = addralign; buf += sizeof(*chdr); - if (compressed.type == ELFCOMPRESS_ZSTD) { - memcpy(buf, compressed.shards[0].data(), compressed.shards[0].size()); - return; + + auto offsets = std::make_unique(compressed.numShards); + if (compressed.type == ELFCOMPRESS_ZLIB) { + buf[0] = 0x78; // CMF + buf[1] = 0x01; // FLG: best speed + offsets[0] = 2; // zlib header + write32be(buf + (size - sizeof(*chdr) - 4), compressed.checksum); } // Compute shard offsets. - auto offsets = std::make_unique(compressed.numShards); - offsets[0] = 2; // zlib header for (size_t i = 1; i != compressed.numShards; ++i) offsets[i] = offsets[i - 1] + compressed.shards[i - 1].size(); - - buf[0] = 0x78; // CMF - buf[1] = 0x01; // FLG: best speed parallelFor(0, compressed.numShards, [&](size_t i) { memcpy(buf + offsets[i], compressed.shards[i].data(), compressed.shards[i].size()); }); - - write32be(buf + (size - sizeof(*chdr) - 4), compressed.checksum); return; } From fbe4d991323b026eb64cd3d0ee811854b54ca33f Mon Sep 17 00:00:00 2001 From: Julian Schmidt Date: Tue, 30 Apr 2024 07:22:30 +0200 Subject: [PATCH 19/65] [clang-tidy] fix false-negative for macros in `readability-math-missing-parentheses` (#90279) When a binary operator is the last operand of a macro, the end location that is past the `BinaryOperator` will be inside the macro and therefore an invalid location to insert a `FixIt` into, which is why the check bails when encountering such a pattern. However, the end location is only required for the `FixIt` and the diagnostic can still be emitted, just without an attached fix. --- .../MathMissingParenthesesCheck.cpp | 16 ++++++++------ .../readability/math-missing-parentheses.cpp | 22 +++++++++++++++++++ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/clang-tools-extra/clang-tidy/readability/MathMissingParenthesesCheck.cpp b/clang-tools-extra/clang-tidy/readability/MathMissingParenthesesCheck.cpp index d1e20b9074cec1..65fd296094915b 100644 --- a/clang-tools-extra/clang-tidy/readability/MathMissingParenthesesCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/MathMissingParenthesesCheck.cpp @@ -61,19 +61,21 @@ static void addParantheses(const BinaryOperator *BinOp, const clang::SourceLocation StartLoc = BinOp->getBeginLoc(); const clang::SourceLocation EndLoc = clang::Lexer::getLocForEndOfToken(BinOp->getEndLoc(), 0, SM, LangOpts); - if (EndLoc.isInvalid()) - return; - Check->diag(StartLoc, - "'%0' has higher precedence than '%1'; add parentheses to " - "explicitly specify the order of operations") + auto Diag = + Check->diag(StartLoc, + "'%0' has higher precedence than '%1'; add parentheses to " + "explicitly specify the order of operations") << (Precedence1 > Precedence2 ? BinOp->getOpcodeStr() : ParentBinOp->getOpcodeStr()) << (Precedence1 > Precedence2 ? ParentBinOp->getOpcodeStr() : BinOp->getOpcodeStr()) - << FixItHint::CreateInsertion(StartLoc, "(") - << FixItHint::CreateInsertion(EndLoc, ")") << SourceRange(StartLoc, EndLoc); + + if (EndLoc.isValid()) { + Diag << FixItHint::CreateInsertion(StartLoc, "(") + << FixItHint::CreateInsertion(EndLoc, ")"); + } } addParantheses(dyn_cast(BinOp->getLHS()->IgnoreImpCasts()), diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/math-missing-parentheses.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/math-missing-parentheses.cpp index edbe2e1c37c770..a6045c079a4823 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability/math-missing-parentheses.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/math-missing-parentheses.cpp @@ -16,6 +16,13 @@ int bar(){ return 4; } +int sink(int); +#define FUN(ARG) (sink(ARG)) +#define FUN2(ARG) sink((ARG)) +#define FUN3(ARG) sink(ARG) +#define FUN4(ARG) sink(1 + ARG) +#define FUN5(ARG) sink(4 * ARG) + class fun{ public: int A; @@ -117,4 +124,19 @@ void f(){ //CHECK-MESSAGES: :[[@LINE+2]]:94: warning: '/' has higher precedence than '-'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] //CHECK-FIXES: int q = (1 MACRO_ADD (2 MACRO_MULTIPLY 3)) MACRO_OR ((4 MACRO_AND 5) MACRO_XOR (6 MACRO_SUBTRACT (7 MACRO_DIVIDE 8))); int q = 1 MACRO_ADD 2 MACRO_MULTIPLY 3 MACRO_OR 4 MACRO_AND 5 MACRO_XOR 6 MACRO_SUBTRACT 7 MACRO_DIVIDE 8; // No warning + + //CHECK-MESSAGES: :[[@LINE+1]]:21: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + int r = FUN(0 + 1 * 2); + + //CHECK-MESSAGES: :[[@LINE+1]]:22: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + int s = FUN2(0 + 1 * 2); + + //CHECK-MESSAGES: :[[@LINE+1]]:22: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + int t = FUN3(0 + 1 * 2); + + //CHECK-MESSAGES: :[[@LINE+1]]:18: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + int u = FUN4(1 * 2); + + //CHECK-MESSAGES: :[[@LINE+1]]:13: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + int v = FUN5(0 + 1); } From bd72f7b0ab98531ab579fafe79c7a8967994583a Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Tue, 30 Apr 2024 13:23:24 +0800 Subject: [PATCH 20/65] [RISCV] Add test case for exact vscale miscompile in #90559. NFC --- llvm/test/CodeGen/RISCV/rvv/pr90559.ll | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 llvm/test/CodeGen/RISCV/rvv/pr90559.ll diff --git a/llvm/test/CodeGen/RISCV/rvv/pr90559.ll b/llvm/test/CodeGen/RISCV/rvv/pr90559.ll new file mode 100644 index 00000000000000..93a251286cf73f --- /dev/null +++ b/llvm/test/CodeGen/RISCV/rvv/pr90559.ll @@ -0,0 +1,23 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 4 +; RUN: llc < %s -mtriple=riscv64 -mattr=+v | FileCheck %s + +; FIXME: The i32 load and store pair isn't dead and shouldn't be omitted. +define void @f(ptr %p) vscale_range(2,2) { +; CHECK-LABEL: f: +; CHECK: # %bb.0: +; CHECK-NEXT: vsetvli a1, zero, e8, m4, ta, ma +; CHECK-NEXT: vmv.v.i v8, 0 +; CHECK-NEXT: vs4r.v v8, (a0) +; CHECK-NEXT: addi a1, a0, 80 +; CHECK-NEXT: vsetivli zero, 16, e8, m1, ta, ma +; CHECK-NEXT: vmv.v.i v8, 0 +; CHECK-NEXT: vs1r.v v8, (a1) +; CHECK-NEXT: addi a0, a0, 64 +; CHECK-NEXT: vs1r.v v8, (a0) +; CHECK-NEXT: ret + %q = getelementptr inbounds i8, ptr %p, i64 84 + %x = load i32, ptr %q + call void @llvm.memset.p0.i64(ptr %p, i8 0, i64 96, i1 false) + store i32 %x, ptr %q + ret void +} From 18268ac0f48d93c2bcddb69732761971669c09ab Mon Sep 17 00:00:00 2001 From: Chuanqi Xu Date: Tue, 30 Apr 2024 13:28:52 +0800 Subject: [PATCH 21/65] [NFC] [C++20] [Modules] Use new class CXX20ModulesGenerator to generate module file for C++20 modules instead of PCHGenerator Previously we're re-using PCHGenerator to generate the module file for C++20 modules. But this is slighty more or less odd. This patch tries to use a new class 'CXX20ModulesGenerator' to generate the module file for C++20 modules. --- clang/include/clang/Serialization/ASTWriter.h | 19 ++++++++++++++++--- clang/lib/Frontend/FrontendActions.cpp | 12 ++++-------- clang/lib/Serialization/GeneratePCH.cpp | 19 ++++++++----------- clang/test/Modules/pr67893.cppm | 2 +- clang/test/Modules/search-partitions.cpp | 8 +++----- 5 files changed, 32 insertions(+), 28 deletions(-) diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h index 6c45b7348b8552..4e433deaaf2dbc 100644 --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -928,17 +928,30 @@ class PCHGenerator : public SemaConsumer { bool hasEmittedPCH() const { return Buffer->IsComplete; } }; -class ReducedBMIGenerator : public PCHGenerator { +class CXX20ModulesGenerator : public PCHGenerator { protected: virtual Module *getEmittingModule(ASTContext &Ctx) override; + CXX20ModulesGenerator(Preprocessor &PP, InMemoryModuleCache &ModuleCache, + StringRef OutputFile, bool GeneratingReducedBMI); + public: - ReducedBMIGenerator(Preprocessor &PP, InMemoryModuleCache &ModuleCache, - StringRef OutputFile); + CXX20ModulesGenerator(Preprocessor &PP, InMemoryModuleCache &ModuleCache, + StringRef OutputFile) + : CXX20ModulesGenerator(PP, ModuleCache, OutputFile, + /*GeneratingReducedBMI=*/false) {} void HandleTranslationUnit(ASTContext &Ctx) override; }; +class ReducedBMIGenerator : public CXX20ModulesGenerator { +public: + ReducedBMIGenerator(Preprocessor &PP, InMemoryModuleCache &ModuleCache, + StringRef OutputFile) + : CXX20ModulesGenerator(PP, ModuleCache, OutputFile, + /*GeneratingReducedBMI=*/true) {} +}; + /// If we can elide the definition of \param D in reduced BMI. /// /// Generally, we can elide the definition of a declaration if it won't affect diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index 04eb1041326713..454653a31534cd 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -272,14 +272,10 @@ bool GenerateModuleInterfaceAction::BeginSourceFileAction( std::unique_ptr GenerateModuleInterfaceAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { - CI.getHeaderSearchOpts().ModulesSkipDiagnosticOptions = true; - CI.getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = true; - CI.getHeaderSearchOpts().ModulesSkipPragmaDiagnosticMappings = true; - - std::vector> Consumers = - CreateMultiplexConsumer(CI, InFile); - if (Consumers.empty()) - return nullptr; + std::vector> Consumers; + Consumers.push_back(std::make_unique( + CI.getPreprocessor(), CI.getModuleCache(), + CI.getFrontendOpts().OutputFile)); if (CI.getFrontendOpts().GenReducedBMI && !CI.getFrontendOpts().ModuleOutputPath.empty()) { diff --git a/clang/lib/Serialization/GeneratePCH.cpp b/clang/lib/Serialization/GeneratePCH.cpp index bed74399098d7f..7b97b73f7bbd00 100644 --- a/clang/lib/Serialization/GeneratePCH.cpp +++ b/clang/lib/Serialization/GeneratePCH.cpp @@ -88,31 +88,28 @@ ASTDeserializationListener *PCHGenerator::GetASTDeserializationListener() { return &Writer; } -ReducedBMIGenerator::ReducedBMIGenerator(Preprocessor &PP, - InMemoryModuleCache &ModuleCache, - StringRef OutputFile) +CXX20ModulesGenerator::CXX20ModulesGenerator(Preprocessor &PP, + InMemoryModuleCache &ModuleCache, + StringRef OutputFile, + bool GeneratingReducedBMI) : PCHGenerator( PP, ModuleCache, OutputFile, llvm::StringRef(), std::make_shared(), /*Extensions=*/ArrayRef>(), /*AllowASTWithErrors*/ false, /*IncludeTimestamps=*/false, /*BuildingImplicitModule=*/false, /*ShouldCacheASTInMemory=*/false, - /*GeneratingReducedBMI=*/true) {} + GeneratingReducedBMI) {} -Module *ReducedBMIGenerator::getEmittingModule(ASTContext &Ctx) { +Module *CXX20ModulesGenerator::getEmittingModule(ASTContext &Ctx) { Module *M = Ctx.getCurrentNamedModule(); assert(M && M->isNamedModuleUnit() && - "ReducedBMIGenerator should only be used with C++20 Named modules."); + "CXX20ModulesGenerator should only be used with C++20 Named modules."); return M; } -void ReducedBMIGenerator::HandleTranslationUnit(ASTContext &Ctx) { - // We need to do this to make sure the size of reduced BMI not to be larger - // than full BMI. - // +void CXX20ModulesGenerator::HandleTranslationUnit(ASTContext &Ctx) { // FIMXE: We'd better to wrap such options to a new class ASTWriterOptions // since this is not about searching header really. - // FIXME2: We'd better to move the class writing full BMI with reduced BMI. HeaderSearchOptions &HSOpts = getPreprocessor().getHeaderSearchInfo().getHeaderSearchOpts(); HSOpts.ModulesSkipDiagnosticOptions = true; diff --git a/clang/test/Modules/pr67893.cppm b/clang/test/Modules/pr67893.cppm index 58990cec01d666..95479193f8ea2b 100644 --- a/clang/test/Modules/pr67893.cppm +++ b/clang/test/Modules/pr67893.cppm @@ -5,7 +5,7 @@ // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/a.cppm \ // RUN: -emit-module-interface -o %t/a.pcm // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/m.cppm \ -// RUN: -emit-module-interface -fprebuilt-module-path=%t +// RUN: -emit-module-interface -fprebuilt-module-path=%t -o %t/m.pcm // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/m.pcm \ // RUN: -fprebuilt-module-path=%t -S -emit-llvm -o - | FileCheck %t/m.cppm diff --git a/clang/test/Modules/search-partitions.cpp b/clang/test/Modules/search-partitions.cpp index 92732958db94e6..92f7c637c83384 100644 --- a/clang/test/Modules/search-partitions.cpp +++ b/clang/test/Modules/search-partitions.cpp @@ -11,7 +11,7 @@ // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/partition3.cpp \ // RUN: -o %t/A-Part3.pcm -// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/moduleA.cpp \ +// RUN: %clang_cc1 -std=c++20 %t/moduleA.cpp -fsyntax-only -verify \ // RUN: -fprebuilt-module-path=%t // Test again with reduced BMI @@ -28,9 +28,7 @@ // RUN: %clang_cc1 -std=c++20 -emit-reduced-module-interface %t/partition3.cpp \ // RUN: -o %t/A-Part3.pcm -// RUN: %clang_cc1 -std=c++20 -fsyntax-only %t/moduleA.cpp -fprebuilt-module-path=%t - -// expected-no-diagnostics +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %t/moduleA.cpp -fprebuilt-module-path=%t //--- partition1.cpp export module A:Part1; @@ -50,7 +48,7 @@ export module A:Part3; int part3(); //--- moduleA.cpp - +// expected-no-diagnostics export module A; import :Part1; From 705636a1130551ab105aec95b909a35a0305fc9f Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Mon, 29 Apr 2024 22:44:24 -0700 Subject: [PATCH 22/65] [SelectionDAG][RISCV] Move VP_REDUCE* legalization to LegalizeDAG.cpp. (#90522) LegalizeVectorType is responsible for legalizing nodes that perform an operation on each element may need to scalarize. This is not true for nodes like VP_REDUCE.*, BUILD_VECTOR, SHUFFLE_VECTOR, EXTRACT_SUBVECTOR, etc. This patch drops any nodes with a scalar result from LegalizeVectorOps and handles them in LegalizeDAG instead. This required moving the reduction promotion to LegalizeDAG. I have removed the support integer promotion as it was incorrect for integer min/max reductions. Since it was untested, it was best to assert on it until it was really needed. There are a couple regressions that can be fixed with a small DAG combine which I will do as a follow up. --- llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp | 62 +++++++++++++++- .../SelectionDAG/LegalizeVectorOps.cpp | 73 ++----------------- .../rvv/fixed-vectors-reduction-int-vp.ll | 26 ++++--- .../CodeGen/RISCV/rvv/vreductions-int-vp.ll | 7 +- 4 files changed, 85 insertions(+), 83 deletions(-) diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp index 46e54b5366d66a..398b5fee990b5d 100644 --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp @@ -180,6 +180,13 @@ class SelectionDAGLegalize { SmallVectorImpl &Results); SDValue PromoteLegalFP_TO_INT_SAT(SDNode *Node, const SDLoc &dl); + /// Implements vector reduce operation promotion. + /// + /// All vector operands are promoted to a vector type with larger element + /// type, and the start value is promoted to a larger scalar type. Then the + /// result is truncated back to the original scalar type. + void PromoteReduction(SDNode *Node, SmallVectorImpl &Results); + SDValue ExpandPARITY(SDValue Op, const SDLoc &dl); SDValue ExpandExtractFromVectorThroughStack(SDValue Op); @@ -2979,6 +2986,47 @@ SDValue SelectionDAGLegalize::ExpandPARITY(SDValue Op, const SDLoc &dl) { return DAG.getNode(ISD::AND, dl, VT, Result, DAG.getConstant(1, dl, VT)); } +void SelectionDAGLegalize::PromoteReduction(SDNode *Node, + SmallVectorImpl &Results) { + MVT VecVT = Node->getOperand(1).getSimpleValueType(); + MVT NewVecVT = TLI.getTypeToPromoteTo(Node->getOpcode(), VecVT); + MVT ScalarVT = Node->getSimpleValueType(0); + MVT NewScalarVT = NewVecVT.getVectorElementType(); + + SDLoc DL(Node); + SmallVector Operands(Node->getNumOperands()); + + // promote the initial value. + // FIXME: Support integer. + assert(Node->getOperand(0).getValueType().isFloatingPoint() && + "Only FP promotion is supported"); + Operands[0] = + DAG.getNode(ISD::FP_EXTEND, DL, NewScalarVT, Node->getOperand(0)); + + for (unsigned j = 1; j != Node->getNumOperands(); ++j) + if (Node->getOperand(j).getValueType().isVector() && + !(ISD::isVPOpcode(Node->getOpcode()) && + ISD::getVPMaskIdx(Node->getOpcode()) == j)) { // Skip mask operand. + // promote the vector operand. + // FIXME: Support integer. + assert(Node->getOperand(j).getValueType().isFloatingPoint() && + "Only FP promotion is supported"); + Operands[j] = + DAG.getNode(ISD::FP_EXTEND, DL, NewVecVT, Node->getOperand(j)); + } else { + Operands[j] = Node->getOperand(j); // Skip VL operand. + } + + SDValue Res = DAG.getNode(Node->getOpcode(), DL, NewScalarVT, Operands, + Node->getFlags()); + + assert(ScalarVT.isFloatingPoint() && "Only FP promotion is supported"); + Res = DAG.getNode(ISD::FP_ROUND, DL, ScalarVT, Res, + DAG.getIntPtrConstant(0, DL, /*isTarget=*/true)); + + Results.push_back(Res); +} + bool SelectionDAGLegalize::ExpandNode(SDNode *Node) { LLVM_DEBUG(dbgs() << "Trying to expand node\n"); SmallVector Results; @@ -4955,7 +5003,12 @@ void SelectionDAGLegalize::PromoteNode(SDNode *Node) { if (Node->getOpcode() == ISD::STRICT_UINT_TO_FP || Node->getOpcode() == ISD::STRICT_SINT_TO_FP || Node->getOpcode() == ISD::STRICT_FSETCC || - Node->getOpcode() == ISD::STRICT_FSETCCS) + Node->getOpcode() == ISD::STRICT_FSETCCS || + Node->getOpcode() == ISD::VP_REDUCE_FADD || + Node->getOpcode() == ISD::VP_REDUCE_FMUL || + Node->getOpcode() == ISD::VP_REDUCE_FMAX || + Node->getOpcode() == ISD::VP_REDUCE_FMIN || + Node->getOpcode() == ISD::VP_REDUCE_SEQ_FADD) OVT = Node->getOperand(1).getSimpleValueType(); if (Node->getOpcode() == ISD::BR_CC || Node->getOpcode() == ISD::SELECT_CC) @@ -5613,6 +5666,13 @@ void SelectionDAGLegalize::PromoteNode(SDNode *Node) { DAG.getIntPtrConstant(0, dl, /*isTarget=*/true))); break; } + case ISD::VP_REDUCE_FADD: + case ISD::VP_REDUCE_FMUL: + case ISD::VP_REDUCE_FMAX: + case ISD::VP_REDUCE_FMIN: + case ISD::VP_REDUCE_SEQ_FADD: + PromoteReduction(Node, Results); + break; } // Replace the original node with the legalized result. diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp index 8f87ee8e09393a..423df9ae6b2a55 100644 --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp @@ -176,13 +176,6 @@ class VectorLegalizer { /// truncated back to the original type. void PromoteFP_TO_INT(SDNode *Node, SmallVectorImpl &Results); - /// Implements vector reduce operation promotion. - /// - /// All vector operands are promoted to a vector type with larger element - /// type, and the start value is promoted to a larger scalar type. Then the - /// result is truncated back to the original scalar type. - void PromoteReduction(SDNode *Node, SmallVectorImpl &Results); - /// Implements vector setcc operation promotion. /// /// All vector operands are promoted to a vector type with larger element @@ -510,6 +503,11 @@ SDValue VectorLegalizer::LegalizeOp(SDValue Op) { if (Action != TargetLowering::Legal) \ break; \ } \ + /* Defer non-vector results to LegalizeDAG. */ \ + if (!Node->getValueType(0).isVector()) { \ + Action = TargetLowering::Legal; \ + break; \ + } \ Action = TLI.getOperationAction(Node->getOpcode(), LegalizeVT); \ } break; #include "llvm/IR/VPIntrinsics.def" @@ -580,50 +578,6 @@ bool VectorLegalizer::LowerOperationWrapper(SDNode *Node, return true; } -void VectorLegalizer::PromoteReduction(SDNode *Node, - SmallVectorImpl &Results) { - MVT VecVT = Node->getOperand(1).getSimpleValueType(); - MVT NewVecVT = TLI.getTypeToPromoteTo(Node->getOpcode(), VecVT); - MVT ScalarVT = Node->getSimpleValueType(0); - MVT NewScalarVT = NewVecVT.getVectorElementType(); - - SDLoc DL(Node); - SmallVector Operands(Node->getNumOperands()); - - // promote the initial value. - if (Node->getOperand(0).getValueType().isFloatingPoint()) - Operands[0] = - DAG.getNode(ISD::FP_EXTEND, DL, NewScalarVT, Node->getOperand(0)); - else - Operands[0] = - DAG.getNode(ISD::ANY_EXTEND, DL, NewScalarVT, Node->getOperand(0)); - - for (unsigned j = 1; j != Node->getNumOperands(); ++j) - if (Node->getOperand(j).getValueType().isVector() && - !(ISD::isVPOpcode(Node->getOpcode()) && - ISD::getVPMaskIdx(Node->getOpcode()) == j)) // Skip mask operand. - // promote the vector operand. - if (Node->getOperand(j).getValueType().isFloatingPoint()) - Operands[j] = - DAG.getNode(ISD::FP_EXTEND, DL, NewVecVT, Node->getOperand(j)); - else - Operands[j] = - DAG.getNode(ISD::ANY_EXTEND, DL, NewVecVT, Node->getOperand(j)); - else - Operands[j] = Node->getOperand(j); // Skip VL operand. - - SDValue Res = DAG.getNode(Node->getOpcode(), DL, NewScalarVT, Operands, - Node->getFlags()); - - if (ScalarVT.isFloatingPoint()) - Res = DAG.getNode(ISD::FP_ROUND, DL, ScalarVT, Res, - DAG.getIntPtrConstant(0, DL, /*isTarget=*/true)); - else - Res = DAG.getNode(ISD::TRUNCATE, DL, ScalarVT, Res); - - Results.push_back(Res); -} - void VectorLegalizer::PromoteSETCC(SDNode *Node, SmallVectorImpl &Results) { MVT VecVT = Node->getOperand(0).getSimpleValueType(); @@ -708,23 +662,6 @@ void VectorLegalizer::Promote(SDNode *Node, SmallVectorImpl &Results) { // Promote the operation by extending the operand. PromoteFP_TO_INT(Node, Results); return; - case ISD::VP_REDUCE_ADD: - case ISD::VP_REDUCE_MUL: - case ISD::VP_REDUCE_AND: - case ISD::VP_REDUCE_OR: - case ISD::VP_REDUCE_XOR: - case ISD::VP_REDUCE_SMAX: - case ISD::VP_REDUCE_SMIN: - case ISD::VP_REDUCE_UMAX: - case ISD::VP_REDUCE_UMIN: - case ISD::VP_REDUCE_FADD: - case ISD::VP_REDUCE_FMUL: - case ISD::VP_REDUCE_FMAX: - case ISD::VP_REDUCE_FMIN: - case ISD::VP_REDUCE_SEQ_FADD: - // Promote the operation by extending the operand. - PromoteReduction(Node, Results); - return; case ISD::VP_SETCC: case ISD::SETCC: // Promote the operation by extending the operand. diff --git a/llvm/test/CodeGen/RISCV/rvv/fixed-vectors-reduction-int-vp.ll b/llvm/test/CodeGen/RISCV/rvv/fixed-vectors-reduction-int-vp.ll index 02a989a9699606..b874a4477f5d17 100644 --- a/llvm/test/CodeGen/RISCV/rvv/fixed-vectors-reduction-int-vp.ll +++ b/llvm/test/CodeGen/RISCV/rvv/fixed-vectors-reduction-int-vp.ll @@ -802,25 +802,27 @@ define signext i32 @vpreduce_xor_v64i32(i32 signext %s, <64 x i32> %v, <64 x i1> ; CHECK-LABEL: vpreduce_xor_v64i32: ; CHECK: # %bb.0: ; CHECK-NEXT: vsetivli zero, 4, e8, mf2, ta, ma -; CHECK-NEXT: li a3, 32 ; CHECK-NEXT: vslidedown.vi v24, v0, 4 -; CHECK-NEXT: mv a2, a1 -; CHECK-NEXT: bltu a1, a3, .LBB49_2 +; CHECK-NEXT: addi a2, a1, -32 +; CHECK-NEXT: sltu a3, a1, a2 +; CHECK-NEXT: addi a3, a3, -1 +; CHECK-NEXT: li a4, 32 +; CHECK-NEXT: and a2, a3, a2 +; CHECK-NEXT: bltu a1, a4, .LBB49_2 ; CHECK-NEXT: # %bb.1: -; CHECK-NEXT: li a2, 32 +; CHECK-NEXT: li a1, 32 ; CHECK-NEXT: .LBB49_2: ; CHECK-NEXT: vsetvli zero, zero, e32, m2, ta, ma ; CHECK-NEXT: vmv.s.x v25, a0 -; CHECK-NEXT: vsetvli zero, a2, e32, m8, ta, ma +; CHECK-NEXT: vsetvli zero, a1, e32, m8, ta, ma ; CHECK-NEXT: vredxor.vs v25, v8, v25, v0.t -; CHECK-NEXT: addi a0, a1, -32 -; CHECK-NEXT: sltu a1, a1, a0 -; CHECK-NEXT: addi a1, a1, -1 -; CHECK-NEXT: and a0, a1, a0 -; CHECK-NEXT: vsetvli zero, a0, e32, m8, ta, ma -; CHECK-NEXT: vmv1r.v v0, v24 -; CHECK-NEXT: vredxor.vs v25, v16, v25, v0.t ; CHECK-NEXT: vmv.x.s a0, v25 +; CHECK-NEXT: vsetivli zero, 1, e32, m8, ta, ma +; CHECK-NEXT: vmv.s.x v8, a0 +; CHECK-NEXT: vsetvli zero, a2, e32, m8, ta, ma +; CHECK-NEXT: vmv1r.v v0, v24 +; CHECK-NEXT: vredxor.vs v8, v16, v8, v0.t +; CHECK-NEXT: vmv.x.s a0, v8 ; CHECK-NEXT: ret %r = call i32 @llvm.vp.reduce.xor.v64i32(i32 %s, <64 x i32> %v, <64 x i1> %m, i32 %evl) ret i32 %r diff --git a/llvm/test/CodeGen/RISCV/rvv/vreductions-int-vp.ll b/llvm/test/CodeGen/RISCV/rvv/vreductions-int-vp.ll index 7bcf37b1af3c8f..95b64cb662a614 100644 --- a/llvm/test/CodeGen/RISCV/rvv/vreductions-int-vp.ll +++ b/llvm/test/CodeGen/RISCV/rvv/vreductions-int-vp.ll @@ -1115,10 +1115,13 @@ define signext i32 @vpreduce_umax_nxv32i32(i32 signext %s, % ; CHECK-NEXT: vmv.s.x v25, a0 ; CHECK-NEXT: vsetvli zero, a1, e32, m8, ta, ma ; CHECK-NEXT: vredmaxu.vs v25, v8, v25, v0.t +; CHECK-NEXT: vmv.x.s a0, v25 +; CHECK-NEXT: vsetivli zero, 1, e32, m8, ta, ma +; CHECK-NEXT: vmv.s.x v8, a0 ; CHECK-NEXT: vsetvli zero, a2, e32, m8, ta, ma ; CHECK-NEXT: vmv1r.v v0, v24 -; CHECK-NEXT: vredmaxu.vs v25, v16, v25, v0.t -; CHECK-NEXT: vmv.x.s a0, v25 +; CHECK-NEXT: vredmaxu.vs v8, v16, v8, v0.t +; CHECK-NEXT: vmv.x.s a0, v8 ; CHECK-NEXT: ret %r = call i32 @llvm.vp.reduce.umax.nxv32i32(i32 %s, %v, %m, i32 %evl) ret i32 %r From 6e83058138210ab1e219d04974a071b57b3196e1 Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Mon, 29 Apr 2024 22:49:10 -0700 Subject: [PATCH 23/65] [RISCV] Use an assert insead of a if/else+llvm_unreachable. NFC --- llvm/lib/TargetParser/RISCVISAInfo.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/llvm/lib/TargetParser/RISCVISAInfo.cpp b/llvm/lib/TargetParser/RISCVISAInfo.cpp index 24b774314fea84..aec82a94cf97bc 100644 --- a/llvm/lib/TargetParser/RISCVISAInfo.cpp +++ b/llvm/lib/TargetParser/RISCVISAInfo.cpp @@ -667,11 +667,10 @@ RISCVISAInfo::parseArchString(StringRef Arch, bool EnableExperimentalExtension, // version since the we don't have clear version scheme for that on // ISA spec. for (const auto *Ext : RISCVGImplications) { - if (auto Version = findDefaultVersion(Ext)) { - // Postpone AddExtension until end of this function - SeenExtMap[Ext] = {Version->Major, Version->Minor}; - } else - llvm_unreachable("Default extension version not found?"); + auto Version = findDefaultVersion(Ext); + assert(Version && "Default extension version not found?"); + // Postpone AddExtension until end of this function + SeenExtMap[Ext] = {Version->Major, Version->Minor}; } } else { // Baseline is `i` or `e` From fb21343473e33e9a886b42d2fe95d1cec1cd0030 Mon Sep 17 00:00:00 2001 From: Chuanqi Xu Date: Tue, 30 Apr 2024 13:42:26 +0800 Subject: [PATCH 24/65] [C++20] [Modules] Don't skip pragma diagnostic mappings Close https://github.com/llvm/llvm-project/issues/75057 Previously, I thought the diagnostic mappings is not meaningful with modules incorrectly. And this problem get revealed by another change recently. So this patch tried to rever the previous "optimization" partially. --- clang/lib/Serialization/GeneratePCH.cpp | 1 - clang/test/Modules/pr75057.cppm | 62 +++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 clang/test/Modules/pr75057.cppm diff --git a/clang/lib/Serialization/GeneratePCH.cpp b/clang/lib/Serialization/GeneratePCH.cpp index 7b97b73f7bbd00..53dda5f9a38580 100644 --- a/clang/lib/Serialization/GeneratePCH.cpp +++ b/clang/lib/Serialization/GeneratePCH.cpp @@ -114,7 +114,6 @@ void CXX20ModulesGenerator::HandleTranslationUnit(ASTContext &Ctx) { getPreprocessor().getHeaderSearchInfo().getHeaderSearchOpts(); HSOpts.ModulesSkipDiagnosticOptions = true; HSOpts.ModulesSkipHeaderSearchPaths = true; - HSOpts.ModulesSkipPragmaDiagnosticMappings = true; PCHGenerator::HandleTranslationUnit(Ctx); diff --git a/clang/test/Modules/pr75057.cppm b/clang/test/Modules/pr75057.cppm new file mode 100644 index 00000000000000..374c324e9f495b --- /dev/null +++ b/clang/test/Modules/pr75057.cppm @@ -0,0 +1,62 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// Treat the behavior of using headers as baseline. +// RUN: %clang_cc1 -std=c++20 %t/use-header.cc -isystem %t -fsyntax-only -verify +// +// RUN: %clang_cc1 -std=c++20 %t/a.cppm -isystem %t -emit-module-interface -o %t/a.pcm +// RUN: %clang_cc1 -std=c++20 %t/use-module.cc -isystem %t -fmodule-file=a=%t/a.pcm -fsyntax-only -verify + +//--- sys.h +#ifndef SYS_H +#define SYS_H + +#pragma GCC system_header + +template +struct [[deprecated]] iterator {}; + +_Pragma("GCC diagnostic push") +_Pragma("GCC diagnostic ignored \"-Wdeprecated\"") +_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") + +template +struct reverse_iterator +: public iterator {}; + +_Pragma("GCC diagnostic pop") + +template +class C { +public: + void i() { + reverse_iterator i; + } +}; + +#endif + +//--- use-header.cc +// expected-no-diagnostics +// However, we see unexpected warnings +#include + +void use() { + C().i(); +} + +//--- a.cppm +module; +#include +export module a; +export using ::iterator; +export using ::C; + +//--- use-module.cc +// expected-no-diagnostics +import a; + +void use() { + C().i(); +} From 940ef9687f5f19ce02b7fa5d2eb6225f458fbdd9 Mon Sep 17 00:00:00 2001 From: Pengcheng Wang Date: Tue, 30 Apr 2024 14:14:16 +0800 Subject: [PATCH 25/65] [RISCV] Remove hasSideEffects=1 for saturating/fault-only-first instructions Marking them as `hasSideEffects=1` stops some optimizations. According to `Target.td`: > // Does the instruction have side effects that are not captured by any > // operands of the instruction or other flags? > bit hasSideEffects = ?; It seems we don't need to set `hasSideEffects` for vleNff since we have modelled `vl` as an output operand. As for saturating instructions, I think that explicit Def/Use list is kind of side effects captured by any operands of the instruction, so we don't need to set `hasSideEffects` either. And I have just investigated AArch64's implementation, they don't set this flag and don't add `Def` list. These changes make optimizations like `performCombineVMergeAndVOps` and MachineCSE possible for these instructions. As a consequence, `copyprop.mir` can't test what we want to test in https://reviews.llvm.org/D155140, so we replace `vssra.vi` with a VCIX instruction (it has side effects). Reviewers: jacquesguan, topperc, preames, asb, lukel97 Reviewed By: topperc, lukel97 Pull Request: https://github.com/llvm/llvm-project/pull/90049 --- llvm/include/llvm/IR/IntrinsicsRISCV.td | 13 ++-- llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp | 1 - .../Target/RISCV/RISCVInstrInfoVPseudos.td | 10 +-- ...regalloc-last-chance-recoloring-failure.ll | 69 ++++++++----------- llvm/test/CodeGen/RISCV/rvv/commutable.ll | 21 +++--- llvm/test/CodeGen/RISCV/rvv/copyprop.mir | 17 +++-- .../RISCV/rvv/rvv-peephole-vmerge-vops.ll | 32 ++++----- 7 files changed, 67 insertions(+), 96 deletions(-) diff --git a/llvm/include/llvm/IR/IntrinsicsRISCV.td b/llvm/include/llvm/IR/IntrinsicsRISCV.td index 1d58860a0afc8a..4c4e7351212f8a 100644 --- a/llvm/include/llvm/IR/IntrinsicsRISCV.td +++ b/llvm/include/llvm/IR/IntrinsicsRISCV.td @@ -661,7 +661,7 @@ let TargetPrefix = "riscv" in { : DefaultAttrsIntrinsic<[llvm_anyvector_ty], [LLVMMatchType<0>, LLVMMatchType<0>, llvm_any_ty, llvm_anyint_ty], - [IntrNoMem, IntrHasSideEffects]>, RISCVVIntrinsic { + [IntrNoMem]>, RISCVVIntrinsic { let ScalarOperand = 2; let VLOperand = 3; } @@ -684,7 +684,7 @@ let TargetPrefix = "riscv" in { [LLVMMatchType<0>, LLVMMatchType<0>, llvm_any_ty, LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>, llvm_anyint_ty, LLVMMatchType<2>], - [ImmArg>, IntrNoMem, IntrHasSideEffects]>, RISCVVIntrinsic { + [ImmArg>, IntrNoMem]>, RISCVVIntrinsic { let ScalarOperand = 2; let VLOperand = 4; } @@ -708,7 +708,7 @@ let TargetPrefix = "riscv" in { : DefaultAttrsIntrinsic<[llvm_anyvector_ty], [LLVMMatchType<0>, LLVMMatchType<0>, llvm_any_ty, llvm_anyint_ty, LLVMMatchType<2>], - [ImmArg>, IntrNoMem, IntrHasSideEffects]>, + [ImmArg>, IntrNoMem]>, RISCVVIntrinsic { let VLOperand = 4; } @@ -721,7 +721,7 @@ let TargetPrefix = "riscv" in { [LLVMMatchType<0>, LLVMMatchType<0>, llvm_any_ty, LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>, llvm_anyint_ty, LLVMMatchType<2>, LLVMMatchType<2>], - [ImmArg>,ImmArg>, IntrNoMem, IntrHasSideEffects]>, + [ImmArg>,ImmArg>, IntrNoMem]>, RISCVVIntrinsic { let VLOperand = 5; } @@ -733,7 +733,7 @@ let TargetPrefix = "riscv" in { : DefaultAttrsIntrinsic<[llvm_anyvector_ty], [LLVMMatchType<0>, llvm_anyvector_ty, llvm_any_ty, llvm_anyint_ty, LLVMMatchType<3>], - [ImmArg>, IntrNoMem, IntrHasSideEffects]>, + [ImmArg>, IntrNoMem]>, RISCVVIntrinsic { let VLOperand = 4; } @@ -746,8 +746,7 @@ let TargetPrefix = "riscv" in { [LLVMMatchType<0>, llvm_anyvector_ty, llvm_any_ty, LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>, llvm_anyint_ty, LLVMMatchType<3>, LLVMMatchType<3>], - [ImmArg>, ImmArg>, IntrNoMem, - IntrHasSideEffects]>, RISCVVIntrinsic { + [ImmArg>, ImmArg>, IntrNoMem]>, RISCVVIntrinsic { let VLOperand = 5; } // Input: (vector_in, vector_in, scalar_in, vl, policy) diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp index b0568297a470a7..da1543bd7112a2 100644 --- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp @@ -3668,7 +3668,6 @@ bool RISCVDAGToDAGISel::performCombineVMergeAndVOps(SDNode *N) { } // Skip if True has side effect. - // TODO: Support vleff and vlsegff. if (TII->get(TrueOpc).hasUnmodeledSideEffects()) return false; diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoVPseudos.td b/llvm/lib/Target/RISCV/RISCVInstrInfoVPseudos.td index fc60a9cc7cd30e..22e54886178467 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfoVPseudos.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoVPseudos.td @@ -6232,7 +6232,7 @@ defm PseudoVSUX : VPseudoIStore; //===----------------------------------------------------------------------===// // vleff may update VL register -let hasSideEffects = 1, Defs = [VL] in +let Defs = [VL] in defm PseudoVL : VPseudoFFLoad; //===----------------------------------------------------------------------===// @@ -6248,7 +6248,7 @@ defm PseudoVSOXSEG : VPseudoISegStore; defm PseudoVSUXSEG : VPseudoISegStore; // vlsegeff.v may update VL register -let hasSideEffects = 1, Defs = [VL] in { +let Defs = [VL] in { defm PseudoVLSEG : VPseudoUSSegLoadFF; } @@ -6450,7 +6450,7 @@ defm PseudoVMV_V : VPseudoUnaryVMV_V_X_I; //===----------------------------------------------------------------------===// // 12.1. Vector Single-Width Saturating Add and Subtract //===----------------------------------------------------------------------===// -let Defs = [VXSAT], hasSideEffects = 1 in { +let Defs = [VXSAT] in { defm PseudoVSADDU : VPseudoVSALU_VV_VX_VI; defm PseudoVSADD : VPseudoVSALU_VV_VX_VI; defm PseudoVSSUBU : VPseudoVSALU_VV_VX; @@ -6468,7 +6468,7 @@ defm PseudoVASUB : VPseudoVAALU_VV_VX_RM; //===----------------------------------------------------------------------===// // 12.3. Vector Single-Width Fractional Multiply with Rounding and Saturation //===----------------------------------------------------------------------===// -let Defs = [VXSAT], hasSideEffects = 1 in { +let Defs = [VXSAT] in { defm PseudoVSMUL : VPseudoVSMUL_VV_VX_RM; } @@ -6481,7 +6481,7 @@ defm PseudoVSSRA : VPseudoVSSHT_VV_VX_VI_RM; //===----------------------------------------------------------------------===// // 12.5. Vector Narrowing Fixed-Point Clip Instructions //===----------------------------------------------------------------------===// -let Defs = [VXSAT], hasSideEffects = 1 in { +let Defs = [VXSAT] in { defm PseudoVNCLIP : VPseudoVNCLP_WV_WX_WI_RM; defm PseudoVNCLIPU : VPseudoVNCLP_WV_WX_WI_RM; } diff --git a/llvm/test/CodeGen/RISCV/regalloc-last-chance-recoloring-failure.ll b/llvm/test/CodeGen/RISCV/regalloc-last-chance-recoloring-failure.ll index 3ce56318426ad2..81ef6072449e80 100644 --- a/llvm/test/CodeGen/RISCV/regalloc-last-chance-recoloring-failure.ll +++ b/llvm/test/CodeGen/RISCV/regalloc-last-chance-recoloring-failure.ll @@ -26,7 +26,10 @@ define void @last_chance_recoloring_failure() { ; CHECK-NEXT: li a0, 55 ; CHECK-NEXT: vsetvli zero, a0, e16, m4, ta, ma ; CHECK-NEXT: vloxseg2ei32.v v16, (a0), v8 -; CHECK-NEXT: addi a0, sp, 16 +; CHECK-NEXT: csrr a0, vlenb +; CHECK-NEXT: slli a0, a0, 3 +; CHECK-NEXT: add a0, sp, a0 +; CHECK-NEXT: addi a0, a0, 16 ; CHECK-NEXT: csrr a1, vlenb ; CHECK-NEXT: slli a1, a1, 2 ; CHECK-NEXT: vs4r.v v16, (a0) # Unknown-size Folded Spill @@ -37,37 +40,24 @@ define void @last_chance_recoloring_failure() { ; CHECK-NEXT: li s0, 36 ; CHECK-NEXT: vsetvli zero, s0, e16, m4, ta, ma ; CHECK-NEXT: vfwadd.vv v16, v8, v12, v0.t -; CHECK-NEXT: csrr a0, vlenb -; CHECK-NEXT: slli a0, a0, 3 -; CHECK-NEXT: add a0, sp, a0 -; CHECK-NEXT: addi a0, a0, 16 +; CHECK-NEXT: addi a0, sp, 16 ; CHECK-NEXT: vs8r.v v16, (a0) # Unknown-size Folded Spill ; CHECK-NEXT: call func -; CHECK-NEXT: li a0, 32 -; CHECK-NEXT: vsetvli zero, a0, e16, m4, ta, ma -; CHECK-NEXT: vrgather.vv v16, v8, v12, v0.t ; CHECK-NEXT: vsetvli zero, s0, e16, m4, ta, ma -; CHECK-NEXT: addi a1, sp, 16 -; CHECK-NEXT: csrr a2, vlenb -; CHECK-NEXT: slli a2, a2, 2 -; CHECK-NEXT: vl4r.v v20, (a1) # Unknown-size Folded Reload -; CHECK-NEXT: add a1, a1, a2 -; CHECK-NEXT: vl4r.v v24, (a1) # Unknown-size Folded Reload -; CHECK-NEXT: csrr a1, vlenb -; CHECK-NEXT: slli a1, a1, 3 -; CHECK-NEXT: add a1, sp, a1 -; CHECK-NEXT: addi a1, a1, 16 -; CHECK-NEXT: vl8r.v v0, (a1) # Unknown-size Folded Reload -; CHECK-NEXT: vfwsub.wv v8, v0, v20 -; CHECK-NEXT: vsetvli zero, a0, e16, m4, tu, mu -; CHECK-NEXT: vssubu.vv v16, v16, v8, v0.t -; CHECK-NEXT: vsetvli zero, s0, e32, m8, tu, mu ; CHECK-NEXT: csrr a0, vlenb ; CHECK-NEXT: slli a0, a0, 3 ; CHECK-NEXT: add a0, sp, a0 ; CHECK-NEXT: addi a0, a0, 16 -; CHECK-NEXT: vl8r.v v16, (a0) # Unknown-size Folded Reload -; CHECK-NEXT: vfdiv.vv v8, v16, v8, v0.t +; CHECK-NEXT: csrr a1, vlenb +; CHECK-NEXT: slli a1, a1, 2 +; CHECK-NEXT: vl4r.v v16, (a0) # Unknown-size Folded Reload +; CHECK-NEXT: add a0, a0, a1 +; CHECK-NEXT: vl4r.v v20, (a0) # Unknown-size Folded Reload +; CHECK-NEXT: addi a0, sp, 16 +; CHECK-NEXT: vl8r.v v24, (a0) # Unknown-size Folded Reload +; CHECK-NEXT: vfwsub.wv v8, v24, v16 +; CHECK-NEXT: vsetvli zero, zero, e32, m8, tu, mu +; CHECK-NEXT: vfdiv.vv v8, v24, v8, v0.t ; CHECK-NEXT: vse32.v v8, (a0) ; CHECK-NEXT: csrr a0, vlenb ; CHECK-NEXT: slli a0, a0, 4 @@ -109,25 +99,20 @@ define void @last_chance_recoloring_failure() { ; SUBREGLIVENESS-NEXT: addi a0, sp, 16 ; SUBREGLIVENESS-NEXT: vs8r.v v16, (a0) # Unknown-size Folded Spill ; SUBREGLIVENESS-NEXT: call func -; SUBREGLIVENESS-NEXT: li a0, 32 -; SUBREGLIVENESS-NEXT: vsetvli zero, a0, e16, m4, ta, ma -; SUBREGLIVENESS-NEXT: vrgather.vv v16, v8, v12, v0.t ; SUBREGLIVENESS-NEXT: vsetvli zero, s0, e16, m4, ta, ma +; SUBREGLIVENESS-NEXT: csrr a0, vlenb +; SUBREGLIVENESS-NEXT: slli a0, a0, 3 +; SUBREGLIVENESS-NEXT: add a0, sp, a0 +; SUBREGLIVENESS-NEXT: addi a0, a0, 16 ; SUBREGLIVENESS-NEXT: csrr a1, vlenb -; SUBREGLIVENESS-NEXT: slli a1, a1, 3 -; SUBREGLIVENESS-NEXT: add a1, sp, a1 -; SUBREGLIVENESS-NEXT: addi a1, a1, 16 -; SUBREGLIVENESS-NEXT: csrr a2, vlenb -; SUBREGLIVENESS-NEXT: slli a2, a2, 2 -; SUBREGLIVENESS-NEXT: vl4r.v v20, (a1) # Unknown-size Folded Reload -; SUBREGLIVENESS-NEXT: add a1, a1, a2 -; SUBREGLIVENESS-NEXT: vl4r.v v24, (a1) # Unknown-size Folded Reload -; SUBREGLIVENESS-NEXT: addi a1, sp, 16 -; SUBREGLIVENESS-NEXT: vl8r.v v24, (a1) # Unknown-size Folded Reload -; SUBREGLIVENESS-NEXT: vfwsub.wv v8, v24, v20 -; SUBREGLIVENESS-NEXT: vsetvli zero, a0, e16, m4, tu, mu -; SUBREGLIVENESS-NEXT: vssubu.vv v16, v16, v8, v0.t -; SUBREGLIVENESS-NEXT: vsetvli zero, s0, e32, m8, tu, mu +; SUBREGLIVENESS-NEXT: slli a1, a1, 2 +; SUBREGLIVENESS-NEXT: vl4r.v v16, (a0) # Unknown-size Folded Reload +; SUBREGLIVENESS-NEXT: add a0, a0, a1 +; SUBREGLIVENESS-NEXT: vl4r.v v20, (a0) # Unknown-size Folded Reload +; SUBREGLIVENESS-NEXT: addi a0, sp, 16 +; SUBREGLIVENESS-NEXT: vl8r.v v24, (a0) # Unknown-size Folded Reload +; SUBREGLIVENESS-NEXT: vfwsub.wv v8, v24, v16 +; SUBREGLIVENESS-NEXT: vsetvli zero, zero, e32, m8, tu, mu ; SUBREGLIVENESS-NEXT: vfdiv.vv v8, v24, v8, v0.t ; SUBREGLIVENESS-NEXT: vse32.v v8, (a0) ; SUBREGLIVENESS-NEXT: csrr a0, vlenb diff --git a/llvm/test/CodeGen/RISCV/rvv/commutable.ll b/llvm/test/CodeGen/RISCV/rvv/commutable.ll index 06a6327d3892b6..d94b529bac0175 100644 --- a/llvm/test/CodeGen/RISCV/rvv/commutable.ll +++ b/llvm/test/CodeGen/RISCV/rvv/commutable.ll @@ -655,10 +655,9 @@ define @commutable_vsadd_vv( %0, @llvm.riscv.vsadd.nxv1i64.nxv1i64( undef, %0, %1, iXLen %2) @@ -673,7 +672,7 @@ define @commutable_vsadd_vv_masked( %0, @commutable_vsaddu_vv( %0, @llvm.riscv.vsaddu.nxv1i64.nxv1i64( undef, %0, %1, iXLen %2) @@ -707,7 +705,7 @@ define @commutable_vsaddu_vv_masked( %0, @commutable_vsmul_vv( %0, @llvm.riscv.vsmul.nxv1i64.nxv1i64( undef, %0, %1, iXLen 0, iXLen %2) @@ -813,7 +810,7 @@ define @commutable_vsmul_vv_masked( %0, %passthru, ptr %p, , i64 } @llvm.riscv.vleff.nxv2i32(, ptr, i64) define @vpmerge_vleff( %passthru, ptr %p, %m, i32 zeroext %vl) { ; CHECK-LABEL: vpmerge_vleff: ; CHECK: # %bb.0: -; CHECK-NEXT: vsetvli zero, a1, e32, m1, ta, ma -; CHECK-NEXT: vle32ff.v v9, (a0) -; CHECK-NEXT: vsetvli zero, a1, e32, m1, tu, ma -; CHECK-NEXT: vmerge.vvm v8, v8, v9, v0 +; CHECK-NEXT: vsetvli zero, a1, e32, m1, tu, mu +; CHECK-NEXT: vle32ff.v v8, (a0), v0.t ; CHECK-NEXT: ret %1 = zext i32 %vl to i64 %a = call { , i64 } @llvm.riscv.vleff.nxv2i32( undef, ptr %p, i64 %1) @@ -634,14 +631,11 @@ define void @vpselect_vpload_store( %passthru, ptr %p, @vpselect_vleff( %passthru, ptr %p, %m, i32 zeroext %vl) { ; CHECK-LABEL: vpselect_vleff: ; CHECK: # %bb.0: -; CHECK-NEXT: vsetvli zero, a1, e32, m1, ta, ma -; CHECK-NEXT: vle32ff.v v9, (a0) -; CHECK-NEXT: vsetvli zero, a1, e32, m1, ta, ma -; CHECK-NEXT: vmerge.vvm v8, v8, v9, v0 +; CHECK-NEXT: vsetvli zero, a1, e32, m1, ta, mu +; CHECK-NEXT: vle32ff.v v8, (a0), v0.t ; CHECK-NEXT: ret %1 = zext i32 %vl to i64 %a = call { , i64 } @llvm.riscv.vleff.nxv2i32( undef, ptr %p, i64 %1) @@ -898,22 +892,20 @@ define @vpselect_trunc( %passthru, @llvm.riscv.vle.nxv32i16.i64( undef, ptr null, i64 1) From 6b961e2abfffd8b5a508b5958849b13b0feafa50 Mon Sep 17 00:00:00 2001 From: Chuanqi Xu Date: Tue, 30 Apr 2024 14:26:52 +0800 Subject: [PATCH 26/65] Revert "[C++20] [Modules] Don't skip pragma diagnostic mappings" and "[NFC] [C++20] [Modules] Use new class CXX20ModulesGenerator to generate module file for C++20 modules instead of PCHGenerator" This reverts commit fb21343473e33e9a886b42d2fe95d1cec1cd0030. and commit 18268ac0f48d93c2bcddb69732761971669c09ab. It looks like there are some problems about linking the compiler --- clang/include/clang/Serialization/ASTWriter.h | 19 +----- clang/lib/Frontend/FrontendActions.cpp | 12 ++-- clang/lib/Serialization/GeneratePCH.cpp | 20 +++--- clang/test/Modules/pr67893.cppm | 2 +- clang/test/Modules/pr75057.cppm | 62 ------------------- clang/test/Modules/search-partitions.cpp | 8 ++- 6 files changed, 29 insertions(+), 94 deletions(-) delete mode 100644 clang/test/Modules/pr75057.cppm diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h index 4e433deaaf2dbc..6c45b7348b8552 100644 --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -928,30 +928,17 @@ class PCHGenerator : public SemaConsumer { bool hasEmittedPCH() const { return Buffer->IsComplete; } }; -class CXX20ModulesGenerator : public PCHGenerator { +class ReducedBMIGenerator : public PCHGenerator { protected: virtual Module *getEmittingModule(ASTContext &Ctx) override; - CXX20ModulesGenerator(Preprocessor &PP, InMemoryModuleCache &ModuleCache, - StringRef OutputFile, bool GeneratingReducedBMI); - public: - CXX20ModulesGenerator(Preprocessor &PP, InMemoryModuleCache &ModuleCache, - StringRef OutputFile) - : CXX20ModulesGenerator(PP, ModuleCache, OutputFile, - /*GeneratingReducedBMI=*/false) {} + ReducedBMIGenerator(Preprocessor &PP, InMemoryModuleCache &ModuleCache, + StringRef OutputFile); void HandleTranslationUnit(ASTContext &Ctx) override; }; -class ReducedBMIGenerator : public CXX20ModulesGenerator { -public: - ReducedBMIGenerator(Preprocessor &PP, InMemoryModuleCache &ModuleCache, - StringRef OutputFile) - : CXX20ModulesGenerator(PP, ModuleCache, OutputFile, - /*GeneratingReducedBMI=*/true) {} -}; - /// If we can elide the definition of \param D in reduced BMI. /// /// Generally, we can elide the definition of a declaration if it won't affect diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index 454653a31534cd..04eb1041326713 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -272,10 +272,14 @@ bool GenerateModuleInterfaceAction::BeginSourceFileAction( std::unique_ptr GenerateModuleInterfaceAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { - std::vector> Consumers; - Consumers.push_back(std::make_unique( - CI.getPreprocessor(), CI.getModuleCache(), - CI.getFrontendOpts().OutputFile)); + CI.getHeaderSearchOpts().ModulesSkipDiagnosticOptions = true; + CI.getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = true; + CI.getHeaderSearchOpts().ModulesSkipPragmaDiagnosticMappings = true; + + std::vector> Consumers = + CreateMultiplexConsumer(CI, InFile); + if (Consumers.empty()) + return nullptr; if (CI.getFrontendOpts().GenReducedBMI && !CI.getFrontendOpts().ModuleOutputPath.empty()) { diff --git a/clang/lib/Serialization/GeneratePCH.cpp b/clang/lib/Serialization/GeneratePCH.cpp index 53dda5f9a38580..bed74399098d7f 100644 --- a/clang/lib/Serialization/GeneratePCH.cpp +++ b/clang/lib/Serialization/GeneratePCH.cpp @@ -88,32 +88,36 @@ ASTDeserializationListener *PCHGenerator::GetASTDeserializationListener() { return &Writer; } -CXX20ModulesGenerator::CXX20ModulesGenerator(Preprocessor &PP, - InMemoryModuleCache &ModuleCache, - StringRef OutputFile, - bool GeneratingReducedBMI) +ReducedBMIGenerator::ReducedBMIGenerator(Preprocessor &PP, + InMemoryModuleCache &ModuleCache, + StringRef OutputFile) : PCHGenerator( PP, ModuleCache, OutputFile, llvm::StringRef(), std::make_shared(), /*Extensions=*/ArrayRef>(), /*AllowASTWithErrors*/ false, /*IncludeTimestamps=*/false, /*BuildingImplicitModule=*/false, /*ShouldCacheASTInMemory=*/false, - GeneratingReducedBMI) {} + /*GeneratingReducedBMI=*/true) {} -Module *CXX20ModulesGenerator::getEmittingModule(ASTContext &Ctx) { +Module *ReducedBMIGenerator::getEmittingModule(ASTContext &Ctx) { Module *M = Ctx.getCurrentNamedModule(); assert(M && M->isNamedModuleUnit() && - "CXX20ModulesGenerator should only be used with C++20 Named modules."); + "ReducedBMIGenerator should only be used with C++20 Named modules."); return M; } -void CXX20ModulesGenerator::HandleTranslationUnit(ASTContext &Ctx) { +void ReducedBMIGenerator::HandleTranslationUnit(ASTContext &Ctx) { + // We need to do this to make sure the size of reduced BMI not to be larger + // than full BMI. + // // FIMXE: We'd better to wrap such options to a new class ASTWriterOptions // since this is not about searching header really. + // FIXME2: We'd better to move the class writing full BMI with reduced BMI. HeaderSearchOptions &HSOpts = getPreprocessor().getHeaderSearchInfo().getHeaderSearchOpts(); HSOpts.ModulesSkipDiagnosticOptions = true; HSOpts.ModulesSkipHeaderSearchPaths = true; + HSOpts.ModulesSkipPragmaDiagnosticMappings = true; PCHGenerator::HandleTranslationUnit(Ctx); diff --git a/clang/test/Modules/pr67893.cppm b/clang/test/Modules/pr67893.cppm index 95479193f8ea2b..58990cec01d666 100644 --- a/clang/test/Modules/pr67893.cppm +++ b/clang/test/Modules/pr67893.cppm @@ -5,7 +5,7 @@ // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/a.cppm \ // RUN: -emit-module-interface -o %t/a.pcm // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/m.cppm \ -// RUN: -emit-module-interface -fprebuilt-module-path=%t -o %t/m.pcm +// RUN: -emit-module-interface -fprebuilt-module-path=%t // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/m.pcm \ // RUN: -fprebuilt-module-path=%t -S -emit-llvm -o - | FileCheck %t/m.cppm diff --git a/clang/test/Modules/pr75057.cppm b/clang/test/Modules/pr75057.cppm deleted file mode 100644 index 374c324e9f495b..00000000000000 --- a/clang/test/Modules/pr75057.cppm +++ /dev/null @@ -1,62 +0,0 @@ -// RUN: rm -rf %t -// RUN: mkdir -p %t -// RUN: split-file %s %t -// -// Treat the behavior of using headers as baseline. -// RUN: %clang_cc1 -std=c++20 %t/use-header.cc -isystem %t -fsyntax-only -verify -// -// RUN: %clang_cc1 -std=c++20 %t/a.cppm -isystem %t -emit-module-interface -o %t/a.pcm -// RUN: %clang_cc1 -std=c++20 %t/use-module.cc -isystem %t -fmodule-file=a=%t/a.pcm -fsyntax-only -verify - -//--- sys.h -#ifndef SYS_H -#define SYS_H - -#pragma GCC system_header - -template -struct [[deprecated]] iterator {}; - -_Pragma("GCC diagnostic push") -_Pragma("GCC diagnostic ignored \"-Wdeprecated\"") -_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") - -template -struct reverse_iterator -: public iterator {}; - -_Pragma("GCC diagnostic pop") - -template -class C { -public: - void i() { - reverse_iterator i; - } -}; - -#endif - -//--- use-header.cc -// expected-no-diagnostics -// However, we see unexpected warnings -#include - -void use() { - C().i(); -} - -//--- a.cppm -module; -#include -export module a; -export using ::iterator; -export using ::C; - -//--- use-module.cc -// expected-no-diagnostics -import a; - -void use() { - C().i(); -} diff --git a/clang/test/Modules/search-partitions.cpp b/clang/test/Modules/search-partitions.cpp index 92f7c637c83384..92732958db94e6 100644 --- a/clang/test/Modules/search-partitions.cpp +++ b/clang/test/Modules/search-partitions.cpp @@ -11,7 +11,7 @@ // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/partition3.cpp \ // RUN: -o %t/A-Part3.pcm -// RUN: %clang_cc1 -std=c++20 %t/moduleA.cpp -fsyntax-only -verify \ +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/moduleA.cpp \ // RUN: -fprebuilt-module-path=%t // Test again with reduced BMI @@ -28,7 +28,9 @@ // RUN: %clang_cc1 -std=c++20 -emit-reduced-module-interface %t/partition3.cpp \ // RUN: -o %t/A-Part3.pcm -// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %t/moduleA.cpp -fprebuilt-module-path=%t +// RUN: %clang_cc1 -std=c++20 -fsyntax-only %t/moduleA.cpp -fprebuilt-module-path=%t + +// expected-no-diagnostics //--- partition1.cpp export module A:Part1; @@ -48,7 +50,7 @@ export module A:Part3; int part3(); //--- moduleA.cpp -// expected-no-diagnostics + export module A; import :Part1; From 2524146b256002bfc70b38008425291f5b3dd08c Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Mon, 29 Apr 2024 23:35:30 -0700 Subject: [PATCH 27/65] [RISCV] Add DAG combine for (vmv_s_x_vl (undef) (vmv_x_s X). (#90524) We can use the original vector as long as the type of X matches the result type of the vmv_s_x_vl. --- llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 4 +++ .../rvv/fixed-vector-i8-index-cornercase.ll | 17 ++++-------- .../rvv/fixed-vectors-reduction-int-vp.ll | 26 +++++++++---------- .../CodeGen/RISCV/rvv/vreductions-int-vp.ll | 7 ++--- 4 files changed, 23 insertions(+), 31 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index 68f4ec5ef49f31..fe8edcf39681db 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -16791,6 +16791,10 @@ SDValue RISCVTargetLowering::PerformDAGCombine(SDNode *N, SDValue Scalar = N->getOperand(1); SDValue VL = N->getOperand(2); + if (Scalar.getOpcode() == RISCVISD::VMV_X_S && Passthru.isUndef() && + Scalar.getOperand(0).getValueType() == N->getValueType(0)) + return Scalar.getOperand(0); + // Use M1 or smaller to avoid over constraining register allocation const MVT M1VT = getLMUL1VT(VT); if (M1VT.bitsLT(VT)) { diff --git a/llvm/test/CodeGen/RISCV/rvv/fixed-vector-i8-index-cornercase.ll b/llvm/test/CodeGen/RISCV/rvv/fixed-vector-i8-index-cornercase.ll index 2874db6debd740..875f4f239028b4 100644 --- a/llvm/test/CodeGen/RISCV/rvv/fixed-vector-i8-index-cornercase.ll +++ b/llvm/test/CodeGen/RISCV/rvv/fixed-vector-i8-index-cornercase.ll @@ -26,17 +26,13 @@ define <512 x i8> @single_source(<512 x i8> %a) { ; CHECK-NEXT: vmv.v.x v8, a1 ; CHECK-NEXT: vslide1down.vx v8, v8, a0 ; CHECK-NEXT: vsetivli zero, 1, e8, m1, ta, ma -; CHECK-NEXT: vslidedown.vi v17, v16, 5 -; CHECK-NEXT: vmv.x.s a0, v17 -; CHECK-NEXT: vmv.s.x v24, a0 +; CHECK-NEXT: vslidedown.vi v24, v16, 5 ; CHECK-NEXT: li a0, 432 ; CHECK-NEXT: li a1, 431 ; CHECK-NEXT: vsetvli zero, a0, e8, m8, tu, ma ; CHECK-NEXT: vslideup.vx v8, v24, a1 ; CHECK-NEXT: vsetivli zero, 1, e8, m1, ta, ma ; CHECK-NEXT: vslidedown.vi v16, v16, 4 -; CHECK-NEXT: vmv.x.s a0, v16 -; CHECK-NEXT: vmv.s.x v16, a0 ; CHECK-NEXT: li a0, 466 ; CHECK-NEXT: li a1, 465 ; CHECK-NEXT: vsetvli zero, a0, e8, m8, tu, ma @@ -109,20 +105,17 @@ define <512 x i8> @two_source(<512 x i8> %a, <512 x i8> %b) { ; CHECK-NEXT: addi a1, sp, 512 ; CHECK-NEXT: vsetvli zero, a0, e8, m8, ta, ma ; CHECK-NEXT: vse8.v v8, (a1) +; CHECK-NEXT: vsetivli zero, 1, e8, m1, ta, ma +; CHECK-NEXT: vslidedown.vi v0, v24, 5 ; CHECK-NEXT: vmv.x.s a1, v24 +; CHECK-NEXT: vsetvli zero, a0, e8, m8, ta, ma ; CHECK-NEXT: vmv.v.x v8, a1 -; CHECK-NEXT: vsetivli zero, 1, e8, m1, ta, ma -; CHECK-NEXT: vslidedown.vi v25, v24, 5 -; CHECK-NEXT: vmv.x.s a1, v25 -; CHECK-NEXT: vmv.s.x v0, a1 ; CHECK-NEXT: li a1, 432 ; CHECK-NEXT: li a2, 431 ; CHECK-NEXT: vsetvli zero, a1, e8, m8, tu, ma ; CHECK-NEXT: vslideup.vx v8, v0, a2 ; CHECK-NEXT: vsetivli zero, 1, e8, m1, ta, ma ; CHECK-NEXT: vslidedown.vi v24, v24, 4 -; CHECK-NEXT: vmv.x.s a1, v24 -; CHECK-NEXT: vmv.s.x v24, a1 ; CHECK-NEXT: li a1, 466 ; CHECK-NEXT: li a2, 465 ; CHECK-NEXT: vsetvli zero, a1, e8, m8, tu, ma @@ -130,9 +123,9 @@ define <512 x i8> @two_source(<512 x i8> %a, <512 x i8> %b) { ; CHECK-NEXT: vslideup.vx v8, v24, a2 ; CHECK-NEXT: vmv.s.x v24, a1 ; CHECK-NEXT: li a1, 478 +; CHECK-NEXT: li a2, 477 ; CHECK-NEXT: vsetvli zero, a1, e8, m8, tu, ma ; CHECK-NEXT: lbu a1, 1012(sp) -; CHECK-NEXT: li a2, 477 ; CHECK-NEXT: vslideup.vx v8, v24, a2 ; CHECK-NEXT: vmv.s.x v24, a1 ; CHECK-NEXT: li a1, 501 diff --git a/llvm/test/CodeGen/RISCV/rvv/fixed-vectors-reduction-int-vp.ll b/llvm/test/CodeGen/RISCV/rvv/fixed-vectors-reduction-int-vp.ll index b874a4477f5d17..02a989a9699606 100644 --- a/llvm/test/CodeGen/RISCV/rvv/fixed-vectors-reduction-int-vp.ll +++ b/llvm/test/CodeGen/RISCV/rvv/fixed-vectors-reduction-int-vp.ll @@ -802,27 +802,25 @@ define signext i32 @vpreduce_xor_v64i32(i32 signext %s, <64 x i32> %v, <64 x i1> ; CHECK-LABEL: vpreduce_xor_v64i32: ; CHECK: # %bb.0: ; CHECK-NEXT: vsetivli zero, 4, e8, mf2, ta, ma +; CHECK-NEXT: li a3, 32 ; CHECK-NEXT: vslidedown.vi v24, v0, 4 -; CHECK-NEXT: addi a2, a1, -32 -; CHECK-NEXT: sltu a3, a1, a2 -; CHECK-NEXT: addi a3, a3, -1 -; CHECK-NEXT: li a4, 32 -; CHECK-NEXT: and a2, a3, a2 -; CHECK-NEXT: bltu a1, a4, .LBB49_2 +; CHECK-NEXT: mv a2, a1 +; CHECK-NEXT: bltu a1, a3, .LBB49_2 ; CHECK-NEXT: # %bb.1: -; CHECK-NEXT: li a1, 32 +; CHECK-NEXT: li a2, 32 ; CHECK-NEXT: .LBB49_2: ; CHECK-NEXT: vsetvli zero, zero, e32, m2, ta, ma ; CHECK-NEXT: vmv.s.x v25, a0 -; CHECK-NEXT: vsetvli zero, a1, e32, m8, ta, ma -; CHECK-NEXT: vredxor.vs v25, v8, v25, v0.t -; CHECK-NEXT: vmv.x.s a0, v25 -; CHECK-NEXT: vsetivli zero, 1, e32, m8, ta, ma -; CHECK-NEXT: vmv.s.x v8, a0 ; CHECK-NEXT: vsetvli zero, a2, e32, m8, ta, ma +; CHECK-NEXT: vredxor.vs v25, v8, v25, v0.t +; CHECK-NEXT: addi a0, a1, -32 +; CHECK-NEXT: sltu a1, a1, a0 +; CHECK-NEXT: addi a1, a1, -1 +; CHECK-NEXT: and a0, a1, a0 +; CHECK-NEXT: vsetvli zero, a0, e32, m8, ta, ma ; CHECK-NEXT: vmv1r.v v0, v24 -; CHECK-NEXT: vredxor.vs v8, v16, v8, v0.t -; CHECK-NEXT: vmv.x.s a0, v8 +; CHECK-NEXT: vredxor.vs v25, v16, v25, v0.t +; CHECK-NEXT: vmv.x.s a0, v25 ; CHECK-NEXT: ret %r = call i32 @llvm.vp.reduce.xor.v64i32(i32 %s, <64 x i32> %v, <64 x i1> %m, i32 %evl) ret i32 %r diff --git a/llvm/test/CodeGen/RISCV/rvv/vreductions-int-vp.ll b/llvm/test/CodeGen/RISCV/rvv/vreductions-int-vp.ll index 95b64cb662a614..7bcf37b1af3c8f 100644 --- a/llvm/test/CodeGen/RISCV/rvv/vreductions-int-vp.ll +++ b/llvm/test/CodeGen/RISCV/rvv/vreductions-int-vp.ll @@ -1115,13 +1115,10 @@ define signext i32 @vpreduce_umax_nxv32i32(i32 signext %s, % ; CHECK-NEXT: vmv.s.x v25, a0 ; CHECK-NEXT: vsetvli zero, a1, e32, m8, ta, ma ; CHECK-NEXT: vredmaxu.vs v25, v8, v25, v0.t -; CHECK-NEXT: vmv.x.s a0, v25 -; CHECK-NEXT: vsetivli zero, 1, e32, m8, ta, ma -; CHECK-NEXT: vmv.s.x v8, a0 ; CHECK-NEXT: vsetvli zero, a2, e32, m8, ta, ma ; CHECK-NEXT: vmv1r.v v0, v24 -; CHECK-NEXT: vredmaxu.vs v8, v16, v8, v0.t -; CHECK-NEXT: vmv.x.s a0, v8 +; CHECK-NEXT: vredmaxu.vs v25, v16, v25, v0.t +; CHECK-NEXT: vmv.x.s a0, v25 ; CHECK-NEXT: ret %r = call i32 @llvm.vp.reduce.umax.nxv32i32(i32 %s, %v, %m, i32 %evl) ret i32 %r From 4a84d8e4c28d873eacfce53f9fd902d67a08a859 Mon Sep 17 00:00:00 2001 From: wanglei Date: Tue, 30 Apr 2024 14:38:30 +0800 Subject: [PATCH 28/65] [LoongArch] Support parsing la.tls.desc pseudo instruction Simultaneously implemented parsing support for the `%desc_*` modifiers. Reviewers: SixWeining, heiher, xen0n Reviewed By: xen0n, SixWeining Pull Request: https://github.com/llvm/llvm-project/pull/90158 --- .../AsmParser/LoongArchAsmParser.cpp | 170 +++++++++++++++++- .../Target/LoongArch/LoongArchInstrInfo.td | 21 +++ .../MCTargetDesc/LoongArchFixupKinds.h | 22 +++ .../MCTargetDesc/LoongArchMCCodeEmitter.cpp | 30 ++++ .../MCTargetDesc/LoongArchMCExpr.cpp | 32 ++++ .../LoongArch/MCTargetDesc/LoongArchMCExpr.h | 10 ++ llvm/test/MC/LoongArch/Macros/macros-la-bad.s | 3 + llvm/test/MC/LoongArch/Macros/macros-la.s | 45 +++++ llvm/test/MC/LoongArch/Misc/tls-symbols.s | 22 +++ .../MC/LoongArch/Relocations/relocations.s | 50 ++++++ 10 files changed, 398 insertions(+), 7 deletions(-) diff --git a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp index 20284b18428bd8..73cb80245bca4d 100644 --- a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp +++ b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp @@ -118,6 +118,13 @@ class LoongArchAsmParser : public MCTargetAsmParser { // Helper to emit pseudo instruction "la.tls.gd $rd, $rj, sym". void emitLoadAddressTLSGDLarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); + // Helper to emit pseudo instruction "la.tls.desc $rd, sym". + void emitLoadAddressTLSDescAbs(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); + void emitLoadAddressTLSDescPcrel(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); + // Helper to emit pseudo instruction "la.tls.desc $rd, $rj, sym". + void emitLoadAddressTLSDescPcrelLarge(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out); + // Helper to emit pseudo instruction "li.w/d $rd, $imm". void emitLoadImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); @@ -132,6 +139,7 @@ class LoongArchAsmParser : public MCTargetAsmParser { Match_RequiresOpnd2NotR0R1, Match_RequiresAMORdDifferRkRj, Match_RequiresLAORdDifferRj, + Match_RequiresLAORdR4, #define GET_OPERAND_DIAGNOSTIC_TYPES #include "LoongArchGenAsmMatcher.inc" #undef GET_OPERAND_DIAGNOSTIC_TYPES @@ -267,7 +275,9 @@ class LoongArchOperand : public MCParsedAsmOperand { bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_None || VK == LoongArchMCExpr::VK_LoongArch_PCALA_LO12 || VK == LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12 || - VK == LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_LO12; + VK == LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_LO12 || + VK == LoongArchMCExpr::VK_LoongArch_TLS_DESC_PC_LO12 || + VK == LoongArchMCExpr::VK_LoongArch_TLS_DESC_LD; return IsConstantImm ? isInt<12>(Imm) && IsValidKind : LoongArchAsmParser::classifySymbolRef(getImm(), VK) && @@ -288,7 +298,9 @@ class LoongArchOperand : public MCParsedAsmOperand { VK == LoongArchMCExpr::VK_LoongArch_GOT64_PC_HI12 || VK == LoongArchMCExpr::VK_LoongArch_TLS_LE64_HI12 || VK == LoongArchMCExpr::VK_LoongArch_TLS_IE64_HI12 || - VK == LoongArchMCExpr::VK_LoongArch_TLS_IE64_PC_HI12; + VK == LoongArchMCExpr::VK_LoongArch_TLS_IE64_PC_HI12 || + VK == LoongArchMCExpr::VK_LoongArch_TLS_DESC64_HI12 || + VK == LoongArchMCExpr::VK_LoongArch_TLS_DESC64_PC_HI12; return IsConstantImm ? isInt<12>(Imm) && IsValidKind : LoongArchAsmParser::classifySymbolRef(getImm(), VK) && @@ -311,7 +323,8 @@ class LoongArchOperand : public MCParsedAsmOperand { VK == LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12 || VK == LoongArchMCExpr::VK_LoongArch_TLS_LE_LO12 || VK == LoongArchMCExpr::VK_LoongArch_TLS_IE_LO12 || - VK == LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_LO12; + VK == LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_LO12 || + VK == LoongArchMCExpr::VK_LoongArch_TLS_DESC_LO12; return IsConstantImm ? isUInt<12>(Imm) && IsValidKind : LoongArchAsmParser::classifySymbolRef(getImm(), VK) && @@ -334,7 +347,8 @@ class LoongArchOperand : public MCParsedAsmOperand { bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_None || VK == LoongArchMCExpr::VK_LoongArch_B16 || - VK == LoongArchMCExpr::VK_LoongArch_PCALA_LO12; + VK == LoongArchMCExpr::VK_LoongArch_PCALA_LO12 || + VK == LoongArchMCExpr::VK_LoongArch_TLS_DESC_CALL; return IsConstantImm ? isShiftedInt<16, 2>(Imm) && IsValidKind : LoongArchAsmParser::classifySymbolRef(getImm(), VK) && @@ -355,7 +369,8 @@ class LoongArchOperand : public MCParsedAsmOperand { VK == LoongArchMCExpr::VK_LoongArch_GOT_PC_HI20 || VK == LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_HI20 || VK == LoongArchMCExpr::VK_LoongArch_TLS_LD_PC_HI20 || - VK == LoongArchMCExpr::VK_LoongArch_TLS_GD_PC_HI20; + VK == LoongArchMCExpr::VK_LoongArch_TLS_GD_PC_HI20 || + VK == LoongArchMCExpr::VK_LoongArch_TLS_DESC_PC_HI20; return IsConstantImm ? isInt<20>(Imm) && IsValidKind : LoongArchAsmParser::classifySymbolRef(getImm(), VK) && @@ -375,7 +390,8 @@ class LoongArchOperand : public MCParsedAsmOperand { VK == LoongArchMCExpr::VK_LoongArch_TLS_GD_HI20 || VK == LoongArchMCExpr::VK_LoongArch_TLS_LD_HI20 || VK == LoongArchMCExpr::VK_LoongArch_TLS_IE_HI20 || - VK == LoongArchMCExpr::VK_LoongArch_TLS_LE_HI20; + VK == LoongArchMCExpr::VK_LoongArch_TLS_LE_HI20 || + VK == LoongArchMCExpr::VK_LoongArch_TLS_DESC_HI20; return IsConstantImm ? isInt<20>(Imm) && IsValidKind : LoongArchAsmParser::classifySymbolRef(getImm(), VK) && @@ -396,7 +412,9 @@ class LoongArchOperand : public MCParsedAsmOperand { VK == LoongArchMCExpr::VK_LoongArch_GOT64_PC_LO20 || VK == LoongArchMCExpr::VK_LoongArch_TLS_IE64_LO20 || VK == LoongArchMCExpr::VK_LoongArch_TLS_IE64_PC_LO20 || - VK == LoongArchMCExpr::VK_LoongArch_TLS_LE64_LO20; + VK == LoongArchMCExpr::VK_LoongArch_TLS_LE64_LO20 || + VK == LoongArchMCExpr::VK_LoongArch_TLS_DESC64_PC_LO20 || + VK == LoongArchMCExpr::VK_LoongArch_TLS_DESC64_LO20; return IsConstantImm ? isInt<20>(Imm) && IsValidKind @@ -801,6 +819,13 @@ void LoongArchAsmParser::emitLAInstSeq(MCRegister DestReg, MCRegister TmpReg, MCInstBuilder(Opc).addReg(DestReg).addReg(DestReg).addImm(0), getSTI()); continue; + } else if (VK == LoongArchMCExpr::VK_LoongArch_TLS_DESC_LD) { + Out.emitInstruction(MCInstBuilder(Opc) + .addReg(LoongArch::R1) + .addReg(DestReg) + .addExpr(LE), + getSTI()); + continue; } Out.emitInstruction( MCInstBuilder(Opc).addReg(DestReg).addReg(DestReg).addExpr(LE), @@ -833,6 +858,13 @@ void LoongArchAsmParser::emitLAInstSeq(MCRegister DestReg, MCRegister TmpReg, MCInstBuilder(Opc).addReg(DestReg).addReg(DestReg).addReg(TmpReg), getSTI()); break; + case LoongArch::JIRL: + Out.emitInstruction(MCInstBuilder(Opc) + .addReg(LoongArch::R1) + .addReg(LoongArch::R1) + .addExpr(LE), + getSTI()); + break; } } } @@ -1116,6 +1148,109 @@ void LoongArchAsmParser::emitLoadAddressTLSGDLarge(MCInst &Inst, SMLoc IDLoc, emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out); } +void LoongArchAsmParser::emitLoadAddressTLSDescAbs(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out) { + // `la.tls.desc $rd, sym` with `la-global-with-abs` feature + // for la32 expands to: + // lu12i.w $rd, %desc_hi20(sym) + // ori $rd, $rd, %desc_lo12(sym) + // ld.w $ra, $rd, %desc_ld(sym) + // jirl $ra, $ra, %desc_call(sym) + // + // for la64 expands to: + // lu12i.w $rd, %desc_hi20(sym) + // ori $rd, $rd, %desc_lo12(sym) + // lu32i.d $rd, %desc64_lo20(sym) + // lu52i.d $rd, $rd, %desc64_hi12(sym) + // ld.d $ra, $rd, %desc_ld(sym) + // jirl $ra, $ra, %desc_call(sym) + MCRegister DestReg = Inst.getOperand(0).getReg(); + const MCExpr *Symbol = Inst.getOpcode() == LoongArch::PseudoLA_TLS_DESC_ABS + ? Inst.getOperand(1).getExpr() + : Inst.getOperand(2).getExpr(); + unsigned LD = is64Bit() ? LoongArch::LD_D : LoongArch::LD_W; + InstSeq Insts; + + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::LU12I_W, LoongArchMCExpr::VK_LoongArch_TLS_DESC_HI20)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::ORI, LoongArchMCExpr::VK_LoongArch_TLS_DESC_LO12)); + + if (is64Bit()) { + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_TLS_DESC64_LO20)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_TLS_DESC64_HI12)); + } + + Insts.push_back( + LoongArchAsmParser::Inst(LD, LoongArchMCExpr::VK_LoongArch_TLS_DESC_LD)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::JIRL, LoongArchMCExpr::VK_LoongArch_TLS_DESC_CALL)); + + emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out); +} + +void LoongArchAsmParser::emitLoadAddressTLSDescPcrel(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out) { + // la.tls.desc $rd, sym + // expands to: + // pcalau12i $rd, %desc_pc_hi20(sym) + // addi.w/d $rd, $rd, %desc_pc_lo12(sym) + // ld.w/d $ra, $rd, %desc_ld(sym) + // jirl $ra, $ra, %desc_call(sym) + MCRegister DestReg = Inst.getOperand(0).getReg(); + const MCExpr *Symbol = Inst.getOperand(1).getExpr(); + unsigned ADDI = is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W; + unsigned LD = is64Bit() ? LoongArch::LD_D : LoongArch::LD_W; + InstSeq Insts; + + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_DESC_PC_HI20)); + Insts.push_back(LoongArchAsmParser::Inst( + ADDI, LoongArchMCExpr::VK_LoongArch_TLS_DESC_PC_LO12)); + Insts.push_back( + LoongArchAsmParser::Inst(LD, LoongArchMCExpr::VK_LoongArch_TLS_DESC_LD)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::JIRL, LoongArchMCExpr::VK_LoongArch_TLS_DESC_CALL)); + + emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out); +} + +void LoongArchAsmParser::emitLoadAddressTLSDescPcrelLarge(MCInst &Inst, + SMLoc IDLoc, + MCStreamer &Out) { + // la.tls.desc $rd, $rj, sym + // expands to: + // pcalau12i $rd, %desc_pc_hi20(sym) + // addi.d $rj, $r0, %desc_pc_lo12(sym) + // lu32i.d $rj, %desc64_pc_lo20(sym) + // lu52i.d $rj, $rj, %desc64_pc_hi12(sym) + // add.d $rd, $rd, $rj + // ld.w/d $ra, $rd, %desc_ld(sym) + // jirl $ra, $ra, %desc_call(sym) + MCRegister DestReg = Inst.getOperand(0).getReg(); + MCRegister TmpReg = Inst.getOperand(1).getReg(); + const MCExpr *Symbol = Inst.getOperand(2).getExpr(); + InstSeq Insts; + + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_DESC_PC_HI20)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::ADDI_D, LoongArchMCExpr::VK_LoongArch_TLS_DESC_PC_LO12)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_TLS_DESC64_PC_LO20)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_TLS_DESC64_PC_HI12)); + Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ADD_D)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::LD_D, LoongArchMCExpr::VK_LoongArch_TLS_DESC_LD)); + Insts.push_back(LoongArchAsmParser::Inst( + LoongArch::JIRL, LoongArchMCExpr::VK_LoongArch_TLS_DESC_CALL)); + + emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out); +} + void LoongArchAsmParser::emitLoadImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out) { MCRegister DestReg = Inst.getOperand(0).getReg(); @@ -1211,6 +1346,16 @@ bool LoongArchAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc, case LoongArch::PseudoLA_TLS_GD_LARGE: emitLoadAddressTLSGDLarge(Inst, IDLoc, Out); return false; + case LoongArch::PseudoLA_TLS_DESC_ABS: + case LoongArch::PseudoLA_TLS_DESC_ABS_LARGE: + emitLoadAddressTLSDescAbs(Inst, IDLoc, Out); + return false; + case LoongArch::PseudoLA_TLS_DESC_PC: + emitLoadAddressTLSDescPcrel(Inst, IDLoc, Out); + return false; + case LoongArch::PseudoLA_TLS_DESC_PC_LARGE: + emitLoadAddressTLSDescPcrelLarge(Inst, IDLoc, Out); + return false; case LoongArch::PseudoLI_W: case LoongArch::PseudoLI_D: emitLoadImm(Inst, IDLoc, Out); @@ -1238,6 +1383,15 @@ unsigned LoongArchAsmParser::checkTargetMatchPredicate(MCInst &Inst) { return Match_RequiresAMORdDifferRkRj; } break; + case LoongArch::PseudoLA_TLS_DESC_ABS: + case LoongArch::PseudoLA_TLS_DESC_ABS_LARGE: + case LoongArch::PseudoLA_TLS_DESC_PC: + case LoongArch::PseudoLA_TLS_DESC_PC_LARGE: { + unsigned Rd = Inst.getOperand(0).getReg(); + if (Rd != LoongArch::R4) + return Match_RequiresLAORdR4; + break; + } case LoongArch::PseudoLA_PCREL_LARGE: case LoongArch::PseudoLA_GOT_LARGE: case LoongArch::PseudoLA_TLS_IE_LARGE: @@ -1376,6 +1530,8 @@ bool LoongArchAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, "$rd must be different from both $rk and $rj"); case Match_RequiresLAORdDifferRj: return Error(Operands[1]->getStartLoc(), "$rd must be different from $rj"); + case Match_RequiresLAORdR4: + return Error(Operands[1]->getStartLoc(), "$rd must be $r4"); case Match_InvalidUImm1: return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0, /*Upper=*/(1 << 1) - 1); diff --git a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td index 80429bc45be14c..958803b52d4ec8 100644 --- a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td +++ b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td @@ -1607,6 +1607,27 @@ def PseudoLA_TLS_GD_LARGE : Pseudo<(outs GPR:$dst), } // Defs = [R20], Size = 20 } +// TLSDESC +let hasSideEffects = 0, mayLoad = 1, mayStore = 0, isCodeGenOnly = 0, + isAsmParserOnly = 1, Defs = [R1] in { +def PseudoLA_TLS_DESC_ABS : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), + [], "la.tls.desc", "$dst, $src">, + Requires<[IsLA32, HasLaGlobalWithAbs]>; +def PseudoLA_TLS_DESC_ABS_LARGE : Pseudo<(outs GPR:$dst), + (ins GPR:$tmp, bare_symbol:$src), [], + "la.tls.desc", "$dst, $src">, + Requires<[IsLA64, HasLaGlobalWithAbs]>; +def PseudoLA_TLS_DESC_PC : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [], + "la.tls.desc", "$dst, $src">; +} + +let isCall = 1, isBarrier = 1, hasSideEffects = 0, mayStore = 0, mayLoad = 0, + isCodeGenOnly = 0, isAsmParserOnly = 1, Defs = [R1, R4, R20], Size = 32 in +def PseudoLA_TLS_DESC_PC_LARGE : Pseudo<(outs GPR:$dst), + (ins GPR:$tmp, bare_symbol:$src), [], + "la.tls.desc", "$dst, $tmp, $src">, + Requires<[IsLA64]>; + // Load address inst alias: "la", "la.global" and "la.local". // Default: // la = la.global = la.got diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h index 0d19d2b0fb1fe8..fb0587bf3bed7c 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h @@ -114,6 +114,28 @@ enum Fixups { // 36-bit fixup corresponding to %call36(foo) for a pair instructions: // pcaddu18i+jirl. fixup_loongarch_call36 = FirstLiteralRelocationKind + ELF::R_LARCH_CALL36, + // 20-bit fixup corresponding to %desc_pc_hi20(foo) for instruction pcalau12i. + fixup_loongarch_tls_desc_pc_hi20 = + FirstLiteralRelocationKind + ELF::R_LARCH_TLS_DESC_PC_HI20, + // 12-bit fixup corresponding to %desc_pc_lo12(foo) for instructions like + // addi.w/d. + fixup_loongarch_tls_desc_pc_lo12, + // 20-bit fixup corresponding to %desc64_pc_lo20(foo) for instruction lu32i.d. + fixup_loongarch_tls_desc64_pc_lo20, + // 12-bit fixup corresponding to %desc64_pc_hi12(foo) for instruction lu52i.d. + fixup_loongarch_tls_desc64_pc_hi12, + // 20-bit fixup corresponding to %desc_hi20(foo) for instruction lu12i.w. + fixup_loongarch_tls_desc_hi20, + // 12-bit fixup corresponding to %desc_lo12(foo) for instruction ori. + fixup_loongarch_tls_desc_lo12, + // 20-bit fixup corresponding to %desc64_lo20(foo) for instruction lu32i.d. + fixup_loongarch_tls_desc64_lo20, + // 12-bit fixup corresponding to %desc64_hi12(foo) for instruction lu52i.d. + fixup_loongarch_tls_desc64_hi12, + // 12-bit fixup corresponding to %desc_ld(foo) for instruction ld.w/d. + fixup_loongarch_tls_desc_ld, + // 12-bit fixup corresponding to %desc_call(foo) for instruction jirl. + fixup_loongarch_tls_desc_call, }; } // end namespace LoongArch } // end namespace llvm diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp index 9ac0128f251728..83812dc3c62a8b 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp @@ -244,6 +244,36 @@ LoongArchMCCodeEmitter::getExprOpValue(const MCInst &MI, const MCOperand &MO, case LoongArchMCExpr::VK_LoongArch_CALL36: FixupKind = LoongArch::fixup_loongarch_call36; break; + case LoongArchMCExpr::VK_LoongArch_TLS_DESC_PC_HI20: + FixupKind = LoongArch::fixup_loongarch_tls_desc_pc_hi20; + break; + case LoongArchMCExpr::VK_LoongArch_TLS_DESC_PC_LO12: + FixupKind = LoongArch::fixup_loongarch_tls_desc_pc_lo12; + break; + case LoongArchMCExpr::VK_LoongArch_TLS_DESC64_PC_LO20: + FixupKind = LoongArch::fixup_loongarch_tls_desc64_pc_lo20; + break; + case LoongArchMCExpr::VK_LoongArch_TLS_DESC64_PC_HI12: + FixupKind = LoongArch::fixup_loongarch_tls_desc64_pc_hi12; + break; + case LoongArchMCExpr::VK_LoongArch_TLS_DESC_HI20: + FixupKind = LoongArch::fixup_loongarch_tls_desc_hi20; + break; + case LoongArchMCExpr::VK_LoongArch_TLS_DESC_LO12: + FixupKind = LoongArch::fixup_loongarch_tls_desc_lo12; + break; + case LoongArchMCExpr::VK_LoongArch_TLS_DESC64_LO20: + FixupKind = LoongArch::fixup_loongarch_tls_desc64_lo20; + break; + case LoongArchMCExpr::VK_LoongArch_TLS_DESC64_HI12: + FixupKind = LoongArch::fixup_loongarch_tls_desc64_hi12; + break; + case LoongArchMCExpr::VK_LoongArch_TLS_DESC_LD: + FixupKind = LoongArch::fixup_loongarch_tls_desc_ld; + break; + case LoongArchMCExpr::VK_LoongArch_TLS_DESC_CALL: + FixupKind = LoongArch::fixup_loongarch_tls_desc_call; + break; } } else if (Kind == MCExpr::SymbolRef && cast(Expr)->getKind() == diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.cpp index d6fa3b6e50968c..34f9bc65ec77c3 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.cpp +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.cpp @@ -140,6 +140,26 @@ StringRef LoongArchMCExpr::getVariantKindName(VariantKind Kind) { return "gd_hi20"; case VK_LoongArch_CALL36: return "call36"; + case VK_LoongArch_TLS_DESC_PC_HI20: + return "desc_pc_hi20"; + case VK_LoongArch_TLS_DESC_PC_LO12: + return "desc_pc_lo12"; + case VK_LoongArch_TLS_DESC64_PC_LO20: + return "desc64_pc_lo20"; + case VK_LoongArch_TLS_DESC64_PC_HI12: + return "desc64_pc_hi12"; + case VK_LoongArch_TLS_DESC_HI20: + return "desc_hi20"; + case VK_LoongArch_TLS_DESC_LO12: + return "desc_lo12"; + case VK_LoongArch_TLS_DESC64_LO20: + return "desc64_lo20"; + case VK_LoongArch_TLS_DESC64_HI12: + return "desc64_hi12"; + case VK_LoongArch_TLS_DESC_LD: + return "desc_ld"; + case VK_LoongArch_TLS_DESC_CALL: + return "desc_call"; } } @@ -183,6 +203,16 @@ LoongArchMCExpr::getVariantKindForName(StringRef name) { .Case("gd_pc_hi20", VK_LoongArch_TLS_GD_PC_HI20) .Case("gd_hi20", VK_LoongArch_TLS_GD_HI20) .Case("call36", VK_LoongArch_CALL36) + .Case("desc_pc_hi20", VK_LoongArch_TLS_DESC_PC_HI20) + .Case("desc_pc_lo12", VK_LoongArch_TLS_DESC_PC_LO12) + .Case("desc64_pc_lo20", VK_LoongArch_TLS_DESC64_PC_LO20) + .Case("desc64_pc_hi12", VK_LoongArch_TLS_DESC64_PC_HI12) + .Case("desc_hi20", VK_LoongArch_TLS_DESC_HI20) + .Case("desc_lo12", VK_LoongArch_TLS_DESC_LO12) + .Case("desc64_lo20", VK_LoongArch_TLS_DESC64_LO20) + .Case("desc64_hi12", VK_LoongArch_TLS_DESC64_HI12) + .Case("desc_ld", VK_LoongArch_TLS_DESC_LD) + .Case("desc_call", VK_LoongArch_TLS_DESC_CALL) .Default(VK_LoongArch_Invalid); } @@ -223,6 +253,8 @@ void LoongArchMCExpr::fixELFSymbolsInTLSFixups(MCAssembler &Asm) const { case VK_LoongArch_TLS_LD_HI20: case VK_LoongArch_TLS_GD_PC_HI20: case VK_LoongArch_TLS_GD_HI20: + case VK_LoongArch_TLS_DESC_PC_HI20: + case VK_LoongArch_TLS_DESC_HI20: break; } fixELFSymbolsInTLSFixupsImpl(getSubExpr(), Asm); diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.h index bd828116d7fa46..71dd5bd14e4ee1 100644 --- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.h +++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.h @@ -62,6 +62,16 @@ class LoongArchMCExpr : public MCTargetExpr { VK_LoongArch_TLS_GD_PC_HI20, VK_LoongArch_TLS_GD_HI20, VK_LoongArch_CALL36, + VK_LoongArch_TLS_DESC_PC_HI20, + VK_LoongArch_TLS_DESC_PC_LO12, + VK_LoongArch_TLS_DESC64_PC_LO20, + VK_LoongArch_TLS_DESC64_PC_HI12, + VK_LoongArch_TLS_DESC_HI20, + VK_LoongArch_TLS_DESC_LO12, + VK_LoongArch_TLS_DESC64_LO20, + VK_LoongArch_TLS_DESC64_HI12, + VK_LoongArch_TLS_DESC_LD, + VK_LoongArch_TLS_DESC_CALL, VK_LoongArch_Invalid // Must be the last item. }; diff --git a/llvm/test/MC/LoongArch/Macros/macros-la-bad.s b/llvm/test/MC/LoongArch/Macros/macros-la-bad.s index 03c6355e40b092..29c9745e4ad864 100644 --- a/llvm/test/MC/LoongArch/Macros/macros-la-bad.s +++ b/llvm/test/MC/LoongArch/Macros/macros-la-bad.s @@ -11,3 +11,6 @@ la.abs $a0, $a1, sym la.pcrel $a0, $a0, sym # CHECK: :[[#@LINE-1]]:11: error: $rd must be different from $rj + +la.tls.desc $a1, sym +# CHECK: :[[#@LINE-1]]:14: error: $rd must be $r4 diff --git a/llvm/test/MC/LoongArch/Macros/macros-la.s b/llvm/test/MC/LoongArch/Macros/macros-la.s index 1a1d12d7d7dfd1..5c572c8e75a0f6 100644 --- a/llvm/test/MC/LoongArch/Macros/macros-la.s +++ b/llvm/test/MC/LoongArch/Macros/macros-la.s @@ -3,6 +3,8 @@ # RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=RELOC # RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax %s -o %t.relax # RUN: llvm-readobj -r %t.relax | FileCheck %s --check-prefixes=RELOC,RELAX +# RUN: llvm-mc --triple=loongarch64 --defsym ABS=1 --mattr=+la-global-with-abs \ +# RUN: %s | FileCheck %s --check-prefix=ABS # RELOC: Relocations [ # RELOC-NEXT: Section ({{.*}}) .rela.text { @@ -124,5 +126,48 @@ la.tls.gd $a0, $a1, sym_gd_large # RELOC-NEXT: R_LARCH_GOT64_PC_LO20 sym_gd_large 0x0 # RELOC-NEXT: R_LARCH_GOT64_PC_HI12 sym_gd_large 0x0 +la.tls.desc $a0, sym_desc +# CHECK-NEXT: pcalau12i $a0, %desc_pc_hi20(sym_desc) +# CHECK-NEXT: addi.d $a0, $a0, %desc_pc_lo12(sym_desc) +# CHECK-NEXT: ld.d $ra, $a0, %desc_ld(sym_desc) +# CHECK-NEXT: jirl $ra, $ra, %desc_call(sym_desc) +# CHECK-EMPTY: +# RELOC-NEXT: R_LARCH_TLS_DESC_PC_HI20 sym_desc 0x0 +# RELOC-NEXT: R_LARCH_TLS_DESC_PC_LO12 sym_desc 0x0 +# RELOC-NEXT: R_LARCH_TLS_DESC_LD sym_desc 0x0 +# RELOC-NEXT: R_LARCH_TLS_DESC_CALL sym_desc 0x0 + +la.tls.desc $a0, $a1, sym_desc_large +# CHECK-NEXT: pcalau12i $a0, %desc_pc_hi20(sym_desc_large) +# CHECK-NEXT: addi.d $a1, $zero, %desc_pc_lo12(sym_desc_large) +# CHECK-NEXT: lu32i.d $a1, %desc64_pc_lo20(sym_desc_large) +# CHECK-NEXT: lu52i.d $a1, $a1, %desc64_pc_hi12(sym_desc_large) +# CHECK-NEXT: add.d $a0, $a0, $a1 +# CHECK-NEXT: ld.d $ra, $a0, %desc_ld(sym_desc_large) +# CHECK-NEXT: jirl $ra, $ra, %desc_call(sym_desc_large) +# CHECK-EMPTY: +# RELOC-NEXT: R_LARCH_TLS_DESC_PC_HI20 sym_desc_large 0x0 +# RELOC-NEXT: R_LARCH_TLS_DESC_PC_LO12 sym_desc_large 0x0 +# RELOC-NEXT: R_LARCH_TLS_DESC64_PC_LO20 sym_desc_large 0x0 +# RELOC-NEXT: R_LARCH_TLS_DESC64_PC_HI12 sym_desc_large 0x0 +# RELOC-NEXT: R_LARCH_TLS_DESC_LD sym_desc_large 0x0 +# RELOC-NEXT: R_LARCH_TLS_DESC_CALL sym_desc_large 0x0 + + # RELOC-NEXT: } # RELOC-NEXT: ] + +############################################################# +## with feature: +la-global-with-abs +############################################################# +.ifdef ABS + +la.tls.desc $a0, sym_desc +# ABS: lu12i.w $a0, %desc_hi20(sym_desc) +# ABS-NEXT: ori $a0, $a0, %desc_lo12(sym_desc) +# ABS-NEXT: lu32i.d $a0, %desc64_lo20(sym_desc) +# ABS-NEXT: lu52i.d $a0, $a0, %desc64_hi12(sym_desc) +# ABS-NEXT: ld.d $ra, $a0, %desc_ld(sym_desc) +# ABS-NEXT: jirl $ra, $ra, %desc_call(sym_desc) + +.endif diff --git a/llvm/test/MC/LoongArch/Misc/tls-symbols.s b/llvm/test/MC/LoongArch/Misc/tls-symbols.s index 2f91cbe004d27d..340fea29ed94a3 100644 --- a/llvm/test/MC/LoongArch/Misc/tls-symbols.s +++ b/llvm/test/MC/LoongArch/Misc/tls-symbols.s @@ -77,3 +77,25 @@ lu12i.w $a1, %le_hi20(le) # CHECK-NEXT: Other: 0 # CHECK-NEXT: Section: Undefined # CHECK-NEXT: } + +pcalau12i $a1, %desc_pc_hi20(desc_pc) +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: desc_pc +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: TLS +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined +# CHECK-NEXT: } + +lu12i.w $a1, %desc_hi20(desc_abs) +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: desc_abs +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: TLS +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined +# CHECK-NEXT: } diff --git a/llvm/test/MC/LoongArch/Relocations/relocations.s b/llvm/test/MC/LoongArch/Relocations/relocations.s index bec71e10389333..87df59978c6ea3 100644 --- a/llvm/test/MC/LoongArch/Relocations/relocations.s +++ b/llvm/test/MC/LoongArch/Relocations/relocations.s @@ -223,3 +223,53 @@ pcaddu18i $t1, %call36(foo) # RELOC: R_LARCH_CALL36 foo 0x0 # INSTR: pcaddu18i $t1, %call36(foo) # FIXUP: fixup A - offset: 0, value: %call36(foo), kind: FK_NONE + +pcalau12i $t1, %desc_pc_hi20(foo) +# RELOC: R_LARCH_TLS_DESC_PC_HI20 foo 0x0 +# INSTR: pcalau12i $t1, %desc_pc_hi20(foo) +# FIXUP: fixup A - offset: 0, value: %desc_pc_hi20(foo), kind: FK_NONE + +addi.d $t1, $t1, %desc_pc_lo12(foo) +# RELOC: R_LARCH_TLS_DESC_PC_LO12 foo 0x0 +# INSTR: addi.d $t1, $t1, %desc_pc_lo12(foo) +# FIXUP: fixup A - offset: 0, value: %desc_pc_lo12(foo), kind: FK_NONE + +lu32i.d $t1, %desc64_pc_lo20(foo) +# RELOC: R_LARCH_TLS_DESC64_PC_LO20 foo 0x0 +# INSTR: lu32i.d $t1, %desc64_pc_lo20(foo) +# FIXUP: fixup A - offset: 0, value: %desc64_pc_lo20(foo), kind: FK_NONE + +lu52i.d $t1, $t1, %desc64_pc_hi12(foo) +# RELOC: R_LARCH_TLS_DESC64_PC_HI12 foo 0x0 +# INSTR: lu52i.d $t1, $t1, %desc64_pc_hi12(foo) +# FIXUP: fixup A - offset: 0, value: %desc64_pc_hi12(foo), kind: FK_NONE + +ld.d $ra, $t1, %desc_ld(foo) +# RELOC: R_LARCH_TLS_DESC_LD foo 0x0 +# INSTR: ld.d $ra, $t1, %desc_ld(foo) +# FIXUP: fixup A - offset: 0, value: %desc_ld(foo), kind: FK_NONE + +jirl $ra, $ra, %desc_call(foo) +# RELOC: R_LARCH_TLS_DESC_CALL foo 0x0 +# INSTR: jirl $ra, $ra, %desc_call(foo) +# FIXUP: fixup A - offset: 0, value: %desc_call(foo), kind: FK_NONE + +lu12i.w $t1, %desc_hi20(foo) +# RELOC: R_LARCH_TLS_DESC_HI20 foo 0x0 +# INSTR: lu12i.w $t1, %desc_hi20(foo) +# FIXUP: fixup A - offset: 0, value: %desc_hi20(foo), kind: FK_NONE + +ori $t1, $t1, %desc_lo12(foo) +# RELOC: R_LARCH_TLS_DESC_LO12 foo 0x0 +# INSTR: ori $t1, $t1, %desc_lo12(foo) +# FIXUP: fixup A - offset: 0, value: %desc_lo12(foo), kind: FK_NONE + +lu32i.d $t1, %desc64_lo20(foo) +# RELOC: R_LARCH_TLS_DESC64_LO20 foo 0x0 +# INSTR: lu32i.d $t1, %desc64_lo20(foo) +# FIXUP: fixup A - offset: 0, value: %desc64_lo20(foo), kind: FK_NONE + +lu52i.d $t1, $t1, %desc64_hi12(foo) +# RELOC: R_LARCH_TLS_DESC64_HI12 foo 0x0 +# INSTR: lu52i.d $t1, $t1, %desc64_hi12(foo) +# FIXUP: fixup A - offset: 0, value: %desc64_hi12(foo), kind: FK_NONE From ec527b21bb4196355184aa95ef31aa561b8e8b7b Mon Sep 17 00:00:00 2001 From: Chuanqi Xu Date: Tue, 30 Apr 2024 13:42:26 +0800 Subject: [PATCH 29/65] [C++20] [Modules] Don't skip pragma diagnostic mappings Close https://github.com/llvm/llvm-project/issues/75057 Previously, I thought the diagnostic mappings is not meaningful with modules incorrectly. And this problem get revealed by another change recently. So this patch tried to rever the previous "optimization" partially. --- clang/lib/Frontend/FrontendActions.cpp | 1 - clang/lib/Serialization/GeneratePCH.cpp | 1 - clang/test/Modules/pr75057.cppm | 66 +++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 clang/test/Modules/pr75057.cppm diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index 04eb1041326713..480dfa8c975933 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -274,7 +274,6 @@ GenerateModuleInterfaceAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { CI.getHeaderSearchOpts().ModulesSkipDiagnosticOptions = true; CI.getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = true; - CI.getHeaderSearchOpts().ModulesSkipPragmaDiagnosticMappings = true; std::vector> Consumers = CreateMultiplexConsumer(CI, InFile); diff --git a/clang/lib/Serialization/GeneratePCH.cpp b/clang/lib/Serialization/GeneratePCH.cpp index bed74399098d7f..a2ddbe4624aae4 100644 --- a/clang/lib/Serialization/GeneratePCH.cpp +++ b/clang/lib/Serialization/GeneratePCH.cpp @@ -117,7 +117,6 @@ void ReducedBMIGenerator::HandleTranslationUnit(ASTContext &Ctx) { getPreprocessor().getHeaderSearchInfo().getHeaderSearchOpts(); HSOpts.ModulesSkipDiagnosticOptions = true; HSOpts.ModulesSkipHeaderSearchPaths = true; - HSOpts.ModulesSkipPragmaDiagnosticMappings = true; PCHGenerator::HandleTranslationUnit(Ctx); diff --git a/clang/test/Modules/pr75057.cppm b/clang/test/Modules/pr75057.cppm new file mode 100644 index 00000000000000..96781b3ccacc0b --- /dev/null +++ b/clang/test/Modules/pr75057.cppm @@ -0,0 +1,66 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// Treat the behavior of using headers as baseline. +// RUN: %clang_cc1 -std=c++20 %t/use-header.cc -isystem %t -fsyntax-only -verify +// +// RUN: %clang_cc1 -std=c++20 %t/a.cppm -isystem %t -emit-module-interface -o %t/a.pcm +// RUN: %clang_cc1 -std=c++20 %t/use-module.cc -isystem %t -fmodule-file=a=%t/a.pcm -fsyntax-only -verify + +// Test again with reduced BMI. +// RUN: %clang_cc1 -std=c++20 %t/a.cppm -isystem %t -emit-reduced-module-interface -o %t/a.pcm +// RUN: %clang_cc1 -std=c++20 %t/use-module.cc -isystem %t -fmodule-file=a=%t/a.pcm -fsyntax-only -verify + +//--- sys.h +#ifndef SYS_H +#define SYS_H + +#pragma GCC system_header + +template +struct [[deprecated]] iterator {}; + +_Pragma("GCC diagnostic push") +_Pragma("GCC diagnostic ignored \"-Wdeprecated\"") +_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") + +template +struct reverse_iterator +: public iterator {}; + +_Pragma("GCC diagnostic pop") + +template +class C { +public: + void i() { + reverse_iterator i; + } +}; + +#endif + +//--- use-header.cc +// expected-no-diagnostics +// However, we see unexpected warnings +#include + +void use() { + C().i(); +} + +//--- a.cppm +module; +#include +export module a; +export using ::iterator; +export using ::C; + +//--- use-module.cc +// expected-no-diagnostics +import a; + +void use() { + C().i(); +} From f4843acd839f4f8687815560b69fa96ed3cbf8cf Mon Sep 17 00:00:00 2001 From: Christian Sigg Date: Tue, 30 Apr 2024 08:45:20 +0200 Subject: [PATCH 30/65] [lldb][bazel] Fix BUILD after 975eca0e6a3459e59e96b0df33ea0cfbd157c597. (#90564) --- .../llvm-project-overlay/lldb/BUILD.bazel | 23 ++++++++++++++++++- .../lldb/source/Plugins/BUILD.bazel | 1 + .../llvm-project-overlay/llvm/BUILD.bazel | 1 + 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/utils/bazel/llvm-project-overlay/lldb/BUILD.bazel b/utils/bazel/llvm-project-overlay/lldb/BUILD.bazel index 7958c6024875a5..b7b52f3ef59ce6 100644 --- a/utils/bazel/llvm-project-overlay/lldb/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/lldb/BUILD.bazel @@ -183,6 +183,20 @@ cc_binary( ], ) +gentbl_cc_library( + name = "lldb-sbapi-dwarf-enums", + strip_include_prefix = "include", + tbl_outs = [ + ( + ["-gen-lldb-sbapi-dwarf-enum"], + "include/lldb/API/SBLanguages.h", + ), + ], + tblgen = ":lldb-tblgen", + td_file = "//llvm:include/llvm/BinaryFormat/Dwarf.def", + deps = [], +) + cc_library( name = "API", srcs = glob([ @@ -192,6 +206,7 @@ cc_library( hdrs = glob(["include/lldb/API/**/*.h"]), strip_include_prefix = "include", deps = [ + ":lldb-sbapi-dwarf-enums", ":Breakpoint", ":Commands", ":Core", @@ -272,6 +287,7 @@ cc_library( hdrs = glob(["include/lldb/Expression/**/*.h"]), strip_include_prefix = "include", deps = [ + ":lldb-sbapi-dwarf-enums", ":Core", ":Headers", ":Host", @@ -280,6 +296,7 @@ cc_library( ":TargetHeaders", ":Utility", "//lldb/source/Plugins:PluginSymbolFileDWARFHeaders", + "//llvm:BinaryFormat", "//llvm:Core", "//llvm:DebugInfoDWARF", "//llvm:ExecutionEngine", @@ -346,7 +363,10 @@ cc_library( name = "ExpressionHeaders", hdrs = glob(["include/lldb/Expression/**/*.h"]), strip_include_prefix = "include", - deps = ["//llvm:ExecutionEngine"], + deps = [ + ":lldb-sbapi-dwarf-enums", + "//llvm:ExecutionEngine" + ], ) cc_library( @@ -673,6 +693,7 @@ cc_library( ":TargetProperties", ":Utility", "//lldb/source/Plugins:PluginProcessUtility", + "//llvm:BinaryFormat", "//llvm:MC", "//llvm:Support", ], diff --git a/utils/bazel/llvm-project-overlay/lldb/source/Plugins/BUILD.bazel b/utils/bazel/llvm-project-overlay/lldb/source/Plugins/BUILD.bazel index 13fec77fe567bb..d705af9167d812 100644 --- a/utils/bazel/llvm-project-overlay/lldb/source/Plugins/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/lldb/source/Plugins/BUILD.bazel @@ -202,6 +202,7 @@ cc_library( "//lldb:SymbolHeaders", "//lldb:TargetHeaders", "//lldb:Utility", + "//llvm:BinaryFormat", "//llvm:Core", "//llvm:ExecutionEngine", "//llvm:IPO", diff --git a/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel b/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel index f45f057b63c226..c159204cede7e5 100644 --- a/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel @@ -22,6 +22,7 @@ licenses(["notice"]) exports_files([ "LICENSE.TXT", "cmake/modules/llvm-driver-template.cpp.in", + "include/llvm/BinaryFormat/Dwarf.def", "include/llvm/CodeGen/SDNodeProperties.td", "include/llvm/CodeGen/ValueTypes.td", "include/llvm/Frontend/Directive/DirectiveBase.td", From ce12b12d0d786773b60adead18e994d6e4a0e228 Mon Sep 17 00:00:00 2001 From: Kareem Ergawy Date: Tue, 30 Apr 2024 08:49:51 +0200 Subject: [PATCH 31/65] [mlir][OpenMP] Extend `omp.private` with a `dealloc` region (#90456) Extends `omp.private` with a new region: `dealloc` where deallocation logic for Fortran deallocatables will be outlined (this will happen in later PRs). --- mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td | 28 +++++++++++++++---- mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp | 27 ++++++++++++++---- mlir/test/Dialect/OpenMP/invalid.mlir | 24 +++++++++++++++- mlir/test/Dialect/OpenMP/ops.mlir | 15 ++++++++++ 4 files changed, 82 insertions(+), 12 deletions(-) diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td index 8ab116ce391e29..a40676d071e620 100644 --- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td +++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td @@ -158,8 +158,9 @@ def PrivateClauseOp : OpenMP_Op<"private", [IsolatedFromAbove]> { let description = [{ This operation provides a declaration of how to implement the [first]privatization of a variable. The dialect users should provide - information about how to create an instance of the type in the alloc region - and how to initialize the copy from the original item in the copy region. + information about how to create an instance of the type in the alloc region, + how to initialize the copy from the original item in the copy region, and if + needed, how to deallocate allocated memory in the dealloc region. Examples: @@ -187,13 +188,26 @@ def PrivateClauseOp : OpenMP_Op<"private", [IsolatedFromAbove]> { } ``` + * `private(x)` for "allocatables" would be emitted as: + ```mlir + omp.private {type = private} @x.privatizer : !some.type alloc { + ^bb0(%arg0: !some.type): + %0 = ... allocate proper memory for the private clone ... + omp.yield(%0 : !fir.ref) + } dealloc { + ^bb0(%arg0: !some.type): + ... deallocate allocated memory ... + omp.yield + } + ``` + There are no restrictions on the body except for: - - The `alloc` region has a single argument. + - The `alloc` & `dealloc` regions have a single argument. - The `copy` region has 2 arguments. - - Both regions are terminated by `omp.yield` ops. + - All three regions are terminated by `omp.yield` ops. The above restrictions and other obvious restrictions (e.g. verifying the type of yielded values) are verified by the custom op verifier. The actual - contents of the blocks inside both regions are not verified. + contents of the blocks inside all regions are not verified. Instances of this op would then be used by ops that model directives that accept data-sharing attribute clauses. @@ -212,12 +226,14 @@ def PrivateClauseOp : OpenMP_Op<"private", [IsolatedFromAbove]> { DataSharingClauseTypeAttr:$data_sharing_type); let regions = (region MinSizedRegion<1>:$alloc_region, - AnyRegion:$copy_region); + AnyRegion:$copy_region, + AnyRegion:$dealloc_region); let assemblyFormat = [{ $data_sharing_type $sym_name `:` $type `alloc` $alloc_region (`copy` $copy_region^)? + (`dealloc` $dealloc_region^)? attr-dict }]; diff --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp index f60668dd0cf995..0799090cdea981 100644 --- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp +++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp @@ -2258,7 +2258,8 @@ void PrivateClauseOp::build(OpBuilder &odsBuilder, OperationState &odsState, LogicalResult PrivateClauseOp::verify() { Type symType = getType(); - auto verifyTerminator = [&](Operation *terminator) -> LogicalResult { + auto verifyTerminator = [&](Operation *terminator, + bool yieldsValue) -> LogicalResult { if (!terminator->getBlock()->getSuccessors().empty()) return success(); @@ -2269,6 +2270,14 @@ LogicalResult PrivateClauseOp::verify() { YieldOp yieldOp = llvm::cast(terminator); TypeRange yieldedTypes = yieldOp.getResults().getTypes(); + if (!yieldsValue) { + if (yieldedTypes.empty()) + return success(); + + return mlir::emitError(terminator->getLoc()) + << "Did not expect any values to be yielded."; + } + if (yieldedTypes.size() == 1 && yieldedTypes.front() == symType) return success(); @@ -2285,7 +2294,8 @@ LogicalResult PrivateClauseOp::verify() { }; auto verifyRegion = [&](Region ®ion, unsigned expectedNumArgs, - StringRef regionName) -> LogicalResult { + StringRef regionName, + bool yieldsValue) -> LogicalResult { assert(!region.empty()); if (region.getNumArguments() != expectedNumArgs) @@ -2299,14 +2309,15 @@ LogicalResult PrivateClauseOp::verify() { if (!block.mightHaveTerminator()) continue; - if (failed(verifyTerminator(block.getTerminator()))) + if (failed(verifyTerminator(block.getTerminator(), yieldsValue))) return failure(); } return success(); }; - if (failed(verifyRegion(getAllocRegion(), /*expectedNumArgs=*/1, "alloc"))) + if (failed(verifyRegion(getAllocRegion(), /*expectedNumArgs=*/1, "alloc", + /*yieldsValue=*/true))) return failure(); DataSharingClauseType dsType = getDataSharingType(); @@ -2319,7 +2330,13 @@ LogicalResult PrivateClauseOp::verify() { "`firstprivate` clauses require both `alloc` and `copy` regions."); if (dsType == DataSharingClauseType::FirstPrivate && - failed(verifyRegion(getCopyRegion(), /*expectedNumArgs=*/2, "copy"))) + failed(verifyRegion(getCopyRegion(), /*expectedNumArgs=*/2, "copy", + /*yieldsValue=*/true))) + return failure(); + + if (!getDeallocRegion().empty() && + failed(verifyRegion(getDeallocRegion(), /*expectedNumArgs=*/1, "dealloc", + /*yieldsValue=*/false))) return failure(); return success(); diff --git a/mlir/test/Dialect/OpenMP/invalid.mlir b/mlir/test/Dialect/OpenMP/invalid.mlir index e329b3010017cf..511e7d396c6875 100644 --- a/mlir/test/Dialect/OpenMP/invalid.mlir +++ b/mlir/test/Dialect/OpenMP/invalid.mlir @@ -2151,6 +2151,17 @@ omp.private {type = private} @x.privatizer : i32 alloc { // ----- +omp.private {type = private} @x.privatizer : f32 alloc { +^bb0(%arg0: f32): + omp.yield(%arg0 : f32) +} dealloc { +^bb0(%arg0: f32): + // expected-error @below {{Did not expect any values to be yielded.}} + omp.yield(%arg0 : f32) +} + +// ----- + omp.private {type = private} @x.privatizer : i32 alloc { ^bb0(%arg0: i32): // expected-error @below {{expected exit block terminator to be an `omp.yield` op.}} @@ -2178,6 +2189,17 @@ omp.private {type = firstprivate} @x.privatizer : f32 alloc { // ----- +// expected-error @below {{`dealloc`: expected 1 region arguments, got: 2}} +omp.private {type = private} @x.privatizer : f32 alloc { +^bb0(%arg0: f32): + omp.yield(%arg0 : f32) +} dealloc { +^bb0(%arg0: f32, %arg1: f32): + omp.yield +} + +// ----- + // expected-error @below {{`private` clauses require only an `alloc` region.}} omp.private {type = private} @x.privatizer : f32 alloc { ^bb0(%arg0: f32): @@ -2249,4 +2271,4 @@ func.func @undefined_privatizer(%arg0: !llvm.ptr) { omp.terminator }) : (!llvm.ptr) -> () return -} \ No newline at end of file +} diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir index a012588f0b5521..60fc10f9d64b73 100644 --- a/mlir/test/Dialect/OpenMP/ops.mlir +++ b/mlir/test/Dialect/OpenMP/ops.mlir @@ -2492,11 +2492,22 @@ func.func @parallel_op_privatizers(%arg0: !llvm.ptr, %arg1: !llvm.ptr) { return } +// CHECK-LABEL: omp.private {type = private} @a.privatizer : !llvm.ptr alloc { +omp.private {type = private} @a.privatizer : !llvm.ptr alloc { +// CHECK: ^bb0(%{{.*}}: {{.*}}): +^bb0(%arg0: !llvm.ptr): + omp.yield(%arg0 : !llvm.ptr) +} + // CHECK-LABEL: omp.private {type = private} @x.privatizer : !llvm.ptr alloc { omp.private {type = private} @x.privatizer : !llvm.ptr alloc { // CHECK: ^bb0(%{{.*}}: {{.*}}): ^bb0(%arg0: !llvm.ptr): omp.yield(%arg0 : !llvm.ptr) +} dealloc { +// CHECK: ^bb0(%{{.*}}: {{.*}}): +^bb0(%arg0: !llvm.ptr): + omp.yield } // CHECK-LABEL: omp.private {type = firstprivate} @y.privatizer : !llvm.ptr alloc { @@ -2509,6 +2520,10 @@ omp.private {type = firstprivate} @y.privatizer : !llvm.ptr alloc { // CHECK: ^bb0(%{{.*}}: {{.*}}, %{{.*}}: {{.*}}): ^bb0(%arg0: !llvm.ptr, %arg1: !llvm.ptr): omp.yield(%arg0 : !llvm.ptr) +} dealloc { +// CHECK: ^bb0(%{{.*}}: {{.*}}): +^bb0(%arg0: !llvm.ptr): + omp.yield } // CHECK-LABEL: parallel_op_reduction_and_private From 09f160c6298255f520b379b88161fbd1c365b308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= Date: Tue, 30 Apr 2024 09:01:45 +0200 Subject: [PATCH 32/65] [clang][analyzer] Move StreamChecker out of the alpha package. (#89247) --- clang/docs/analyzer/checkers.rst | 186 +++++++++--------- .../clang/StaticAnalyzer/Checkers/Checkers.td | 31 +-- clang/test/Analysis/analyzer-config.c | 2 +- .../test/Analysis/analyzer-enabled-checkers.c | 1 + ...c-library-functions-arg-enabled-checkers.c | 6 +- .../std-c-library-functions-arg-weakdeps.c | 4 +- ...td-c-library-functions-vs-stream-checker.c | 8 +- clang/test/Analysis/stream-errno-note.c | 4 +- clang/test/Analysis/stream-errno.c | 4 +- clang/test/Analysis/stream-error.c | 4 +- clang/test/Analysis/stream-invalidate.c | 2 +- .../test/Analysis/stream-non-posix-function.c | 2 +- clang/test/Analysis/stream-noopen.c | 2 +- clang/test/Analysis/stream-note.c | 8 +- clang/test/Analysis/stream-pedantic.c | 8 +- .../Analysis/stream-stdlibraryfunctionargs.c | 10 +- clang/test/Analysis/stream.c | 16 +- clang/test/Analysis/stream.cpp | 2 +- clang/www/analyzer/alpha_checks.html | 57 ------ clang/www/analyzer/open_projects.html | 16 -- 20 files changed, 151 insertions(+), 222 deletions(-) diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst index fb748d23a53d01..0d87df36ced0e9 100644 --- a/clang/docs/analyzer/checkers.rst +++ b/clang/docs/analyzer/checkers.rst @@ -1462,6 +1462,99 @@ checker). Default value of the option is ``true``. +.. _unix-Stream: + +unix.Stream (C) +""""""""""""""" +Check C stream handling functions: +``fopen, fdopen, freopen, tmpfile, fclose, fread, fwrite, fgetc, fgets, fputc, fputs, fprintf, fscanf, ungetc, getdelim, getline, fseek, fseeko, ftell, ftello, fflush, rewind, fgetpos, fsetpos, clearerr, feof, ferror, fileno``. + +The checker maintains information about the C stream objects (``FILE *``) and +can detect error conditions related to use of streams. The following conditions +are detected: + +* The ``FILE *`` pointer passed to the function is NULL (the single exception is + ``fflush`` where NULL is allowed). +* Use of stream after close. +* Opened stream is not closed. +* Read from a stream after end-of-file. (This is not a fatal error but reported + by the checker. Stream remains in EOF state and the read operation fails.) +* Use of stream when the file position is indeterminate after a previous failed + operation. Some functions (like ``ferror``, ``clearerr``, ``fseek``) are + allowed in this state. +* Invalid 3rd ("``whence``") argument to ``fseek``. + +The stream operations are by this checker usually split into two cases, a success +and a failure case. However, in the case of write operations (like ``fwrite``, +``fprintf`` and even ``fsetpos``) this behavior could produce a large amount of +unwanted reports on projects that don't have error checks around the write +operations, so by default the checker assumes that write operations always succeed. +This behavior can be controlled by the ``Pedantic`` flag: With +``-analyzer-config unix.Stream:Pedantic=true`` the checker will model the +cases where a write operation fails and report situations where this leads to +erroneous behavior. (The default is ``Pedantic=false``, where write operations +are assumed to succeed.) + +.. code-block:: c + + void test1() { + FILE *p = fopen("foo", "r"); + } // warn: opened file is never closed + + void test2() { + FILE *p = fopen("foo", "r"); + fseek(p, 1, SEEK_SET); // warn: stream pointer might be NULL + fclose(p); + } + + void test3() { + FILE *p = fopen("foo", "r"); + if (p) { + fseek(p, 1, 3); // warn: third arg should be SEEK_SET, SEEK_END, or SEEK_CUR + fclose(p); + } + } + + void test4() { + FILE *p = fopen("foo", "r"); + if (!p) + return; + + fclose(p); + fclose(p); // warn: stream already closed + } + + void test5() { + FILE *p = fopen("foo", "r"); + if (!p) + return; + + fgetc(p); + if (!ferror(p)) + fgetc(p); // warn: possible read after end-of-file + + fclose(p); + } + + void test6() { + FILE *p = fopen("foo", "r"); + if (!p) + return; + + fgetc(p); + if (!feof(p)) + fgetc(p); // warn: file position may be indeterminate after I/O error + + fclose(p); + } + +**Limitations** + +The checker does not track the correspondence between integer file descriptors +and ``FILE *`` pointers. Operations on standard streams like ``stdin`` are not +treated specially and are therefore often not recognized (because these streams +are usually not opened explicitly by the program, and are global variables). + .. _osx-checkers: osx @@ -3116,99 +3209,6 @@ Check for misuses of stream APIs. Check for misuses of stream APIs: ``fopen, fcl fclose(F); // warn: closing a previously closed file stream } -.. _alpha-unix-Stream: - -alpha.unix.Stream (C) -""""""""""""""""""""" -Check C stream handling functions: -``fopen, fdopen, freopen, tmpfile, fclose, fread, fwrite, fgetc, fgets, fputc, fputs, fprintf, fscanf, ungetc, getdelim, getline, fseek, fseeko, ftell, ftello, fflush, rewind, fgetpos, fsetpos, clearerr, feof, ferror, fileno``. - -The checker maintains information about the C stream objects (``FILE *``) and -can detect error conditions related to use of streams. The following conditions -are detected: - -* The ``FILE *`` pointer passed to the function is NULL (the single exception is - ``fflush`` where NULL is allowed). -* Use of stream after close. -* Opened stream is not closed. -* Read from a stream after end-of-file. (This is not a fatal error but reported - by the checker. Stream remains in EOF state and the read operation fails.) -* Use of stream when the file position is indeterminate after a previous failed - operation. Some functions (like ``ferror``, ``clearerr``, ``fseek``) are - allowed in this state. -* Invalid 3rd ("``whence``") argument to ``fseek``. - -The stream operations are by this checker usually split into two cases, a success -and a failure case. However, in the case of write operations (like ``fwrite``, -``fprintf`` and even ``fsetpos``) this behavior could produce a large amount of -unwanted reports on projects that don't have error checks around the write -operations, so by default the checker assumes that write operations always succeed. -This behavior can be controlled by the ``Pedantic`` flag: With -``-analyzer-config alpha.unix.Stream:Pedantic=true`` the checker will model the -cases where a write operation fails and report situations where this leads to -erroneous behavior. (The default is ``Pedantic=false``, where write operations -are assumed to succeed.) - -.. code-block:: c - - void test1() { - FILE *p = fopen("foo", "r"); - } // warn: opened file is never closed - - void test2() { - FILE *p = fopen("foo", "r"); - fseek(p, 1, SEEK_SET); // warn: stream pointer might be NULL - fclose(p); - } - - void test3() { - FILE *p = fopen("foo", "r"); - if (p) { - fseek(p, 1, 3); // warn: third arg should be SEEK_SET, SEEK_END, or SEEK_CUR - fclose(p); - } - } - - void test4() { - FILE *p = fopen("foo", "r"); - if (!p) - return; - - fclose(p); - fclose(p); // warn: stream already closed - } - - void test5() { - FILE *p = fopen("foo", "r"); - if (!p) - return; - - fgetc(p); - if (!ferror(p)) - fgetc(p); // warn: possible read after end-of-file - - fclose(p); - } - - void test6() { - FILE *p = fopen("foo", "r"); - if (!p) - return; - - fgetc(p); - if (!feof(p)) - fgetc(p); // warn: file position may be indeterminate after I/O error - - fclose(p); - } - -**Limitations** - -The checker does not track the correspondence between integer file descriptors -and ``FILE *`` pointers. Operations on standard streams like ``stdin`` are not -treated specially and are therefore often not recognized (because these streams -are usually not opened explicitly by the program, and are global variables). - .. _alpha-unix-cstring-BufferOverlap: alpha.unix.cstring.BufferOverlap (C) diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index 9aa1c6ddfe4492..520286b57c9fdc 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -563,6 +563,20 @@ def MismatchedDeallocatorChecker : Checker<"MismatchedDeallocator">, Dependencies<[DynamicMemoryModeling]>, Documentation; +// This must appear before StdCLibraryFunctionsChecker because a dependency. +def StreamChecker : Checker<"Stream">, + HelpText<"Check stream handling functions">, + WeakDependencies<[NonNullParamChecker]>, + CheckerOptions<[ + CmdLineOption + ]>, + Documentation; + def StdCLibraryFunctionsChecker : Checker<"StdCLibraryFunctions">, HelpText<"Check for invalid arguments of C standard library functions, " "and apply relations between arguments and return value">, @@ -581,7 +595,7 @@ def StdCLibraryFunctionsChecker : Checker<"StdCLibraryFunctions">, "true", InAlpha> ]>, - WeakDependencies<[CallAndMessageChecker, NonNullParamChecker]>, + WeakDependencies<[CallAndMessageChecker, NonNullParamChecker, StreamChecker]>, Documentation; def VforkChecker : Checker<"Vfork">, @@ -601,20 +615,6 @@ def PthreadLockChecker : Checker<"PthreadLock">, Dependencies<[PthreadLockBase]>, Documentation; -def StreamChecker : Checker<"Stream">, - HelpText<"Check stream handling functions">, - WeakDependencies<[NonNullParamChecker]>, - CheckerOptions<[ - CmdLineOption - ]>, - Documentation; - def SimpleStreamChecker : Checker<"SimpleStream">, HelpText<"Check for misuses of stream APIs">, Documentation; @@ -1628,6 +1628,7 @@ def TaintTesterChecker : Checker<"TaintTest">, def StreamTesterChecker : Checker<"StreamTester">, HelpText<"Add test functions to StreamChecker for test and debugging " "purposes.">, + WeakDependencies<[StreamChecker]>, Documentation; def ErrnoTesterChecker : Checker<"ErrnoTest">, diff --git a/clang/test/Analysis/analyzer-config.c b/clang/test/Analysis/analyzer-config.c index 23e37a856d09f7..fda920fa3951e0 100644 --- a/clang/test/Analysis/analyzer-config.c +++ b/clang/test/Analysis/analyzer-config.c @@ -12,7 +12,6 @@ // CHECK-NEXT: alpha.security.MmapWriteExec:MmapProtExec = 0x04 // CHECK-NEXT: alpha.security.MmapWriteExec:MmapProtRead = 0x01 // CHECK-NEXT: alpha.security.taint.TaintPropagation:Config = "" -// CHECK-NEXT: alpha.unix.Stream:Pedantic = false // CHECK-NEXT: apply-fixits = false // CHECK-NEXT: assume-controlled-environment = false // CHECK-NEXT: avoid-suppressing-null-argument-paths = false @@ -131,6 +130,7 @@ // CHECK-NEXT: unix.Errno:AllowErrnoReadOutsideConditionExpressions = true // CHECK-NEXT: unix.StdCLibraryFunctions:DisplayLoadedSummaries = false // CHECK-NEXT: unix.StdCLibraryFunctions:ModelPOSIX = true +// CHECK-NEXT: unix.Stream:Pedantic = false // CHECK-NEXT: unroll-loops = false // CHECK-NEXT: verbose-report-filename = false // CHECK-NEXT: widen-loops = false diff --git a/clang/test/Analysis/analyzer-enabled-checkers.c b/clang/test/Analysis/analyzer-enabled-checkers.c index 2c4e34f4990bf5..9543ba8ec02fc1 100644 --- a/clang/test/Analysis/analyzer-enabled-checkers.c +++ b/clang/test/Analysis/analyzer-enabled-checkers.c @@ -48,6 +48,7 @@ // CHECK-NEXT: unix.Malloc // CHECK-NEXT: unix.MallocSizeof // CHECK-NEXT: unix.MismatchedDeallocator +// CHECK-NEXT: unix.Stream // CHECK-NEXT: unix.StdCLibraryFunctions // CHECK-NEXT: unix.Vfork // CHECK-NEXT: unix.cstring.BadSizeArg diff --git a/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c b/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c index d4721c0a59a3d0..14aca5a948bf4b 100644 --- a/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c +++ b/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c @@ -6,7 +6,7 @@ // RUN: -Xclang -analyzer-checker=unix.StdCLibraryFunctions \ // RUN: -Xclang -analyzer-config \ // RUN: -Xclang unix.StdCLibraryFunctions:ModelPOSIX=true \ -// RUN: -Xclang -analyzer-checker=alpha.unix.Stream \ +// RUN: -Xclang -analyzer-checker=unix.Stream \ // RUN: -Xclang -analyzer-list-enabled-checkers \ // RUN: -Xclang -analyzer-display-progress \ // RUN: 2>&1 | FileCheck %s --implicit-check-not=ANALYZE \ @@ -14,8 +14,6 @@ // CHECK: OVERVIEW: Clang Static Analyzer Enabled Checkers List // CHECK-EMPTY: -// CHECK-NEXT: core.NonNullParamChecker -// CHECK-NEXT: alpha.unix.Stream // CHECK-NEXT: apiModeling.Errno // CHECK-NEXT: apiModeling.TrustNonnull // CHECK-NEXT: apiModeling.TrustReturnsNonnull @@ -26,6 +24,7 @@ // CHECK-NEXT: core.CallAndMessage // CHECK-NEXT: core.DivideZero // CHECK-NEXT: core.DynamicTypePropagation +// CHECK-NEXT: core.NonNullParamChecker // CHECK-NEXT: core.NonnilStringConstants // CHECK-NEXT: core.NullDereference // CHECK-NEXT: core.StackAddrEscapeBase @@ -57,6 +56,7 @@ // CHECK-NEXT: unix.Malloc // CHECK-NEXT: unix.MallocSizeof // CHECK-NEXT: unix.MismatchedDeallocator +// CHECK-NEXT: unix.Stream // CHECK-NEXT: unix.StdCLibraryFunctions // CHECK-NEXT: unix.Vfork // CHECK-NEXT: unix.cstring.BadSizeArg diff --git a/clang/test/Analysis/std-c-library-functions-arg-weakdeps.c b/clang/test/Analysis/std-c-library-functions-arg-weakdeps.c index 5df5a770015b50..1f0d3627fae340 100644 --- a/clang/test/Analysis/std-c-library-functions-arg-weakdeps.c +++ b/clang/test/Analysis/std-c-library-functions-arg-weakdeps.c @@ -3,7 +3,7 @@ // RUN: %clang_analyze_cc1 %s \ // RUN: -analyzer-checker=core \ -// RUN: -analyzer-checker=alpha.unix.Stream \ +// RUN: -analyzer-checker=unix.Stream \ // RUN: -analyzer-checker=unix.StdCLibraryFunctions \ // RUN: -analyzer-config unix.StdCLibraryFunctions:ModelPOSIX=true \ // RUN: -triple x86_64-unknown-linux-gnu \ @@ -57,5 +57,5 @@ void test_notnull_arg(FILE *F) { void test_notnull_stream_arg(void) { fileno(0); // \ - // expected-warning{{Stream pointer might be NULL [alpha.unix.Stream]}} + // expected-warning{{Stream pointer might be NULL [unix.Stream]}} } diff --git a/clang/test/Analysis/std-c-library-functions-vs-stream-checker.c b/clang/test/Analysis/std-c-library-functions-vs-stream-checker.c index cac3fe5c5151cd..b99cc30149c912 100644 --- a/clang/test/Analysis/std-c-library-functions-vs-stream-checker.c +++ b/clang/test/Analysis/std-c-library-functions-vs-stream-checker.c @@ -1,7 +1,7 @@ // Check the case when only the StreamChecker is enabled. // RUN: %clang_analyze_cc1 %s \ -// RUN: -analyzer-checker=core,alpha.unix.Stream \ -// RUN: -analyzer-config alpha.unix.Stream:Pedantic=true \ +// RUN: -analyzer-checker=core,unix.Stream \ +// RUN: -analyzer-config unix.Stream:Pedantic=true \ // RUN: -analyzer-checker=debug.ExprInspection \ // RUN: -analyzer-config eagerly-assume=false \ // RUN: -triple x86_64-unknown-linux \ @@ -19,8 +19,8 @@ // Check the case when both the StreamChecker and the // StdLibraryFunctionsChecker are enabled. // RUN: %clang_analyze_cc1 %s \ -// RUN: -analyzer-checker=core,alpha.unix.Stream \ -// RUN: -analyzer-config alpha.unix.Stream:Pedantic=true \ +// RUN: -analyzer-checker=core,unix.Stream \ +// RUN: -analyzer-config unix.Stream:Pedantic=true \ // RUN: -analyzer-checker=unix.StdCLibraryFunctions \ // RUN: -analyzer-config unix.StdCLibraryFunctions:DisplayLoadedSummaries=true \ // RUN: -analyzer-checker=debug.ExprInspection \ diff --git a/clang/test/Analysis/stream-errno-note.c b/clang/test/Analysis/stream-errno-note.c index fb12f0bace937f..71ea026ed4de33 100644 --- a/clang/test/Analysis/stream-errno-note.c +++ b/clang/test/Analysis/stream-errno-note.c @@ -1,6 +1,6 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=core \ -// RUN: -analyzer-checker=alpha.unix.Stream \ -// RUN: -analyzer-config alpha.unix.Stream:Pedantic=true \ +// RUN: -analyzer-checker=unix.Stream \ +// RUN: -analyzer-config unix.Stream:Pedantic=true \ // RUN: -analyzer-checker=unix.Errno \ // RUN: -analyzer-checker=unix.StdCLibraryFunctions \ // RUN: -analyzer-config unix.StdCLibraryFunctions:ModelPOSIX=true \ diff --git a/clang/test/Analysis/stream-errno.c b/clang/test/Analysis/stream-errno.c index 08382eaf6abf9f..b28cc301a4ec25 100644 --- a/clang/test/Analysis/stream-errno.c +++ b/clang/test/Analysis/stream-errno.c @@ -1,5 +1,5 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.unix.Stream,unix.Errno,unix.StdCLibraryFunctions,debug.ExprInspection \ -// RUN: -analyzer-config alpha.unix.Stream:Pedantic=true \ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Stream,unix.Errno,unix.StdCLibraryFunctions,debug.ExprInspection \ +// RUN: -analyzer-config unix.Stream:Pedantic=true \ // RUN: -analyzer-config unix.StdCLibraryFunctions:ModelPOSIX=true -verify %s #include "Inputs/system-header-simulator.h" diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index 2abf4b900a047f..3f791d13346419 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -1,7 +1,7 @@ // RUN: %clang_analyze_cc1 -verify %s \ // RUN: -analyzer-checker=core \ -// RUN: -analyzer-checker=alpha.unix.Stream \ -// RUN: -analyzer-config alpha.unix.Stream:Pedantic=true \ +// RUN: -analyzer-checker=unix.Stream \ +// RUN: -analyzer-config unix.Stream:Pedantic=true \ // RUN: -analyzer-checker=debug.StreamTester \ // RUN: -analyzer-checker=debug.ExprInspection diff --git a/clang/test/Analysis/stream-invalidate.c b/clang/test/Analysis/stream-invalidate.c index 5046a356d0583d..749c53d164fb5c 100644 --- a/clang/test/Analysis/stream-invalidate.c +++ b/clang/test/Analysis/stream-invalidate.c @@ -1,6 +1,6 @@ // RUN: %clang_analyze_cc1 -verify %s \ // RUN: -analyzer-checker=core \ -// RUN: -analyzer-checker=alpha.unix.Stream \ +// RUN: -analyzer-checker=unix.Stream \ // RUN: -analyzer-checker=debug.ExprInspection #include "Inputs/system-header-simulator.h" diff --git a/clang/test/Analysis/stream-non-posix-function.c b/clang/test/Analysis/stream-non-posix-function.c index 091d95a573ddf9..b9ece31cde1568 100644 --- a/clang/test/Analysis/stream-non-posix-function.c +++ b/clang/test/Analysis/stream-non-posix-function.c @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -fno-builtin -analyzer-checker=core,alpha.unix.Stream -verify %s +// RUN: %clang_analyze_cc1 -fno-builtin -analyzer-checker=core,unix.Stream -verify %s // RUN: %clang_analyze_cc1 -fno-builtin -analyzer-checker=core,alpha.unix.SimpleStream -verify %s // expected-no-diagnostics diff --git a/clang/test/Analysis/stream-noopen.c b/clang/test/Analysis/stream-noopen.c index 644c699d05e246..87761b3afb76b4 100644 --- a/clang/test/Analysis/stream-noopen.c +++ b/clang/test/Analysis/stream-noopen.c @@ -1,7 +1,7 @@ // RUN: %clang_analyze_cc1 -verify %s \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-checker=unix.Errno \ -// RUN: -analyzer-checker=alpha.unix.Stream \ +// RUN: -analyzer-checker=unix.Stream \ // RUN: -analyzer-checker=unix.StdCLibraryFunctions \ // RUN: -analyzer-config unix.StdCLibraryFunctions:ModelPOSIX=true \ // RUN: -analyzer-checker=debug.ExprInspection diff --git a/clang/test/Analysis/stream-note.c b/clang/test/Analysis/stream-note.c index 03a8ff4e468f66..3aef707d50056e 100644 --- a/clang/test/Analysis/stream-note.c +++ b/clang/test/Analysis/stream-note.c @@ -1,8 +1,8 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.unix.Stream -analyzer-output text \ -// RUN: -analyzer-config alpha.unix.Stream:Pedantic=true \ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Stream -analyzer-output text \ +// RUN: -analyzer-config unix.Stream:Pedantic=true \ // RUN: -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.unix.Stream,unix.StdCLibraryFunctions -analyzer-output text \ -// RUN: -analyzer-config alpha.unix.Stream:Pedantic=true \ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Stream,unix.StdCLibraryFunctions -analyzer-output text \ +// RUN: -analyzer-config unix.Stream:Pedantic=true \ // RUN: -analyzer-config unix.StdCLibraryFunctions:ModelPOSIX=true -verify=expected,stdargs %s #include "Inputs/system-header-simulator.h" diff --git a/clang/test/Analysis/stream-pedantic.c b/clang/test/Analysis/stream-pedantic.c index 2bbea81d47ef60..2a3dff86789030 100644 --- a/clang/test/Analysis/stream-pedantic.c +++ b/clang/test/Analysis/stream-pedantic.c @@ -1,8 +1,8 @@ -// RUN: %clang_analyze_cc1 -triple=x86_64-pc-linux-gnu -analyzer-checker=core,alpha.unix.Stream,debug.ExprInspection \ -// RUN: -analyzer-config alpha.unix.Stream:Pedantic=false -verify=nopedantic %s +// RUN: %clang_analyze_cc1 -triple=x86_64-pc-linux-gnu -analyzer-checker=core,unix.Stream,debug.ExprInspection \ +// RUN: -analyzer-config unix.Stream:Pedantic=false -verify=nopedantic %s -// RUN: %clang_analyze_cc1 -triple=x86_64-pc-linux-gnu -analyzer-checker=core,alpha.unix.Stream,debug.ExprInspection \ -// RUN: -analyzer-config alpha.unix.Stream:Pedantic=true -verify=pedantic %s +// RUN: %clang_analyze_cc1 -triple=x86_64-pc-linux-gnu -analyzer-checker=core,unix.Stream,debug.ExprInspection \ +// RUN: -analyzer-config unix.Stream:Pedantic=true -verify=pedantic %s #include "Inputs/system-header-simulator.h" diff --git a/clang/test/Analysis/stream-stdlibraryfunctionargs.c b/clang/test/Analysis/stream-stdlibraryfunctionargs.c index 2ea6a8c472c613..c3a6302f9c7afc 100644 --- a/clang/test/Analysis/stream-stdlibraryfunctionargs.c +++ b/clang/test/Analysis/stream-stdlibraryfunctionargs.c @@ -1,13 +1,13 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.unix.Stream,unix.StdCLibraryFunctions,debug.ExprInspection \ -// RUN: -analyzer-config alpha.unix.Stream:Pedantic=true \ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Stream,unix.StdCLibraryFunctions,debug.ExprInspection \ +// RUN: -analyzer-config unix.Stream:Pedantic=true \ // RUN: -analyzer-config unix.StdCLibraryFunctions:ModelPOSIX=true -verify=stream,any %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.unix.Stream,debug.ExprInspection \ -// RUN: -analyzer-config alpha.unix.Stream:Pedantic=true \ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Stream,debug.ExprInspection \ +// RUN: -analyzer-config unix.Stream:Pedantic=true \ // RUN: -analyzer-config unix.StdCLibraryFunctions:ModelPOSIX=true -verify=stream,any %s // RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.StdCLibraryFunctions,debug.ExprInspection \ -// RUN: -analyzer-config alpha.unix.Stream:Pedantic=true \ +// RUN: -analyzer-config unix.Stream:Pedantic=true \ // RUN: -analyzer-config unix.StdCLibraryFunctions:ModelPOSIX=true -verify=stdfunc,any %s #include "Inputs/system-header-simulator.h" diff --git a/clang/test/Analysis/stream.c b/clang/test/Analysis/stream.c index 93ed555c89ebd1..db03d90cd8af4e 100644 --- a/clang/test/Analysis/stream.c +++ b/clang/test/Analysis/stream.c @@ -1,11 +1,11 @@ -// RUN: %clang_analyze_cc1 -triple=x86_64-pc-linux-gnu -analyzer-checker=core,alpha.unix.Stream,debug.ExprInspection \ -// RUN: -analyzer-config alpha.unix.Stream:Pedantic=true -verify %s -// RUN: %clang_analyze_cc1 -triple=armv8-none-linux-eabi -analyzer-checker=core,alpha.unix.Stream,debug.ExprInspection \ -// RUN: -analyzer-config alpha.unix.Stream:Pedantic=true -verify %s -// RUN: %clang_analyze_cc1 -triple=aarch64-linux-gnu -analyzer-checker=core,alpha.unix.Stream,debug.ExprInspection \ -// RUN: -analyzer-config alpha.unix.Stream:Pedantic=true -verify %s -// RUN: %clang_analyze_cc1 -triple=hexagon -analyzer-checker=core,alpha.unix.Stream,debug.ExprInspection \ -// RUN: -analyzer-config alpha.unix.Stream:Pedantic=true -verify %s +// RUN: %clang_analyze_cc1 -triple=x86_64-pc-linux-gnu -analyzer-checker=core,unix.Stream,debug.ExprInspection \ +// RUN: -analyzer-config unix.Stream:Pedantic=true -verify %s +// RUN: %clang_analyze_cc1 -triple=armv8-none-linux-eabi -analyzer-checker=core,unix.Stream,debug.ExprInspection \ +// RUN: -analyzer-config unix.Stream:Pedantic=true -verify %s +// RUN: %clang_analyze_cc1 -triple=aarch64-linux-gnu -analyzer-checker=core,unix.Stream,debug.ExprInspection \ +// RUN: -analyzer-config unix.Stream:Pedantic=true -verify %s +// RUN: %clang_analyze_cc1 -triple=hexagon -analyzer-checker=core,unix.Stream,debug.ExprInspection \ +// RUN: -analyzer-config unix.Stream:Pedantic=true -verify %s #include "Inputs/system-header-simulator.h" #include "Inputs/system-header-simulator-for-malloc.h" diff --git a/clang/test/Analysis/stream.cpp b/clang/test/Analysis/stream.cpp index 7eca505bcaf5d9..8c65d9c287fdaf 100644 --- a/clang/test/Analysis/stream.cpp +++ b/clang/test/Analysis/stream.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.unix.Stream -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Stream -verify %s typedef struct _IO_FILE FILE; extern FILE *fopen(const char *path, const char *mode); diff --git a/clang/www/analyzer/alpha_checks.html b/clang/www/analyzer/alpha_checks.html index f040d1957b0f98..2c8eece41fb2fc 100644 --- a/clang/www/analyzer/alpha_checks.html +++ b/clang/www/analyzer/alpha_checks.html @@ -910,63 +910,6 @@

Unix Alpha Checkers

- - - -