Skip to content

Commit 2423ec5

Browse files
committed
[libc] Add memmove implementation.
Use `memcpy` rather than copying bytes one by one, for there might be large size structs to move. Reviewed By: gchatelet, sivachandra Differential Revision: https://reviews.llvm.org/D93195
1 parent 8f283ca commit 2423ec5

File tree

7 files changed

+176
-0
lines changed

7 files changed

+176
-0
lines changed

libc/config/linux/aarch64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ set(TARGET_LIBC_ENTRYPOINTS
3030
libc.src.string.memchr
3131
libc.src.string.memcmp
3232
libc.src.string.memcpy
33+
libc.src.string.memmove
3334
libc.src.string.memset
3435
libc.src.string.memrchr
3536
libc.src.string.strcat

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ set(TARGET_LIBC_ENTRYPOINTS
5252
libc.src.string.memchr
5353
libc.src.string.memcmp
5454
libc.src.string.memcpy
55+
libc.src.string.memmove
5556
libc.src.string.memrchr
5657
libc.src.string.memset
5758
libc.src.string.strcat

libc/src/string/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,18 @@ add_entrypoint_object(
6666
memcmp.h
6767
)
6868

69+
add_entrypoint_object(
70+
memmove
71+
SRCS
72+
memmove.cpp
73+
HDRS
74+
memmove.h
75+
DEPENDS
76+
libc.include.unistd
77+
libc.src.stdlib.abs_utils
78+
libc.src.string.memcpy
79+
)
80+
6981
add_entrypoint_object(
7082
strchr
7183
SRCS

libc/src/string/memmove.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//===-- Implementation of memmove -----------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/string/memmove.h"
10+
#include "src/__support/common.h"
11+
#include "src/stdlib/abs_utils.h"
12+
#include "src/string/memcpy.h"
13+
#include <stddef.h> // size_t, ptrdiff_t
14+
#include <unistd.h> // ssize_t
15+
16+
namespace __llvm_libc {
17+
18+
// src_m and dest_m might be the beginning or end.
19+
static inline void move_byte(unsigned char *dest_m, const unsigned char *src_m,
20+
size_t count, ssize_t direction) {
21+
for (ssize_t offset = 0; count; --count, offset += direction)
22+
dest_m[offset] = src_m[offset];
23+
}
24+
25+
LLVM_LIBC_FUNCTION(void *, memmove,
26+
(void *dest, const void *src, size_t count)) {
27+
unsigned char *dest_c = reinterpret_cast<unsigned char *>(dest);
28+
const unsigned char *src_c = reinterpret_cast<const unsigned char *>(src);
29+
30+
// If the distance between src_c and dest_c is equal to or greater
31+
// than count (integer_abs(src_c - dest_c) >= count), they would not overlap.
32+
// e.g. greater equal overlapping
33+
// [12345678] [12345678] [12345678]
34+
// src_c: [_ab_____] [_ab_____] [_ab_____]
35+
// dest_c:[_____yz_] [___yz___] [__yz____]
36+
37+
// Use memcpy if src_c and dest_c do not overlap.
38+
if (__llvm_libc::integer_abs(src_c - dest_c) >= static_cast<ptrdiff_t>(count))
39+
return __llvm_libc::memcpy(dest_c, src_c, count);
40+
41+
// Overlap cases.
42+
// If dest_c starts before src_c (dest_c < src_c), copy forward(pointer add 1)
43+
// from beginning to end.
44+
// If dest_c starts after src_c (dest_c > src_c), copy backward(pointer add
45+
// -1) from end to beginning.
46+
// If dest_c and src_c start at the same address (dest_c == src_c),
47+
// just return dest.
48+
// e.g. forward backward
49+
// *--> <--*
50+
// src_c : [___abcde_] [_abcde___]
51+
// dest_c: [_abc--___] [___--cde_]
52+
53+
// TODO: Optimize `move_byte(...)` function.
54+
if (dest_c < src_c)
55+
move_byte(dest_c, src_c, count, /*pointer add*/ 1);
56+
if (dest_c > src_c)
57+
move_byte(dest_c + count - 1, src_c + count - 1, count, /*pointer add*/ -1);
58+
return dest;
59+
}
60+
61+
} // namespace __llvm_libc

libc/src/string/memmove.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===-- Implementation header for memmove -----------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_STRING_MEMMOVE_H
10+
#define LLVM_LIBC_SRC_STRING_MEMMOVE_H
11+
12+
#include <stddef.h> // size_t
13+
14+
namespace __llvm_libc {
15+
16+
void *memmove(void *dest, const void *src, size_t count);
17+
18+
} // namespace __llvm_libc
19+
20+
#endif // LLVM_LIBC_SRC_STRING_MEMMOVE_H

libc/test/src/string/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,17 @@ add_libc_unittest(
6262
libc.src.string.memcmp
6363
)
6464

65+
add_libc_unittest(
66+
memmove_test
67+
SUITE
68+
libc_string_unittests
69+
SRCS
70+
memmove_test.cpp
71+
DEPENDS
72+
libc.src.string.memcmp
73+
libc.src.string.memmove
74+
)
75+
6576
add_libc_unittest(
6677
strchr_test
6778
SUITE

libc/test/src/string/memmove_test.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//===-- Unittests for memmove ---------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/string/memcmp.h"
10+
#include "src/string/memmove.h"
11+
#include "utils/CPP/ArrayRef.h"
12+
#include "utils/UnitTest/Test.h"
13+
14+
class MemmoveTest : public __llvm_libc::testing::Test {
15+
public:
16+
void check_memmove(void *dest, const void *src, size_t count, const void *str,
17+
const __llvm_libc::cpp::ArrayRef<unsigned char> expected) {
18+
void *result = __llvm_libc::memmove(dest, src, count);
19+
// Making sure the pointer returned is same with dest.
20+
EXPECT_EQ(result, dest);
21+
// expected is designed according to str.
22+
// dest and src might be part of str.
23+
// Making sure the str is same with expected.
24+
EXPECT_EQ(__llvm_libc::memcmp(str, expected.data(), expected.size()), 0);
25+
}
26+
};
27+
28+
TEST_F(MemmoveTest, MoveZeroByte) {
29+
unsigned char dest[] = {'a', 'b'};
30+
const unsigned char src[] = {'y', 'z'};
31+
const unsigned char expected[] = {'a', 'b'};
32+
check_memmove(dest, src, 0, dest, expected);
33+
}
34+
35+
TEST_F(MemmoveTest, OverlapThatDestAndSrcPointToSameAddress) {
36+
unsigned char str[] = {'a', 'b'};
37+
const unsigned char expected[] = {'a', 'b'};
38+
check_memmove(str, str, 1, str, expected);
39+
}
40+
41+
TEST_F(MemmoveTest, OverlapThatDestStartsBeforeSrc) {
42+
// Set boundary at beginning and end for not overstepping when
43+
// copy forward or backward.
44+
unsigned char str[] = {'z', 'a', 'b', 'c', 'z'};
45+
const unsigned char expected[] = {'z', 'b', 'c', 'c', 'z'};
46+
// dest is &str[1].
47+
check_memmove(&str[1], &str[2], 2, str, expected);
48+
}
49+
50+
TEST_F(MemmoveTest, OverlapThatDestStartsAfterSrc) {
51+
unsigned char str[] = {'z', 'a', 'b', 'c', 'z'};
52+
const unsigned char expected[] = {'z', 'a', 'a', 'b', 'z'};
53+
check_memmove(&str[2], &str[1], 2, str, expected);
54+
}
55+
56+
// e.g. dest follow src.
57+
// str: [abcdefghij]
58+
// [__src_____]
59+
// [_____dest_]
60+
TEST_F(MemmoveTest, SrcFollowDest) {
61+
unsigned char str[] = {'z', 'a', 'b', 'z'};
62+
const unsigned char expected[] = {'z', 'b', 'b', 'z'};
63+
check_memmove(&str[1], &str[2], 1, str, expected);
64+
}
65+
66+
TEST_F(MemmoveTest, DestFollowSrc) {
67+
unsigned char str[] = {'z', 'a', 'b', 'z'};
68+
const unsigned char expected[] = {'z', 'a', 'a', 'z'};
69+
check_memmove(&str[2], &str[1], 1, str, expected);
70+
}

0 commit comments

Comments
 (0)