Files
UniversalContainer/tools/audit_build_flags.ps1
T
2026-08-13 23:42:26 +03:00

128 lines
5.2 KiB
PowerShell

param(
[Parameter(Mandatory = $true)]
[ValidateSet('scalar', 'baseline', 'avx2')]
[string]$Profile
)
$ErrorActionPreference = 'Stop'
$build = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..\out\build\msvc-$Profile"))
$targets = @('uc_demo', 'uc_tests', 'uc_bench', 'uc_focused_bench')
function Require-Flag(
[string]$Command,
[string]$Pattern,
[string]$Description,
[string]$Context
) {
if ($Command -notmatch $Pattern) {
throw "${Profile}/${Context}: missing $Description"
}
}
function Reject-Flag(
[string]$Command,
[string]$Pattern,
[string]$Description,
[string]$Context
) {
if ($Command -match $Pattern) {
throw "${Profile}/${Context}: unexpected $Description"
}
}
function Assert-ProfileCommand(
[string]$Command,
[string]$Context
) {
$optimizationFlags = @([regex]::Matches(
$Command,
'(?i)(?:^|\s)/(?:O1|O2|Od|Ox)(?=\s|$)'
) | ForEach-Object { $_.Value.Trim() })
if ($optimizationFlags.Count -ne 1 -or $optimizationFlags[0] -ine '/O2') {
$actual = if ($optimizationFlags.Count -eq 0) { 'none' } else { $optimizationFlags -join ', ' }
throw "${Profile}/${Context}: expected exactly /O2, found $actual"
}
$runtimeFlags = @([regex]::Matches(
$Command,
'(?i)(?:^|\s)/(?:MDd?|MTd?)(?=\s|$)'
) | ForEach-Object { $_.Value.Trim() })
if ($runtimeFlags.Count -ne 1 -or $runtimeFlags[0] -ine '/MT') {
$actual = if ($runtimeFlags.Count -eq 0) { 'none' } else { $runtimeFlags -join ', ' }
throw "${Profile}/${Context}: expected exactly /MT, found $actual"
}
Reject-Flag $Command '(?i)(?:^|\s)/GL(?=\s|$)' '/GL (IPO/LTO must be off)' $Context
switch ($Profile) {
'scalar' {
Require-Flag $Command '(?i)(?:^|\s)/d2Qvec-(?=\s|$)' '/d2Qvec-' $Context
Require-Flag $Command '(?i)(?:^|\s)/Qvec-report:1(?=\s|$)' '/Qvec-report:1' $Context
Require-Flag $Command '(?i)(?:^|\s)/Oi-(?=\s|$)' '/Oi-' $Context
Require-Flag $Command '(?i)UC_SIMD_PROFILE_SCALAR=1' 'UC_SIMD_PROFILE_SCALAR=1' $Context
Require-Flag $Command '(?i)_USE_STD_VECTOR_ALGORITHMS=0' '_USE_STD_VECTOR_ALGORITHMS=0' $Context
Reject-Flag $Command '(?i)(?:^|\s)/arch:AVX\S*' '/arch:AVX*' $Context
Reject-Flag $Command '(?i)UC_SIMD_PROFILE_(?:BASELINE|AVX2)=1' 'a foreign SIMD profile definition' $Context
}
'baseline' {
Require-Flag $Command '(?i)UC_SIMD_PROFILE_BASELINE=1' 'UC_SIMD_PROFILE_BASELINE=1' $Context
Reject-Flag $Command '(?i)(?:^|\s)/(?:d2Qvec-|Qvec-)(?=\s|$)' 'vectorizer-disable flag' $Context
Reject-Flag $Command '(?i)(?:^|\s)/Oi-(?=\s|$)' '/Oi-' $Context
Reject-Flag $Command '(?i)(?:^|\s)/arch:AVX\S*' '/arch:AVX*' $Context
Reject-Flag $Command '(?i)_USE_STD_VECTOR_ALGORITHMS=0|UC_SIMD_PROFILE_(?:SCALAR|AVX2)=1' 'a foreign SIMD profile definition' $Context
}
'avx2' {
Require-Flag $Command '(?i)UC_SIMD_PROFILE_AVX2=1' 'UC_SIMD_PROFILE_AVX2=1' $Context
Reject-Flag $Command '(?i)(?:^|\s)/(?:d2Qvec-|Qvec-)(?=\s|$)' 'vectorizer-disable flag' $Context
Reject-Flag $Command '(?i)(?:^|\s)/Oi-(?=\s|$)' '/Oi-' $Context
Reject-Flag $Command '(?i)_USE_STD_VECTOR_ALGORITHMS=0|UC_SIMD_PROFILE_(?:SCALAR|BASELINE)=1' 'a foreign SIMD profile definition' $Context
$architectureFlags = @([regex]::Matches(
$Command,
'(?i)(?:^|\s)/arch:\S+'
) | ForEach-Object { $_.Value.Trim() })
if ($architectureFlags.Count -ne 1 -or $architectureFlags[0] -ine '/arch:AVX2') {
$actual = if ($architectureFlags.Count -eq 0) {
'none'
} else {
$architectureFlags -join ', '
}
throw "${Profile}/${Context}: expected exactly /arch:AVX2, found $actual"
}
}
}
}
$validatedCommands = 0
foreach ($target in $targets) {
$releaseDirectory = Join-Path $build "$target.dir\Release"
if (-not (Test-Path -LiteralPath $releaseDirectory -PathType Container)) {
throw "${Profile}/${target}: missing Release build directory $releaseDirectory"
}
$logs = @(Get-ChildItem -LiteralPath $releaseDirectory -Recurse `
-Filter 'CL.command.1.tlog' -File)
if ($logs.Count -ne 1) {
throw "${Profile}/${target}: expected one compiler command log, found $($logs.Count)"
}
# MSBuild writes command tlogs as UTF-16LE without a reliable BOM.
$commandLines = @(Get-Content -LiteralPath $logs[0].FullName -Encoding Unicode |
Where-Object { $_ -and -not $_.StartsWith('^') })
if ($commandLines.Count -eq 0) {
throw "${Profile}/${target}: compiler command log contains no commands"
}
for ($index = 0; $index -lt $commandLines.Count; ++$index) {
$context = if ($commandLines.Count -eq 1) {
$target
} else {
"$target command $($index + 1)"
}
Assert-ProfileCommand $commandLines[$index] $context
++$validatedCommands
}
}
Write-Host "Build flag audit passed for ${Profile}: $validatedCommands Release compiler command(s) across $($targets.Count) targets."