From 5c586bf7478b7041dda53bfb921f4bdd514f1cf5 Mon Sep 17 00:00:00 2001 From: Tim Blechmann Date: Tue, 15 Jul 2025 15:32:15 +0800 Subject: [PATCH 1/5] Windows: use EcoQoS for ThreadPriority::Background The SetThreadInformation API allows threads to be scheduled on the most efficient cores on the most efficient frequency. Using this API for ThreadPriority::Background should make clangd-based IDEs a little less CPU hungry. --- llvm/lib/Support/Windows/Threading.inc | 29 ++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/llvm/lib/Support/Windows/Threading.inc b/llvm/lib/Support/Windows/Threading.inc index d862dbd7f71c9..571e0960a6888 100644 --- a/llvm/lib/Support/Windows/Threading.inc +++ b/llvm/lib/Support/Windows/Threading.inc @@ -107,6 +107,35 @@ void llvm::get_thread_name(SmallVectorImpl &Name) { } SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) { + + // SetThreadInformation is only available on Windows 8 and later. Since we still + // support compilation on Windows 7, we load the function dynamically. + typedef BOOL(WINAPI * SetThreadInformation_t)( + HANDLE hThread, THREAD_INFORMATION_CLASS ThreadInformationClass, + _In_reads_bytes_(ThreadInformationSize) PVOID ThreadInformation, + ULONG ThreadInformationSize); + static const auto pfnSetThreadInformation = + (SetThreadInformation_t)GetProcAddress( + GetModuleHandle(TEXT("kernel32.dll")), "SetThreadInformation"); + + if (pfnSetThreadInformation) { + auto setThreadInformation = [](ULONG ControlMaskAndStateMask) { + THREAD_POWER_THROTTLING_STATE state{}; + state.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION; + state.ControlMask = ControlMaskAndStateMask; + state.StateMask = ControlMaskAndStateMask; + return pfnSetThreadInformation(GetCurrentThread(), ThreadPowerThrottling, + &state, sizeof(state)); + }; + + // Use EcoQoS for ThreadPriority::Background available (running on most + // efficent cores at the most efficient cpu frequency): + // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadinformation + // https://learn.microsoft.com/en-us/windows/win32/procthread/quality-of-service + setThreadInformation(Priority == ThreadPriority::Background ? THREAD_POWER_THROTTLING_EXECUTION_SPEED + : 0); + } + // https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-setthreadpriority // Begin background processing mode. The system lowers the resource scheduling // priorities of the thread so that it can perform background work without From 00e16ce021e2297e1bc3b417ead2e0e0fdc42139 Mon Sep 17 00:00:00 2001 From: Alexandre Ganea Date: Wed, 23 Jul 2025 16:36:04 -0400 Subject: [PATCH 2/5] clang-format --- llvm/lib/Support/Windows/Threading.inc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/llvm/lib/Support/Windows/Threading.inc b/llvm/lib/Support/Windows/Threading.inc index 571e0960a6888..49a7974d312af 100644 --- a/llvm/lib/Support/Windows/Threading.inc +++ b/llvm/lib/Support/Windows/Threading.inc @@ -107,9 +107,8 @@ void llvm::get_thread_name(SmallVectorImpl &Name) { } SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) { - - // SetThreadInformation is only available on Windows 8 and later. Since we still - // support compilation on Windows 7, we load the function dynamically. + // SetThreadInformation is only available on Windows 8 and later. Since we + // still support compilation on Windows 7, we load the function dynamically. typedef BOOL(WINAPI * SetThreadInformation_t)( HANDLE hThread, THREAD_INFORMATION_CLASS ThreadInformationClass, _In_reads_bytes_(ThreadInformationSize) PVOID ThreadInformation, @@ -132,8 +131,9 @@ SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) { // efficent cores at the most efficient cpu frequency): // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadinformation // https://learn.microsoft.com/en-us/windows/win32/procthread/quality-of-service - setThreadInformation(Priority == ThreadPriority::Background ? THREAD_POWER_THROTTLING_EXECUTION_SPEED - : 0); + setThreadInformation(Priority == ThreadPriority::Background + ? THREAD_POWER_THROTTLING_EXECUTION_SPEED + : 0); } // https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-setthreadpriority From 8130f416a1135ff905c9f210197cd6c844f676c0 Mon Sep 17 00:00:00 2001 From: Alexandre Ganea Date: Wed, 23 Jul 2025 17:37:31 -0400 Subject: [PATCH 3/5] Ensure we load the system kernel32.dll and not another injected lib bearing the same name --- llvm/lib/Support/Windows/Threading.inc | 84 +++++++++++++++++--------- 1 file changed, 57 insertions(+), 27 deletions(-) diff --git a/llvm/lib/Support/Windows/Threading.inc b/llvm/lib/Support/Windows/Threading.inc index 49a7974d312af..2d295d1c07ee8 100644 --- a/llvm/lib/Support/Windows/Threading.inc +++ b/llvm/lib/Support/Windows/Threading.inc @@ -106,34 +106,64 @@ void llvm::get_thread_name(SmallVectorImpl &Name) { Name.clear(); } -SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) { - // SetThreadInformation is only available on Windows 8 and later. Since we - // still support compilation on Windows 7, we load the function dynamically. - typedef BOOL(WINAPI * SetThreadInformation_t)( - HANDLE hThread, THREAD_INFORMATION_CLASS ThreadInformationClass, - _In_reads_bytes_(ThreadInformationSize) PVOID ThreadInformation, - ULONG ThreadInformationSize); - static const auto pfnSetThreadInformation = - (SetThreadInformation_t)GetProcAddress( - GetModuleHandle(TEXT("kernel32.dll")), "SetThreadInformation"); - - if (pfnSetThreadInformation) { - auto setThreadInformation = [](ULONG ControlMaskAndStateMask) { - THREAD_POWER_THROTTLING_STATE state{}; - state.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION; - state.ControlMask = ControlMaskAndStateMask; - state.StateMask = ControlMaskAndStateMask; - return pfnSetThreadInformation(GetCurrentThread(), ThreadPowerThrottling, - &state, sizeof(state)); - }; +static HMODULE LoadSystemModuleSecure(LPCWSTR lpModuleName) { + // Ensure we load indeed a module from system32 path. + // As per GetModuleHandle documentation: + // "If lpModuleName does not include a path and there is more than one loaded + // module with the same base name and extension, you cannot predict which + // module handle will be returned.". This mitigates + // https://learn.microsoft.com/en-us/security-updates/securityadvisories/2010/2269637 + SmallVector Buf; + size_t Size = MAX_PATH; + do { + Buf.resize_for_overwrite(Size); + SetLastError(NO_ERROR); + Size = ::GetSystemDirectoryW(Buf.data(), Buf.size()); + if (Size == 0) + return NULL; + + // Try again with larger buffer. + } while (Size > Buf.size()); + + Buf.truncate(Size); + Buf.push_back(TEXT('\\')); + Buf.append(lpModuleName, lpModuleName + std::wcslen(lpModuleName)); + Buf.push_back(0); + + return GetModuleHandleW(Buf.data()); +} - // Use EcoQoS for ThreadPriority::Background available (running on most - // efficent cores at the most efficient cpu frequency): - // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadinformation - // https://learn.microsoft.com/en-us/windows/win32/procthread/quality-of-service - setThreadInformation(Priority == ThreadPriority::Background - ? THREAD_POWER_THROTTLING_EXECUTION_SPEED - : 0); +SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) { + HMODULE kernelMod = LoadSystemModuleSecure(TEXT("kernel32.dll")); + if (kernelMod) { + // SetThreadInformation is only available on Windows 8 and later. Since we + // still support compilation on Windows 7, we load the function dynamically. + typedef BOOL(WINAPI * SetThreadInformation_t)( + HANDLE hThread, THREAD_INFORMATION_CLASS ThreadInformationClass, + _In_reads_bytes_(ThreadInformationSize) PVOID ThreadInformation, + ULONG ThreadInformationSize); + static const auto pfnSetThreadInformation = + (SetThreadInformation_t)GetProcAddress(kernelMod, + "SetThreadInformation"); + + if (pfnSetThreadInformation) { + auto setThreadInformation = [](ULONG ControlMaskAndStateMask) { + THREAD_POWER_THROTTLING_STATE state{}; + state.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION; + state.ControlMask = ControlMaskAndStateMask; + state.StateMask = ControlMaskAndStateMask; + return pfnSetThreadInformation( + GetCurrentThread(), ThreadPowerThrottling, &state, sizeof(state)); + }; + + // Use EcoQoS for ThreadPriority::Background available (running on most + // efficent cores at the most efficient cpu frequency): + // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadinformation + // https://learn.microsoft.com/en-us/windows/win32/procthread/quality-of-service + setThreadInformation(Priority == ThreadPriority::Background + ? THREAD_POWER_THROTTLING_EXECUTION_SPEED + : 0); + } } // https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-setthreadpriority From 670a81a8220b5d37d5ed083a5d868683b1657fed Mon Sep 17 00:00:00 2001 From: Alexandre Ganea Date: Wed, 23 Jul 2025 17:44:25 -0400 Subject: [PATCH 4/5] Use global functions --- llvm/lib/Support/Windows/Threading.inc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Support/Windows/Threading.inc b/llvm/lib/Support/Windows/Threading.inc index 2d295d1c07ee8..5ef79b340caf1 100644 --- a/llvm/lib/Support/Windows/Threading.inc +++ b/llvm/lib/Support/Windows/Threading.inc @@ -130,7 +130,7 @@ static HMODULE LoadSystemModuleSecure(LPCWSTR lpModuleName) { Buf.append(lpModuleName, lpModuleName + std::wcslen(lpModuleName)); Buf.push_back(0); - return GetModuleHandleW(Buf.data()); + return ::GetModuleHandleW(Buf.data()); } SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) { @@ -143,8 +143,8 @@ SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) { _In_reads_bytes_(ThreadInformationSize) PVOID ThreadInformation, ULONG ThreadInformationSize); static const auto pfnSetThreadInformation = - (SetThreadInformation_t)GetProcAddress(kernelMod, - "SetThreadInformation"); + (SetThreadInformation_t)::GetProcAddress(kernelMod, + "SetThreadInformation"); if (pfnSetThreadInformation) { auto setThreadInformation = [](ULONG ControlMaskAndStateMask) { @@ -153,7 +153,7 @@ SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) { state.ControlMask = ControlMaskAndStateMask; state.StateMask = ControlMaskAndStateMask; return pfnSetThreadInformation( - GetCurrentThread(), ThreadPowerThrottling, &state, sizeof(state)); + ::GetCurrentThread(), ThreadPowerThrottling, &state, sizeof(state)); }; // Use EcoQoS for ThreadPriority::Background available (running on most From c37d94c94323a419123fae5521c2c87994117e7e Mon Sep 17 00:00:00 2001 From: Alexandre Ganea Date: Sun, 27 Jul 2025 13:59:42 -0400 Subject: [PATCH 5/5] Force wchar_t string; move function to WindowsSupport.h --- llvm/include/llvm/Support/Windows/WindowsSupport.h | 4 ++++ llvm/lib/Support/Windows/Threading.inc | 13 +++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/llvm/include/llvm/Support/Windows/WindowsSupport.h b/llvm/include/llvm/Support/Windows/WindowsSupport.h index ffc6fdf5b983c..f35e7b55cb8d3 100644 --- a/llvm/include/llvm/Support/Windows/WindowsSupport.h +++ b/llvm/include/llvm/Support/Windows/WindowsSupport.h @@ -245,6 +245,10 @@ LLVM_ABI std::error_code widenPath(const Twine &Path8, SmallVectorImpl &Path16, size_t MaxPathLen = MAX_PATH); +/// Retrieves the handle to a in-memory system module such as ntdll.dll, while +/// ensuring we're not retrieving a malicious injected module but a module +/// loaded from the system path. +LLVM_ABI HMODULE loadSystemModuleSecure(LPCWSTR lpModuleName); } // end namespace windows } // end namespace sys } // end namespace llvm. diff --git a/llvm/lib/Support/Windows/Threading.inc b/llvm/lib/Support/Windows/Threading.inc index 5ef79b340caf1..8dd7c88fad34a 100644 --- a/llvm/lib/Support/Windows/Threading.inc +++ b/llvm/lib/Support/Windows/Threading.inc @@ -106,7 +106,8 @@ void llvm::get_thread_name(SmallVectorImpl &Name) { Name.clear(); } -static HMODULE LoadSystemModuleSecure(LPCWSTR lpModuleName) { +namespace llvm::sys::windows { +HMODULE loadSystemModuleSecure(LPCWSTR lpModuleName) { // Ensure we load indeed a module from system32 path. // As per GetModuleHandle documentation: // "If lpModuleName does not include a path and there is more than one loaded @@ -126,16 +127,17 @@ static HMODULE LoadSystemModuleSecure(LPCWSTR lpModuleName) { } while (Size > Buf.size()); Buf.truncate(Size); - Buf.push_back(TEXT('\\')); + Buf.push_back(L'\\'); Buf.append(lpModuleName, lpModuleName + std::wcslen(lpModuleName)); Buf.push_back(0); return ::GetModuleHandleW(Buf.data()); } +} // namespace llvm::sys::windows SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) { - HMODULE kernelMod = LoadSystemModuleSecure(TEXT("kernel32.dll")); - if (kernelMod) { + HMODULE kernelM = llvm::sys::windows::loadSystemModuleSecure(L"kernel32.dll"); + if (kernelM) { // SetThreadInformation is only available on Windows 8 and later. Since we // still support compilation on Windows 7, we load the function dynamically. typedef BOOL(WINAPI * SetThreadInformation_t)( @@ -143,9 +145,8 @@ SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) { _In_reads_bytes_(ThreadInformationSize) PVOID ThreadInformation, ULONG ThreadInformationSize); static const auto pfnSetThreadInformation = - (SetThreadInformation_t)::GetProcAddress(kernelMod, + (SetThreadInformation_t)::GetProcAddress(kernelM, "SetThreadInformation"); - if (pfnSetThreadInformation) { auto setThreadInformation = [](ULONG ControlMaskAndStateMask) { THREAD_POWER_THROTTLING_STATE state{};