On 26. 5. 26 17:47, Ivan Zhakov wrote:
On Thu, 21 May 2026 at 15:33, Jun Omae <[email protected]> wrote:
On 2026/05/21 21:32, Evgeny Kotkov wrote:
> Jun Omae <[email protected]> writes:
>
>> It occurs only with MSVC toolset v143 (--vsnet-version=2022)
and Whole
>> Program Optimization enabled. It doesn't with v142
(--vsnet-version=2019).
>>
>> svn_fs_fs__delete_revprops_shard() is built by the optimization to
>> delete the "revprops/0" directory even if `shard` argument is 0.
>>
>> Workaround is to disable the optimization.
>
> Interesting. Could be a compiler issue or an UB somewhere in
the code.
>
> Would you mind providing more details on your environment to
make it easier
> to reproduce?
It is able to reproduce using cmake with toolset v143.
1. Pass -vcvars_ver=14.44 to vcvars64.bat
2. Pass CMAKE_INTERPROCEDURAL_OPTIMIZATION=ON to cmake configure
(The option makes Whole Program Optimization enabled in cmake
build)
I can reproduce this issue in my environment and I believe it is
indeed a compiler bug. Here is the behavior across different MSVC
versions:
- MSVC 19.*40*.33821.0: No problem
- MSVC 19.*44*.35227.0: Problem occurs
- MSVC 19.*50*.35730.0: No problem
In version 19.44.35227.0 with /GL (Whole Program Optimization)
enabled, the `if (shard == 0)` branch seems to be incorrrectly
**optimized away**.
I think this is caused by combination of the do-while loop and
the `while (kind == svn_node_dir && to_cleanup > 0)` condition on
the calling side (details below).
The code below from subversion/libsvn_fs_fs/pack.c:synced_pack_shard()
becomes a simple call to svn_io_remove_dir2() that recursively
deletes the revprops/0 directory:
[[[
do
{
SVN_ERR(svn_fs_fs__delete_revprops_shard(revprops_shard_path,
to_cleanup,
ffd->max_files_per_dir,
pb->cancel_func,
pb->cancel_baton,
pool));
/* If the previous shard exists, clean it up as well.
Don't try to clean up shard 0 as it we can't tell quickly
whether it actually needs cleaning up. */
revprops_shard_path = svn_dirent_join(pb->revsprops_dir,
apr_psprintf(pool,"%" APR_INT64_T_FMT, --to_cleanup),
pool);
SVN_ERR(svn_io_check_path(revprops_shard_path, &kind,pool));
}
while (kind ==svn_node_dir && to_cleanup > 0);
]]]
[[[
if (shard == 0)
{
apr_pool_t *iterpool =svn_pool_create(scratch_pool);
int i;
/* delete all files except the one for revision 0 */
for (i = 1; i <max_files_per_dir; ++i)
{
const char *path;
svn_pool_clear(iterpool);
path = svn_dirent_join(shard_path,
apr_psprintf(iterpool,"%d", i),
iterpool);
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
SVN_ERR(svn_io_remove_file2(path,TRUE, iterpool));
}
svn_pool_destroy(iterpool);
}
else
SVN_ERR(svn_io_remove_dir2(shard_path,TRUE,
cancel_func,cancel_baton,scratch_pool));
]]]
I checked that replacing the do-while loop with an equivalent while {}
loop makes the problem go away. My theory is that the compiler
incorrectly assumes to_cleanup is > 0 within the loop block and
optimizes away the inner check.
Could there be an even simpler "fix"? Move the --to_cleanup before the
call to svn_dirent_join?
That shouldn't change the semantics at all, but it would indicate that
the MSVC data flow analysis somehow misses that the decrement in the
function parameter has externally visible side effects. The code is
clearly correct.
-- Brane
I have attached several generated assembly files for comparison:
- svn-disasm-vc19.44-orig.asm - code generated by MSVC 19.44 compiler.
- svn-disasm-vc19.44-patched.asm - code where `do {} while` loop
replaced with `while() {}` equivalent generated by MSVC 19.44 compiler
- svn-disasm-vc19.50-orig.asm - code generated by MSVC 19.50 compiler.
--
Ivan Zhakov