Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions openmp/runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@ endif()

add_subdirectory(src)
add_subdirectory(test)
add_subdirectory(unittests)

# make these variables available for tools:
set(LIBOMP_LIBRARY_DIR ${LIBOMP_LIBRARY_DIR} PARENT_SCOPE)
Expand Down
13 changes: 11 additions & 2 deletions openmp/runtime/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -174,17 +174,26 @@ if(NOT WIN32)
endif()

# Add the OpenMP library

# First, create an OBJECT library with all the runtime sources.
# This allows both the main library and unit tests to link against the same compiled objects.
add_library(omp_objects OBJECT ${LIBOMP_SOURCE_FILES})
set_property(TARGET omp_objects PROPERTY FOLDER "OpenMP/Libraries")
set_property(TARGET omp_objects PROPERTY POSITION_INDEPENDENT_CODE ON)
# Export the omp_objects target so unittests can use it
set(LIBOMP_OBJECTS_TARGET omp_objects PARENT_SCOPE)

libomp_get_ldflags(LIBOMP_CONFIGURED_LDFLAGS)

libomp_get_libflags(LIBOMP_CONFIGURED_LIBFLAGS)
# Build libomp library. Add LLVMSupport dependency if building in-tree with libomptarget profiling enabled.
if(OPENMP_STANDALONE_BUILD OR (NOT OPENMP_ENABLE_LIBOMP_PROFILING))
add_library(omp ${LIBOMP_LIBRARY_KIND} ${LIBOMP_SOURCE_FILES})
add_library(omp ${LIBOMP_LIBRARY_KIND} $<TARGET_OBJECTS:omp_objects>)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[not a change request] According to the documentation, you can also

target_link_libraries(omp PUBLIC omp_objects)

I had mixed success with it; keep $<TARGET_OBJECTS:omp_objects> if it works.

set_property(TARGET omp PROPERTY FOLDER "OpenMP/Libraries")
# Linking command will include libraries in LIBOMP_CONFIGURED_LIBFLAGS
target_link_libraries(omp ${LIBOMP_CONFIGURED_LIBFLAGS} ${LIBOMP_DL_LIBS})
else()
add_llvm_library(omp ${LIBOMP_LIBRARY_KIND} ${LIBOMP_SOURCE_FILES} PARTIAL_SOURCES_INTENDED
add_llvm_library(omp ${LIBOMP_LIBRARY_KIND} $<TARGET_OBJECTS:omp_objects> PARTIAL_SOURCES_INTENDED
LINK_LIBS ${LIBOMP_CONFIGURED_LIBFLAGS} ${LIBOMP_DL_LIBS}
LINK_COMPONENTS Support
BUILDTREE_ONLY
Expand Down
39 changes: 39 additions & 0 deletions openmp/runtime/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
add_custom_target(OpenMPUnitTests)
set_target_properties(OpenMPUnitTests PROPERTIES FOLDER "OpenMP/Tests")

if (NOT TARGET llvm_gtest)
message(WARNING "OpenMP unittests disabled due to GTest being unavailable; "
"Try LLVM_INSTALL_GTEST=ON for the LLVM build")
return ()
endif ()

function(add_openmp_unittest test_dirname)
add_unittest(OpenMPUnitTests ${test_dirname} ${ARGN})

# Link against the object library created in runtime/src
target_link_libraries(${test_dirname} PRIVATE
$<TARGET_OBJECTS:omp_objects>
)

target_include_directories(${test_dirname} PRIVATE
${LIBOMP_INCLUDE_DIR}
${LIBOMP_SRC_DIR}
)
endfunction()

configure_lit_site_cfg(
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in
${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg.py
MAIN_CONFIG
${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py
)

add_openmp_testsuite(
check-libomp-unit
"Running libomp unit tests"
${CMAKE_CURRENT_BINARY_DIR}
EXCLUDE_FROM_CHECK_ALL
DEPENDS OpenMPUnitTests omp
)

add_subdirectory(src)
7 changes: 7 additions & 0 deletions openmp/runtime/unittests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# libomp Unit Tests

Usage:
```
cd <your-llvm-build-directory>/runtimes/runtimes-bins
ninja check-libomp-unit
```
22 changes: 22 additions & 0 deletions openmp/runtime/unittests/lit.cfg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# -*- Python -*-

# Configuration file for the 'lit' test runner.

import os
import subprocess

import lit.formats

# name: The name of this test suite.
config.name = "OpenMP-Unit"

# suffixes: A list of file extensions to treat as test files.
config.suffixes = []

# test_source_root: The root path where tests are located.
# test_exec_root: The root path where tests should be run.
config.test_exec_root = config.openmp_unittests_dir
config.test_source_root = config.test_exec_root

# testFormat: The test format to use to interpret tests.
config.test_format = lit.formats.GoogleTest(config.llvm_build_mode, ".unittests")
9 changes: 9 additions & 0 deletions openmp/runtime/unittests/lit.site.cfg.py.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@AUTO_GEN_COMMENT@

config.library_dir = "@LIBOMP_LIBRARY_DIR@"
config.openmp_unittests_dir = "@CMAKE_CURRENT_BINARY_DIR@"
config.llvm_build_mode = lit_config.substitute("@LLVM_BUILD_MODE@")

# Let the main config do the real work.
lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg.py")

3 changes: 3 additions & 0 deletions openmp/runtime/unittests/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
add_openmp_unittest(libomp.unittests
TestKmpStr.cpp
)
239 changes: 239 additions & 0 deletions openmp/runtime/unittests/src/TestKmpStr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
//===- TestKmpStr.cpp - Tests for kmp_str utilities ----------------------===//
//
// 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 "kmp_str.h"
#include "gtest/gtest.h"
#include <cstring>

namespace {

// Test basic string buffer initialization
TEST(KmpStrTest, BufferInit) {
kmp_str_buf_t buffer;
__kmp_str_buf_init(&buffer);

EXPECT_NE(buffer.str, nullptr);
EXPECT_GT(buffer.size, 0u);
EXPECT_EQ(buffer.used, 0);
EXPECT_EQ(buffer.str[0], '\0');
}

// Test string buffer clear
TEST(KmpStrTest, BufferClear) {
kmp_str_buf_t buffer;
__kmp_str_buf_init(&buffer);
__kmp_str_buf_print(&buffer, "test string");

EXPECT_GT(buffer.used, 0);

__kmp_str_buf_clear(&buffer);
EXPECT_EQ(buffer.used, 0);
EXPECT_EQ(buffer.str[0], '\0');

__kmp_str_buf_free(&buffer);
}

// Test string buffer print
TEST(KmpStrTest, BufferPrint) {
kmp_str_buf_t buffer;
__kmp_str_buf_init(&buffer);

__kmp_str_buf_print(&buffer, "Hello, %s!", "World");

EXPECT_STREQ(buffer.str, "Hello, World!");
EXPECT_EQ(buffer.used, 13);

__kmp_str_buf_free(&buffer);
}

// Test string buffer concatenation
TEST(KmpStrTest, BufferCat) {
kmp_str_buf_t buffer;
__kmp_str_buf_init(&buffer);

__kmp_str_buf_cat(&buffer, "Hello", 5);
__kmp_str_buf_cat(&buffer, " ", 1);
__kmp_str_buf_cat(&buffer, "World", 5);

EXPECT_STREQ(buffer.str, "Hello World");

__kmp_str_buf_free(&buffer);
}

// Test string buffer reservation
TEST(KmpStrTest, BufferReserve) {
kmp_str_buf_t buffer;
__kmp_str_buf_init(&buffer);

size_t large_size = 2048;
__kmp_str_buf_reserve(&buffer, large_size);

EXPECT_GE(buffer.size, large_size);

__kmp_str_buf_free(&buffer);
}

// Test basic string to int conversion
TEST(KmpStrTest, BasicStrToInt) {
EXPECT_EQ(__kmp_basic_str_to_int("0"), 0);
EXPECT_EQ(__kmp_basic_str_to_int("1"), 1);
EXPECT_EQ(__kmp_basic_str_to_int("42"), 42);
EXPECT_EQ(__kmp_basic_str_to_int("123"), 123);
}

// Test string match
TEST(KmpStrTest, StrMatch) {
const char *data = "Hello World";

// Test exact match (len == 0)
EXPECT_TRUE(__kmp_str_match("Hello World", 0, data));
EXPECT_FALSE(__kmp_str_match("Hello", 0, data)); // Not exact (data is longer)

// Test prefix match (len < 0)
EXPECT_TRUE(
__kmp_str_match("Hello", -1, data)); // "Hello" is prefix of "Hello World"
EXPECT_FALSE(__kmp_str_match("World", -1, data)); // "World" is not a prefix

// Test minimum length match (len > 0)
EXPECT_TRUE(__kmp_str_match("Hello", 5, data)); // At least 5 chars match
EXPECT_TRUE(__kmp_str_match("Hello", 3, data)); // At least 3 chars match
EXPECT_FALSE(__kmp_str_match("World", 5, data)); // First chars don't match
}

// Test string contains
TEST(KmpStrTest, StrContains) {
const char *data = "Hello World";

EXPECT_TRUE(__kmp_str_contains("Hello", 5, data));
EXPECT_TRUE(__kmp_str_contains("World", 5, data));
EXPECT_TRUE(__kmp_str_contains("lo Wo", 5, data));
EXPECT_FALSE(__kmp_str_contains("Goodbye", 7, data));
}

// Test string match for true/false values
TEST(KmpStrTest, MatchBool) {
// Test true values
EXPECT_TRUE(__kmp_str_match_true("true"));
EXPECT_TRUE(__kmp_str_match_true("TRUE"));
EXPECT_TRUE(__kmp_str_match_true("on"));
EXPECT_TRUE(__kmp_str_match_true("ON"));
EXPECT_TRUE(__kmp_str_match_true("1"));
EXPECT_TRUE(__kmp_str_match_true("yes"));
EXPECT_TRUE(__kmp_str_match_true("YES"));

// Test false values
EXPECT_TRUE(__kmp_str_match_false("false"));
EXPECT_TRUE(__kmp_str_match_false("FALSE"));
EXPECT_TRUE(__kmp_str_match_false("off"));
EXPECT_TRUE(__kmp_str_match_false("OFF"));
EXPECT_TRUE(__kmp_str_match_false("0"));
EXPECT_TRUE(__kmp_str_match_false("no"));
EXPECT_TRUE(__kmp_str_match_false("NO"));
}

// Test string replace
TEST(KmpStrTest, StrReplace) {
char str[] = "Hello World";
__kmp_str_replace(str, ' ', '_');
EXPECT_STREQ(str, "Hello_World");

__kmp_str_replace(str, 'o', '0');
EXPECT_STREQ(str, "Hell0_W0rld");
}

// Test string split
TEST(KmpStrTest, StrSplit) {
char str[] = "key=value";
char *head = nullptr;
char *tail = nullptr;

__kmp_str_split(str, '=', &head, &tail);

EXPECT_STREQ(head, "key");
EXPECT_STREQ(tail, "value");
}

// Test file name parsing
TEST(KmpStrTest, FileNameInit) {
const char *path = "/path/to/file.txt";
kmp_str_fname_t fname;
__kmp_str_fname_init(&fname, path);

EXPECT_NE(fname.path, nullptr);
EXPECT_STREQ(fname.path, path);
EXPECT_NE(fname.base, nullptr);
EXPECT_STREQ(fname.base, "file.txt");

__kmp_str_fname_free(&fname);
}

// Test string format
TEST(KmpStrTest, StrFormat) {
char *result = __kmp_str_format("Number: %d, String: %s", 42, "test");

EXPECT_NE(result, nullptr);
EXPECT_STREQ(result, "Number: 42, String: test");

__kmp_str_free(&result);
EXPECT_EQ(result, nullptr);
}

// Test string buffer concatenate buffers
TEST(KmpStrTest, BufferCatBuf) {
kmp_str_buf_t buf1, buf2;
__kmp_str_buf_init(&buf1);
__kmp_str_buf_init(&buf2);

__kmp_str_buf_print(&buf1, "Hello");
__kmp_str_buf_print(&buf2, " World");

__kmp_str_buf_catbuf(&buf1, &buf2);

EXPECT_STREQ(buf1.str, "Hello World");

__kmp_str_buf_free(&buf1);
__kmp_str_buf_free(&buf2);
}

// Test size string parsing
TEST(KmpStrTest, StrToSize) {
size_t result;
const char *error = nullptr;

__kmp_str_to_size("100", &result, 1, &error);
EXPECT_EQ(error, nullptr);
EXPECT_EQ(result, 100u);

__kmp_str_to_size("1K", &result, 1024, &error);
EXPECT_EQ(error, nullptr);
EXPECT_EQ(result, 1024u);

__kmp_str_to_size("2M", &result, 1024, &error);
EXPECT_EQ(error, nullptr);
EXPECT_EQ(result, 2u * 1024u * 1024u);
}

// Test uint string parsing
TEST(KmpStrTest, StrToUint) {
kmp_uint64 result;
const char *error = nullptr;

__kmp_str_to_uint("0", &result, &error);
EXPECT_EQ(error, nullptr);
EXPECT_EQ(result, 0u);

__kmp_str_to_uint("42", &result, &error);
EXPECT_EQ(error, nullptr);
EXPECT_EQ(result, 42u);

__kmp_str_to_uint("1234567890", &result, &error);
EXPECT_EQ(error, nullptr);
EXPECT_EQ(result, 1234567890u);
}

} // namespace
Loading