Strange assembly output when optimizing string hashing

Viewed 57

when trying to create a compile-time hash macro, it worked but it had its problems. So I thought if the strings are known at compile time(which they are), the whole hashing should get optimized away to a constant. This gcc C99 code with optimization level -O3 enabled:

#include <stdio.h>


int main(void)
{
    char const *const string = "hello";
    int hash = 0;
    for (unsigned char i=0; i < sizeof string; ++i)
    {
        hash += string[i]; //reeaally simple hash :)
    }

    printf("%i", hash);
    return 0;
}

produced the following assembly code:

.LC0:
        .string "hello"
.LC1:
        .string "%i"
main:
        sub     rsp, 8
        movsx   eax, BYTE PTR .LC0[rip+6]
        movsx   edx, BYTE PTR .LC0[rip+7]
        mov     edi, OFFSET FLAT:.LC1
        lea     esi, [rax+532+rdx]
        xor     eax, eax
        call    printf
        xor     eax, eax
        add     rsp, 8
        ret

whilst the same code, I only changed "hello" to "hello w", produces this assembly code, which completely optimized the hashing away:

.LC0:
        .string "%i"
main:
        sub     rsp, 8
        mov     esi, 683
        mov     edi, OFFSET FLAT:.LC0
        xor     eax, eax
        call    printf
        xor     eax, eax
        add     rsp, 8
        ret

Try it yourself

What is the reason? Does this mean I can't use this way of hashing because it may be that the overhead won't get optimized out? How can I make sure there won't be any overhead, what are alternatives?

EDIT 1: I have played around a bit and it seems if the number of chars in the string is 6, it won't get optimized away if the number of chars is 7, it will

1 Answers

sizeof is wrong here. It returns the size of the char pointer not the length of the string.

In your case it is an UB and the compiler cannot optimize it out as you read outside string literal bounds. it is a clang bug not the feature.

if you do it properly gcc will optimize it as well

int main(void)
{
    char const string[] = "hello";
    int hash = 0;
    for (unsigned char i=0; i < sizeof(string); ++i)
    {
        hash += string[i]; //reeaally simple hash :)
    }

    printf("%i", hash);
    return 0;
}

https://godbolt.org/z/YCCNCt

Related