The only safe way to implement strlen involves reading the string one
char at a time. It is faster to read in larger blocks, but this leads to
reading beyond the string boundary, which is undefined behavior. This
patch adds an implementation and flag to use this fast but unsafe
version of strlen.
Details
- Reviewers
sivachandra lntue - Commits
- rGa3b745818d2e: [libc] add unsafe mode to strlen
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
libc/src/string/CMakeLists.txt | ||
---|---|---|
12 | I do not think this will do anything at all without a flag applier implemented somewhere. Also, can we consider a design without the FLAGS feature for comparison? |
libc/src/string/CMakeLists.txt | ||
---|---|---|
12 | To make FLAGS property become a compile flag, you'll need to update _get_common_compile_options at https://github.com/llvm/llvm-project/blob/main/libc/cmake/modules/LLVMLibCObjectRules.cmake#L3 similar to FMA_OPT. In your case the compile option to be append should be -DLLVM_LIBC_STRLEN_UNSAFE for gcc/clang and /DLLVM_LIBC_STRLEN_UNSAFE for MSVC. |
libc/src/string/string_utils.h | ||
---|---|---|
27 | It is set in stone that a byte has 8 bits for all llvm-libc variants? In LLVM there is always somebody with other ideas. |
libc/src/string/CMakeLists.txt | ||
---|---|---|
12 | The setting to use the unsafe path or not has to be done via some config time CMake option. So, there has to be some manual setting somewhere. For most users, we will have a default making the manual setting unnecessary. By, "a design without the FLAGS feature", I mean that we should provide a CMake option which does not go through the FLAGS machinery. |
change from using FLAGS to using a full cmake option. Currently it defaults to off, so if we want to test this on the bots then we'll either need to change the default or adjust the cmake command the bots use.
IIRC, @michaelrj and I had a discussion about how this relates to sanitizers. For LIBC_FAST_UNSAFE_STRLEN could this be the default unless an existing cmake flag is used to build llvm-libc sanitized? (I assume there's an existing cmake flag for denoting that I would like a build of llvm-libc instrumented with *SAN?)
libc/src/string/string_utils.h | ||
---|---|---|
65 | Dunno how similar memchr is to strlen, but I'd bet that the implementations are pretty similar. Maybe the value being searched for could be a parameter, then similar code shared between the two? |
Reading a word vs reading a byte can be employed at places beyond strlen I think. So, we should probably name the flag more generic like, LIBC_FAST_UNSAFE_WORD_READ. Also, we will want the default behavior to be the opposite. As in, it should be OFF by default and should be switched ON explicitly. There is an LLVM CMake flag for sanitizers - -DLLVM_USE_SANITIZER=<...>. I would think this flag and the unsafe word reading flag should not be mixed.
Thanks! How about strchr? Looks like there's a TODO about this in libc/src/string/strchr.cpp?
For fun, can we do anything like this for strrchr? (We're on to the point of diminishing returns here; I only truly care about strlen here).
So slower by default? :( Not my circus, not my monkeys; but I'll take an implementation over no implementation.
libc/CMakeLists.txt | ||
---|---|---|
29 | is strlen here still precise after Diff 478668? | |
libc/src/string/string_utils.h | ||
92 | What requested size? I don't follow what this comment means. Can you expand on it, inline? | |
140 | Is n the "requested size"? If so, care to break out the crayons and be super explicit in the comment? |
libc/CMakeLists.txt | ||
---|---|---|
29 | Move the listing of such tuning parameters to a separate file, say common_tuners.cmake and include it here. When we have tuners specific to certain functions, for example printf related tuners, they will be listed next to the printf implementation as they will not affect LIBC_COMPILER_OPTIONS_DEFAULT. | |
libc/src/string/string_utils.h | ||
65 | Nit: compare with \0. | |
92 | This comment is not addressed yet. | |
99 | Nit: Using the typename Word instead of T improves the readability. | |
121 | "find the match in the block" ? | |
142 | ch_mask is a local var of another function. Can you rephrase this comment so that it will not go stale when the other function changes? | |
144 | Same as above - not sure what this comment is talking about. |
Re tschuett: Performance numbers here: https://quick-bench.com/q/Z7wq6I3K6xUIeAIogIpHpPgBwSw but the TL;DR is that once strings get larger than about 32 bytes it's faster to read a block at a time, and if the string can't be assumed to be word-aligned then it's faster to work with 32 bits (on x64 machines). I've adjusted the code to account for this.
Re nickdesaulniers: I think strchr and strrchr will have to wait for a followup patch, this patch is getting rather large as it is. As for slower by default, yes but it's also less likely to cause issues (e.g. with sanitizers, memory tagging, etc.).
Re sivachandra: I've set up the cmake to enforce wide strlen NAND sanitizers.
OK after addressing the comments which are mostly nits.
libc/cmake/modules/LLVMLibCCommonTuners.cmake | ||
---|---|---|
1 ↗ | (On Diff #479065) | This file is not a CMake infrastructure component so it should live in the libc directory and can have a simple name like common_libc_tuners.cmake. |
7 ↗ | (On Diff #479065) | The earlier name of LIBC_UNSAFE_WORD_READ was more suggestive than this name. Also, I think the description should be more generic instead of referring to strlen and memchr. |
libc/src/string/string_utils.h | ||
94 | s/I/we | |
153 | Nit: I think this unsigned int is to be in sync with the template unsigned int argument. So, define a local type say, using BlockType = unsigned int;. |
address final comments
libc/cmake/modules/LLVMLibCCommonTuners.cmake | ||
---|---|---|
7 ↗ | (On Diff #479065) | I compromised on LIBC_STRING_UNSAFE_WIDE_READ which I think has all the relevant information. |
is strlen here still precise after Diff 478668?