An example of how GCC 13 optimizes std::memcpy

C++ code below demonstrates how GCC 13 optimizes std::memcpy and a temporary std::array:

#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <new>
#include <type_traits>

template <class T>
T* startLifetimeAs(void* const buffer)
{
    std::array<std::byte, sizeof(T)> representation;

    // Preserve the bytes before placement new ends the previous lifetime.
    std::memcpy(representation.data(), buffer, representation.size());

    // Start the lifetime of T in the original storage.
    T* const result = ::new (buffer) T;

    // Initialize the object representation of the newly created T.
    std::memcpy(result, representation.data(), representation.size());

    return result;
}
int main()
{
    constexpr std::uint32_t expected = 0x12345678;

    alignas(std::uint32_t)
        std::byte buffer[sizeof(std::uint32_t)];

    // Simulate bytes received from an external source.
    std::memcpy(buffer, &expected, sizeof(expected));

    std::uint32_t* const value =
        startLifetimeAs<std::uint32_t>(buffer);

    std::cout
        << "Value: 0x"
        << std::hex
        << std::setfill('0')
        << std::setw(8)
        << *value
        << '\n';

    return *value == expected ? 0 : 1;
}

Compile options:

g++ -std=c++23 \
    -O2 \
    -march=native \
    -Wall -Wextra \
    example.cpp -o example

GCC 13.3 generates the following code:

        .globl std::ios_base_library_init()
.LC0:
        .string "Value: 0x"
main:
        push    r12
        mov     esi, OFFSET FLAT:.LC0
        mov     edi, OFFSET FLAT:std::cout
        push    rbp
        push    rbx
        sub     rsp, 16
        call    std::basic_ostream<char, std::char_traits<char>>& std::operator<<<std::char_traits<char>>(std::basic_ostream<char, std::char_traits<char>>&, char const*)
        mov     rdx, QWORD PTR [rax]
        mov     rbp, rax
        mov     rbx, QWORD PTR [rdx-24]
        add     rbx, rax
        mov     eax, DWORD PTR [rbx+24]
        and     eax, -75
        or      eax, 8
        cmp     BYTE PTR [rbx+225], 0
        mov     DWORD PTR [rbx+24], eax
        mov     rax, rbx
        je      .L18
.L4:
        mov     BYTE PTR [rbx+224], 48
        mov     rdi, rbp
        mov     esi, 305419896
        mov     QWORD PTR [rax+16], 8
        call    std::ostream& std::ostream::_M_insert<unsigned long>(unsigned long)
        mov     BYTE PTR [rsp+15], 10
        mov     rdi, rax
        mov     rax, QWORD PTR [rax]
        mov     rax, QWORD PTR [rax-24]
        cmp     QWORD PTR [rdi+16+rax], 0
        jne     .L19
        mov     esi, 10
        call    std::ostream::put(char)
.L10:
        add     rsp, 16
        xor     eax, eax
        pop     rbx
        pop     rbp
        pop     r12
        ret
.L19:
        mov     edx, 1
        lea     rsi, [rsp+15]
        call    std::basic_ostream<char, std::char_traits<char>>& std::__ostream_insert<char, std::char_traits<char>>(std::basic_ostream<char, std::char_traits<char>>&, char const*, long)
        jmp     .L10
.L18:
        mov     r12, QWORD PTR [rbx+240]
        test    r12, r12
        je      .L20
        cmp     BYTE PTR [r12+56], 0
        je      .L21
.L7:
        mov     rax, QWORD PTR [rdx-24]
        mov     BYTE PTR [rbx+225], 1
        add     rax, rbp
        jmp     .L4
.L21:
        mov     rdi, r12
        call    std::ctype<char>::_M_widen_init() const
        mov     rax, QWORD PTR [r12]
        mov     rax, QWORD PTR [rax+48]
        cmp     rax, OFFSET FLAT:_ZNKSt5ctypeIcE8do_widenEc
        jne     .L14
.L17:
        mov     rdx, QWORD PTR [rbp+0]
        jmp     .L7
.L14:
        mov     esi, 32
        mov     rdi, r12
        call    rax
        jmp     .L17
.L20:
        call    std::__throw_bad_cast()

Leave a Reply

Your email address will not be published. Required fields are marked *