Bug 81387 - UBSAN consumes too much memory at -O2
Summary: UBSAN consumes too much memory at -O2
Alias: None
Product: gcc
Classification: Unclassified
Component: sanitizer (show other bugs)
Version: 8.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
Depends on:
Blocks: yarpgen
  Show dependency treegraph
Reported: 2017-07-11 02:21 UTC by Dmitry Babokin
Modified: 2021-11-01 23:07 UTC (History)
3 users (show)

See Also:
Known to work:
Known to fail:
Last reconfirmed:

reproducer (752.80 KB, application/x-xz)
2017-07-11 02:21 UTC, Dmitry Babokin

Note You need to log in before you can comment on or make changes to this bug.
Description Dmitry Babokin 2017-07-11 02:21:54 UTC
Created attachment 41712 [details]

I understand that compiling with UBSAN should require more memory, but in case of scalar code increase in memory consumption by 50x looks a bit too much.

-O0: 13sec, 0.8Gb
-O2: 5s, 0.4Gb
-O0 -fsanitize=undefined: 100s, 2.7Gb
-O2 -fsanitize=undefined: 255s 42.9Gb

So, it may be useful test case to make sure that memory consumption is reasonable and not beyond expected limits.

Here is stack when memory consumption goes wild:

#0  bitmap_elt_insert_after(bitmap_head*, bitmap_element*, unsigned int) () at ../../gcc/gcc/bitmap.c:409
#1  0x0000000000a6d7cd in bitmap_set_range (head=head@entry=0x249bac630, start=start@entry=0, count=<optimized out>) at ../../gcc/gcc/bitmap.c:1233
#2  0x0000000000a6fd3e in bitmap_set_range (head=head@entry=0x249bac630, start=start@entry=0, count=<optimized out>) at ../../gcc/gcc/bitmap.c:1197
#3  0x0000000000b0b186 in df_mir_alloc(bitmap_head*) () at ../../gcc/gcc/df-problems.c:1913
#4  0x0000000000b05971 in df_analyze_problem (dflow=0xe5f0440, blocks_to_consider=0x5232ed8, postorder=0x27ebe678, n_blocks=120403) at ../../gcc/gcc/df-core.c:1161
#5  0x0000000000b05a7c in df_analyze_1() () at ../../gcc/gcc/df-core.c:1222
#6  0x00000000015d8cbf in find_and_remove_re () at ../../gcc/gcc/ree.c:1216
#7  rest_of_handle_ree () at ../../gcc/gcc/ree.c:1310
#8  (anonymous namespace)::pass_ree::execute(function*) () at ../../gcc/gcc/ree.c:1338
#9  0x0000000000dce3bb in execute_one_pass(opt_pass*) () at ../../gcc/gcc/passes.c:2492
#10 0x0000000000dcec15 in execute_pass_list_1(opt_pass*) () at ../../gcc/gcc/passes.c:2581
#11 0x0000000000dcec27 in execute_pass_list_1(opt_pass*) () at ../../gcc/gcc/passes.c:2582
#12 0x0000000000dcec27 in execute_pass_list_1(opt_pass*) () at ../../gcc/gcc/passes.c:2582
#13 0x0000000000dcec59 in execute_pass_list(function*, opt_pass*) () at ../../gcc/gcc/passes.c:2592
#14 0x0000000000ae11e0 in cgraph_node::expand() () at ../../gcc/gcc/cgraphunit.c:2052
#15 0x0000000000ae2361 in expand_all_functions () at ../../gcc/gcc/cgraphunit.c:2188
#16 symbol_table::compile() [clone .part.54] () at ../../gcc/gcc/cgraphunit.c:2540
#17 0x0000000000ae4807 in compile (this=0x7fb63a52e100) at ../../gcc/gcc/cgraphunit.c:2632
#18 symbol_table::finalize_compilation_unit (this=0x7fb63a52e100) at ../../gcc/gcc/cgraphunit.c:2629
#19 0x0000000000e9c018 in compile_file () at ../../gcc/gcc/toplev.c:493
#20 0x00000000007ffcb6 in do_compile () at ../../gcc/gcc/toplev.c:2021
#21 toplev::main(int, char**) () at ../../gcc/gcc/toplev.c:2155
#22 0x0000000000801cbb in main (argc=15, argv=0x7ffe16a14d48) at ../../gcc/gcc/main.c:39

I don't mind if this bug is closed as "will not be fixed", but such behaviour is definitely make using UBSAN problematic on larger code bases, as it may basically nuke the build system. And I'm not sure if it's UBSAN implementation is to blame or DF analysis consumes more than it should.
Comment 1 Jakub Jelinek 2017-07-11 08:33:14 UTC
I'm afraid it is unfixable, if you want smaller memory consumption, you either need smaller routines, or use -fno-sanitize-recover=all, or do multiple builds with selected subsets of -fsanitize=undefined, so that you don't have that many checks in one function, or use -O1 on the larger functions to avoid many optimizations that are e.g. quadratic in the number of basic blocks.
The UBSAN checks are many and especially if they are recoverable, they increase the number of basic blocks way too much.
Comment 2 Martin Liška 2017-07-11 09:34:17 UTC
Just for curiosity, I tried to use clang++ 4.0.0 and clang++ pr81387.ii -c -O2 -fsanitize=undefined took me about 180s and memory was ~2.5GB.
Comment 3 Dmitry Babokin 2017-07-11 21:25:30 UTC
Interesting that you've mentioned -fno-sanitize-recover, I haven't realized that it has effect on the number of basic blocks. But by default I run "-fsanitize=undefined -fno-sanitize-recover=undefined", so the numbers that I posted are for this combination (-fno-sanitize-recover=all is effectively the same). I've tried without -fno-sanitize-recover=undefined and it is:
-O2 -fsanitize=undefined: 358s, 62Gb
-O1 -fsanitize=undefined: 280s, 2.7Gb

Also clang trunk:
-O2 -fsanitize=undefined: 173s, 1.5Gb
-O2 -fsanitize=undefined -fno-sanitize-recover=undefined: 224s, 1.8Gb

I understand that the reasons for consuming more memory with UBSAN enabled are quite fundamental, but looking at such an extreme increase may uncover problems when algorithms consume more that they supposed to.

Again, I'm not insisting on fixing it, I'm rather drawing attention to suspicious behaviour, which you may consider worth looking at.
Comment 4 Richard Biener 2017-07-17 10:58:48 UTC
There's a dup of this specific issue (workaround: -fno-ree), can't find it right now.
Comment 5 Jakub Jelinek 2017-07-17 11:02:02 UTC
Maybe we should just disable the REE pass if we can predict from number of basic blocks, their sizes or number of edges that DF_UD_CHAIN + DF_DU_CHAIN computation will be really expensive.