| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| //===--- clock_gettime linux implementation ---------------------*- C++ -*-===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "src/__support/time/linux/clock_gettime.h" | ||
| #include "hdr/types/clockid_t.h" | ||
| #include "hdr/types/struct_timespec.h" | ||
| #include "src/__support/OSUtil/linux/vdso.h" | ||
| #include "src/__support/OSUtil/syscall.h" | ||
| #include "src/__support/common.h" | ||
| #include "src/__support/error_or.h" | ||
| #include "src/__support/macros/config.h" | ||
| #include <sys/syscall.h> | ||
|
|
||
| #if defined(SYS_clock_gettime64) | ||
| #include <linux/time_types.h> | ||
| #endif | ||
|
|
||
| namespace LIBC_NAMESPACE_DECL { | ||
| namespace internal { | ||
| ErrorOr<int> clock_gettime(clockid_t clockid, timespec *ts) { | ||
| using namespace vdso; | ||
| int ret; | ||
| #if defined(SYS_clock_gettime) | ||
| TypedSymbol<VDSOSym::ClockGetTime> clock_gettime; | ||
| if (LIBC_LIKELY(clock_gettime != nullptr)) | ||
| ret = clock_gettime(clockid, ts); | ||
| else | ||
| ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_gettime, | ||
| static_cast<long>(clockid), | ||
| reinterpret_cast<long>(ts)); | ||
| #elif defined(SYS_clock_gettime64) | ||
| static_assert( | ||
| sizeof(time_t) == sizeof(int64_t), | ||
| "SYS_clock_gettime64 requires struct timespec with 64-bit members."); | ||
|
|
||
| TypedSymbol<VDSOSym::ClockGetTime64> clock_gettime64; | ||
| __kernel_timespec ts64{}; | ||
| if (LIBC_LIKELY(clock_gettime64 != nullptr)) | ||
| ret = clock_gettime64(clockid, &ts64); | ||
| else | ||
| ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_gettime64, | ||
| static_cast<long>(clockid), | ||
| reinterpret_cast<long>(&ts64)); | ||
| if (ret == 0) { | ||
| ts->tv_sec = static_cast<decltype(ts->tv_sec)>(ts64.tv_sec); | ||
| ts->tv_nsec = static_cast<decltype(ts->tv_nsec)>(ts64.tv_nsec); | ||
| } | ||
| #else | ||
| #error "SYS_clock_gettime and SYS_clock_gettime64 syscalls not available." | ||
| #endif | ||
| if (ret < 0) | ||
| return Error(-ret); | ||
| return ret; | ||
| } | ||
|
|
||
| } // namespace internal | ||
| } // namespace LIBC_NAMESPACE_DECL |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| //===-- Unittests for a freestore -------------------------------*- C++ -*-===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include <stddef.h> | ||
|
|
||
| #include "src/__support/freestore.h" | ||
| #include "test/UnitTest/Test.h" | ||
|
|
||
| using LIBC_NAMESPACE::Block; | ||
| using LIBC_NAMESPACE::FreeList; | ||
| using LIBC_NAMESPACE::FreeStore; | ||
| using LIBC_NAMESPACE::FreeTrie; | ||
| using LIBC_NAMESPACE::cpp::byte; | ||
| using LIBC_NAMESPACE::cpp::optional; | ||
|
|
||
| // Inserting or removing blocks too small to be tracked does nothing. | ||
| TEST(LlvmLibcFreeStore, TooSmall) { | ||
| byte mem[1024]; | ||
| optional<Block<> *> maybeBlock = Block<>::init(mem); | ||
| ASSERT_TRUE(maybeBlock.has_value()); | ||
| Block<> *too_small = *maybeBlock; | ||
| maybeBlock = too_small->split(sizeof(Block<>::offset_type)); | ||
| ASSERT_TRUE(maybeBlock.has_value()); | ||
| Block<> *remainder = *maybeBlock; | ||
|
|
||
| FreeStore store; | ||
| store.set_range({0, 4096}); | ||
| store.insert(too_small); | ||
| store.insert(remainder); | ||
|
|
||
| EXPECT_EQ(store.remove_best_fit(too_small->inner_size()), remainder); | ||
| store.remove(too_small); | ||
| } | ||
|
|
||
| TEST(LlvmLibcFreeStore, RemoveBestFit) { | ||
| byte mem[1024]; | ||
| optional<Block<> *> maybeBlock = Block<>::init(mem); | ||
| ASSERT_TRUE(maybeBlock.has_value()); | ||
|
|
||
| Block<> *smallest = *maybeBlock; | ||
| maybeBlock = | ||
| smallest->split(sizeof(FreeList::Node) + sizeof(Block<>::offset_type)); | ||
| ASSERT_TRUE(maybeBlock.has_value()); | ||
|
|
||
| Block<> *largest_small = *maybeBlock; | ||
| maybeBlock = | ||
| largest_small->split(sizeof(FreeTrie::Node) + | ||
| sizeof(Block<>::offset_type) - alignof(max_align_t)); | ||
| ASSERT_TRUE(maybeBlock.has_value()); | ||
| if (largest_small->inner_size() == smallest->inner_size()) | ||
| largest_small = smallest; | ||
| ASSERT_GE(largest_small->inner_size(), smallest->inner_size()); | ||
|
|
||
| Block<> *remainder = *maybeBlock; | ||
|
|
||
| FreeStore store; | ||
| store.set_range({0, 4096}); | ||
| store.insert(smallest); | ||
| if (largest_small != smallest) | ||
| store.insert(largest_small); | ||
| store.insert(remainder); | ||
|
|
||
| // Find exact match for smallest. | ||
| ASSERT_EQ(store.remove_best_fit(smallest->inner_size()), smallest); | ||
| store.insert(smallest); | ||
|
|
||
| // Find exact match for largest. | ||
| ASSERT_EQ(store.remove_best_fit(largest_small->inner_size()), largest_small); | ||
| store.insert(largest_small); | ||
|
|
||
| // Search small list for best fit. | ||
| Block<> *next_smallest = | ||
| largest_small == smallest ? remainder : largest_small; | ||
| ASSERT_EQ(store.remove_best_fit(smallest->inner_size() + 1), next_smallest); | ||
| store.insert(next_smallest); | ||
|
|
||
| // Continue search for best fit to large blocks. | ||
| EXPECT_EQ(store.remove_best_fit(largest_small->inner_size() + 1), remainder); | ||
| } | ||
|
|
||
| TEST(LlvmLibcFreeStore, Remove) { | ||
| byte mem[1024]; | ||
| optional<Block<> *> maybeBlock = Block<>::init(mem); | ||
| ASSERT_TRUE(maybeBlock.has_value()); | ||
|
|
||
| Block<> *small = *maybeBlock; | ||
| maybeBlock = | ||
| small->split(sizeof(FreeList::Node) + sizeof(Block<>::offset_type)); | ||
| ASSERT_TRUE(maybeBlock.has_value()); | ||
|
|
||
| Block<> *remainder = *maybeBlock; | ||
|
|
||
| FreeStore store; | ||
| store.set_range({0, 4096}); | ||
| store.insert(small); | ||
| store.insert(remainder); | ||
|
|
||
| store.remove(remainder); | ||
| ASSERT_EQ(store.remove_best_fit(remainder->inner_size()), | ||
| static_cast<Block<> *>(nullptr)); | ||
| store.remove(small); | ||
| ASSERT_EQ(store.remove_best_fit(small->inner_size()), | ||
| static_cast<Block<> *>(nullptr)); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| //===-- Unittests for a freetrie --------------------------------*- C++ -*-===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include <stddef.h> | ||
|
|
||
| #include "src/__support/freetrie.h" | ||
| #include "test/UnitTest/Test.h" | ||
|
|
||
| using LIBC_NAMESPACE::Block; | ||
| using LIBC_NAMESPACE::FreeTrie; | ||
| using LIBC_NAMESPACE::cpp::byte; | ||
| using LIBC_NAMESPACE::cpp::optional; | ||
|
|
||
| TEST(LlvmLibcFreeTrie, FindBestFitRoot) { | ||
| FreeTrie trie({0, 4096}); | ||
| EXPECT_EQ(trie.find_best_fit(123), static_cast<FreeTrie::Node *>(nullptr)); | ||
|
|
||
| byte mem[1024]; | ||
| optional<Block<> *> maybeBlock = Block<>::init(mem); | ||
| ASSERT_TRUE(maybeBlock.has_value()); | ||
| Block<> *block = *maybeBlock; | ||
| trie.push(block); | ||
|
|
||
| FreeTrie::Node *root = trie.find_best_fit(0); | ||
| ASSERT_EQ(root->block(), block); | ||
| EXPECT_EQ(trie.find_best_fit(block->inner_size() - 1), root); | ||
| EXPECT_EQ(trie.find_best_fit(block->inner_size()), root); | ||
| EXPECT_EQ(trie.find_best_fit(block->inner_size() + 1), | ||
| static_cast<FreeTrie::Node *>(nullptr)); | ||
| EXPECT_EQ(trie.find_best_fit(4095), static_cast<FreeTrie::Node *>(nullptr)); | ||
| } | ||
|
|
||
| TEST(LlvmLibcFreeTrie, FindBestFitLower) { | ||
| byte mem[4096]; | ||
| optional<Block<> *> maybeBlock = Block<>::init(mem); | ||
| ASSERT_TRUE(maybeBlock.has_value()); | ||
| Block<> *lower = *maybeBlock; | ||
| maybeBlock = lower->split(512); | ||
| ASSERT_TRUE(maybeBlock.has_value()); | ||
| Block<> *root = *maybeBlock; | ||
|
|
||
| FreeTrie trie({0, 4096}); | ||
| trie.push(root); | ||
| trie.push(lower); | ||
|
|
||
| EXPECT_EQ(trie.find_best_fit(0)->block(), lower); | ||
| } | ||
|
|
||
| TEST(LlvmLibcFreeTrie, FindBestFitUpper) { | ||
| byte mem[4096]; | ||
| optional<Block<> *> maybeBlock = Block<>::init(mem); | ||
| ASSERT_TRUE(maybeBlock.has_value()); | ||
| Block<> *root = *maybeBlock; | ||
| maybeBlock = root->split(512); | ||
| ASSERT_TRUE(maybeBlock.has_value()); | ||
| Block<> *upper = *maybeBlock; | ||
|
|
||
| FreeTrie trie({0, 4096}); | ||
| trie.push(root); | ||
| trie.push(upper); | ||
|
|
||
| EXPECT_EQ(trie.find_best_fit(root->inner_size() + 1)->block(), upper); | ||
| // The upper subtrie should be skipped if it could not contain a better fit. | ||
| EXPECT_EQ(trie.find_best_fit(root->inner_size() - 1)->block(), root); | ||
| } | ||
|
|
||
| TEST(LlvmLibcFreeTrie, FindBestFitLowerAndUpper) { | ||
| byte mem[4096]; | ||
| optional<Block<> *> maybeBlock = Block<>::init(mem); | ||
| ASSERT_TRUE(maybeBlock.has_value()); | ||
| Block<> *root = *maybeBlock; | ||
| maybeBlock = root->split(1024); | ||
| ASSERT_TRUE(maybeBlock.has_value()); | ||
| Block<> *lower = *maybeBlock; | ||
| maybeBlock = lower->split(128); | ||
| ASSERT_TRUE(maybeBlock.has_value()); | ||
| Block<> *upper = *maybeBlock; | ||
|
|
||
| FreeTrie trie({0, 4096}); | ||
| trie.push(root); | ||
| trie.push(lower); | ||
| trie.push(upper); | ||
|
|
||
| // The lower subtrie is examined first. | ||
| EXPECT_EQ(trie.find_best_fit(0)->block(), lower); | ||
| // The upper subtrie is examined if there are no fits found in the upper | ||
| // subtrie. | ||
| EXPECT_EQ(trie.find_best_fit(2048)->block(), upper); | ||
| } | ||
|
|
||
| TEST(LlvmLibcFreeTrie, Remove) { | ||
| byte mem[4096]; | ||
| optional<Block<> *> maybeBlock = Block<>::init(mem); | ||
| ASSERT_TRUE(maybeBlock.has_value()); | ||
| Block<> *small1 = *maybeBlock; | ||
| maybeBlock = small1->split(512); | ||
| ASSERT_TRUE(maybeBlock.has_value()); | ||
| Block<> *small2 = *maybeBlock; | ||
| maybeBlock = small2->split(512); | ||
| ASSERT_TRUE(maybeBlock.has_value()); | ||
| ASSERT_EQ(small1->inner_size(), small2->inner_size()); | ||
| Block<> *large = *maybeBlock; | ||
|
|
||
| // Removing the root empties the trie. | ||
| FreeTrie trie({0, 4096}); | ||
| trie.push(large); | ||
| FreeTrie::Node *large_node = trie.find_best_fit(0); | ||
| ASSERT_EQ(large_node->block(), large); | ||
| trie.remove(large_node); | ||
| ASSERT_TRUE(trie.empty()); | ||
|
|
||
| // Removing the head of a trie list preserves the trie structure. | ||
| trie.push(small1); | ||
| trie.push(small2); | ||
| trie.push(large); | ||
| trie.remove(trie.find_best_fit(small1->inner_size())); | ||
| EXPECT_EQ(trie.find_best_fit(large->inner_size())->block(), large); | ||
| trie.remove(trie.find_best_fit(small1->inner_size())); | ||
| EXPECT_EQ(trie.find_best_fit(large->inner_size())->block(), large); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| ## Test that inline function resolution works when the function has been split | ||
| ## into multiple discontinuous parts (and those parts are placed in different | ||
| ## sections) | ||
|
|
||
| # RUN: llvm-mc -triple x86_64-pc-linux -filetype=obj %s -o %t | ||
| # RUN: %lldb %t -o "image lookup -v -n look_me_up" -o exit | FileCheck %s | ||
|
|
||
| # CHECK: 1 match found in {{.*}} | ||
| # CHECK: Summary: {{.*}}`foo + 6 [inlined] foo_inl + 1 | ||
| # CHECK-NEXT: {{.*}}`foo + 5 | ||
| # CHECK: Blocks: id = {{.*}}, ranges = [0x00000000-0x00000003)[0x00000004-0x00000008) | ||
| # CHECK-NEXT: id = {{.*}}, ranges = [0x00000001-0x00000002)[0x00000005-0x00000007), name = "foo_inl" | ||
|
|
||
| .text | ||
|
|
||
| .type foo,@function | ||
| foo: | ||
| nop | ||
| .Lfoo_inl: | ||
| nop | ||
| .Lfoo_inl_end: | ||
| nop | ||
| .Lfoo_end: | ||
| .size foo, .Lfoo_end-foo | ||
|
|
||
| bar: | ||
| nop | ||
| .Lbar_end: | ||
| .size bar, .Lbar_end-bar | ||
|
|
||
| .section .text.__part1,"ax",@progbits | ||
| foo.__part.1: | ||
| nop | ||
| .Lfoo_inl.__part.1: | ||
| nop | ||
| .type look_me_up,@function | ||
| look_me_up: | ||
| nop | ||
| .Lfoo_inl.__part.1_end: | ||
| nop | ||
| .Lfoo.__part.1_end: | ||
| .size foo.__part.1, .Lfoo.__part.1_end-foo.__part.1 | ||
|
|
||
|
|
||
| .section .debug_abbrev,"",@progbits | ||
| .byte 1 # Abbreviation Code | ||
| .byte 17 # DW_TAG_compile_unit | ||
| .byte 1 # DW_CHILDREN_yes | ||
| .byte 37 # DW_AT_producer | ||
| .byte 8 # DW_FORM_string | ||
| .byte 19 # DW_AT_language | ||
| .byte 5 # DW_FORM_data2 | ||
| .byte 17 # DW_AT_low_pc | ||
| .byte 1 # DW_FORM_addr | ||
| .byte 85 # DW_AT_ranges | ||
| .byte 35 # DW_FORM_rnglistx | ||
| .byte 116 # DW_AT_rnglists_base | ||
| .byte 23 # DW_FORM_sec_offset | ||
| .byte 0 # EOM(1) | ||
| .byte 0 # EOM(2) | ||
| .byte 2 # Abbreviation Code | ||
| .byte 46 # DW_TAG_subprogram | ||
| .byte 0 # DW_CHILDREN_no | ||
| .byte 3 # DW_AT_name | ||
| .byte 8 # DW_FORM_string | ||
| .byte 0 # EOM(1) | ||
| .byte 0 # EOM(2) | ||
| .byte 3 # Abbreviation Code | ||
| .byte 46 # DW_TAG_subprogram | ||
| .byte 1 # DW_CHILDREN_yes | ||
| .byte 85 # DW_AT_ranges | ||
| .byte 35 # DW_FORM_rnglistx | ||
| .byte 3 # DW_AT_name | ||
| .byte 8 # DW_FORM_string | ||
| .byte 0 # EOM(1) | ||
| .byte 0 # EOM(2) | ||
| .byte 4 # Abbreviation Code | ||
| .byte 29 # DW_TAG_inlined_subroutine | ||
| .byte 0 # DW_CHILDREN_no | ||
| .byte 85 # DW_AT_ranges | ||
| .byte 35 # DW_FORM_rnglistx | ||
| .byte 49 # DW_AT_abstract_origin | ||
| .byte 19 # DW_FORM_ref4 | ||
| .byte 0 # EOM(1) | ||
| .byte 0 # EOM(2) | ||
| .byte 0 # EOM(3) | ||
|
|
||
| .section .debug_info,"",@progbits | ||
| .Lcu_begin0: | ||
| .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit | ||
| .Ldebug_info_start0: | ||
| .short 5 # DWARF version number | ||
| .byte 1 # DWARF Unit Type | ||
| .byte 8 # Address Size (in bytes) | ||
| .long .debug_abbrev # Offset Into Abbrev. Section | ||
| .byte 1 # Abbrev DW_TAG_compile_unit | ||
| .asciz "Hand-written DWARF" # DW_AT_producer | ||
| .short 29 # DW_AT_language | ||
| .quad 0 # DW_AT_low_pc | ||
| .byte 1 # DW_AT_ranges | ||
| .long .Lrnglists_table_base0 # DW_AT_rnglists_base | ||
|
|
||
| .byte 3 # Abbrev DW_TAG_subprogram | ||
| .byte 2 # DW_AT_ranges | ||
| .asciz "bar" # DW_AT_name | ||
| .byte 0 # End Of Children Mark | ||
|
|
||
| .Lfoo_inl_die: | ||
| .byte 2 # Abbrev DW_TAG_subprogram | ||
| .asciz "foo_inl" # DW_AT_name | ||
|
|
||
| .byte 3 # Abbrev DW_TAG_subprogram | ||
| .byte 0 # DW_AT_ranges | ||
| .asciz "foo" # DW_AT_name | ||
| .byte 4 # Abbrev DW_TAG_inlined_subroutine | ||
| .byte 3 # DW_AT_ranges | ||
| .long .Lfoo_inl_die-.Lcu_begin0 # DW_AT_abstract_origin | ||
| .byte 0 # End Of Children Mark | ||
|
|
||
| .byte 0 # End Of Children Mark | ||
| .Ldebug_info_end0: | ||
|
|
||
| .section .debug_rnglists,"",@progbits | ||
| .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length | ||
| .Ldebug_list_header_start0: | ||
| .short 5 # Version | ||
| .byte 8 # Address size | ||
| .byte 0 # Segment selector size | ||
| .long 4 # Offset entry count | ||
| .Lrnglists_table_base0: | ||
| .long .Ldebug_ranges0-.Lrnglists_table_base0 | ||
| .long .Ldebug_ranges1-.Lrnglists_table_base0 | ||
| .long .Ldebug_ranges2-.Lrnglists_table_base0 | ||
| .long .Ldebug_ranges3-.Lrnglists_table_base0 | ||
| .Ldebug_ranges0: | ||
| .byte 6 # DW_RLE_start_end | ||
| .quad foo | ||
| .quad .Lfoo_end | ||
| .byte 6 # DW_RLE_start_end | ||
| .quad foo.__part.1 | ||
| .quad .Lfoo.__part.1_end | ||
| .byte 0 # DW_RLE_end_of_list | ||
| .Ldebug_ranges1: | ||
| .byte 6 # DW_RLE_start_end | ||
| .quad bar | ||
| .quad .Lbar_end | ||
| .byte 6 # DW_RLE_start_end | ||
| .quad foo.__part.1 | ||
| .quad .Lfoo.__part.1_end | ||
| .byte 6 # DW_RLE_start_end | ||
| .quad foo | ||
| .quad .Lfoo_end | ||
| .byte 0 # DW_RLE_end_of_list | ||
| .Ldebug_ranges2: | ||
| .byte 6 # DW_RLE_start_end | ||
| .quad bar | ||
| .quad .Lbar_end | ||
| .byte 0 # DW_RLE_end_of_list | ||
| .Ldebug_ranges3: | ||
| .byte 6 # DW_RLE_start_end | ||
| .quad .Lfoo_inl | ||
| .quad .Lfoo_inl_end | ||
| .byte 6 # DW_RLE_start_end | ||
| .quad .Lfoo_inl.__part.1 | ||
| .quad .Lfoo_inl.__part.1_end | ||
| .byte 0 # DW_RLE_end_of_list | ||
| .Ldebug_list_header_end0: |