Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
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
7 changes: 3 additions & 4 deletions compiler-rt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -370,14 +370,16 @@ if("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "hexagon")
list(APPEND SANITIZER_COMMON_CFLAGS -fno-emulated-tls)
string(APPEND COMPILER_RT_TEST_COMPILER_CFLAGS " -fno-emulated-tls")
endif()
if("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "amdgcn|nvptx")
if(COMPILER_RT_GPU_BUILD)
append_list_if(UBSAN_COMMON_SUPPORTED_ARCH -ffreestanding SANITIZER_COMMON_CFLAGS)
append_list_if(COMPILER_RT_HAS_NOGPULIB_FLAG -nogpulib SANITIZER_COMMON_CFLAGS)
append_list_if(COMPILER_RT_HAS_FLTO_FLAG -flto SANITIZER_COMMON_CFLAGS)
if("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "amdgcn")
append_list_if(COMPILER_RT_HAS_CODE_OBJECT_VERSION_FLAG
"SHELL:-Xclang -mcode-object-version=none" SANITIZER_COMMON_CFLAGS)
endif()
else()
append_list_if(COMPILER_RT_HAS_FNO_LTO_FLAG -fno-lto SANITIZER_COMMON_CFLAGS)
endif()

if(NOT WIN32)
Expand All @@ -395,9 +397,6 @@ append_list_if(COMPILER_RT_HAS_FVISIBILITY_HIDDEN_FLAG -fvisibility=hidden SANIT
if(NOT COMPILER_RT_HAS_FVISIBILITY_HIDDEN_FLAG)
append_list_if(COMPILER_RT_HAS_FVISIBILITY_INLINES_HIDDEN_FLAG -fvisibility-inlines-hidden SANITIZER_COMMON_CFLAGS)
endif()
if(NOT "${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "amdgcn|nvptx")
append_list_if(COMPILER_RT_HAS_FNO_LTO_FLAG -fno-lto SANITIZER_COMMON_CFLAGS)
endif()

# By default do not instrument or use profdata for compiler-rt.
if(NOT COMPILER_RT_ENABLE_PGO)
Expand Down
5 changes: 4 additions & 1 deletion compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ set(RISCV64 riscv64)
set(S390X s390x)
set(SPARC sparc)
set(SPARCV9 sparcv9)
set(SPIRV64 spirv64)
set(WASM32 wasm32)
set(WASM64 wasm64)
set(VE ve)
Expand Down Expand Up @@ -105,7 +106,9 @@ endif()
set(ALL_TYSAN_SUPPORTED_ARCH ${X86_64} ${ARM64} ${S390X} ${HEXAGON})
set(ALL_UBSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${RISCV64}
${MIPS32} ${MIPS64} ${PPC64} ${S390X} ${SPARC} ${SPARCV9} ${HEXAGON}
${LOONGARCH64} ${AMDGPU} ${NVPTX})
${LOONGARCH64})
set(ALL_UBSAN_MINIMAL_SUPPORTED_ARCH
${ALL_UBSAN_SUPPORTED_ARCH} ${AMDGPU} ${NVPTX} ${SPIRV64})
if (OS_NAME MATCHES "FreeBSD")
set(ALL_SAFESTACK_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM64})
else()
Expand Down
3 changes: 3 additions & 0 deletions compiler-rt/cmake/Modules/CompilerRTUtils.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,9 @@ macro(construct_compiler_rt_default_triple)
elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "nvptx|spirv64")
set(COMPILER_RT_GPU_BUILD ON)
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -flto -c")
elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "spirv64")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Isn't this case handled above?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed: 06ffdd6

set(COMPILER_RT_GPU_BUILD ON)
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -flto -c")
endif()
endif()

Expand Down
2 changes: 2 additions & 0 deletions compiler-rt/cmake/base-config-ix.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,8 @@ macro(test_targets)
endif()
elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "nvptx")
test_target_arch(nvptx64 "" "--nvptx64-nvidia-cuda" "-nogpulib" "-flto" "-c")
elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "spirv64")
test_target_arch(spirv64 "" "--target=spirv64-unknown-unknown" "-nogpulib" "-flto" "-c")
elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "arm")
if(WIN32)
test_target_arch(arm "" "" "")
Expand Down
3 changes: 3 additions & 0 deletions compiler-rt/cmake/config-ix.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,7 @@ else()
filter_available_targets(TSAN_SUPPORTED_ARCH ${ALL_TSAN_SUPPORTED_ARCH})
filter_available_targets(TYSAN_SUPPORTED_ARCH ${ALL_TYSAN_SUPPORTED_ARCH})
filter_available_targets(UBSAN_SUPPORTED_ARCH ${ALL_UBSAN_SUPPORTED_ARCH})
filter_available_targets(UBSAN_MINIMAL_SUPPORTED_ARCH ${ALL_UBSAN_MINIMAL_SUPPORTED_ARCH})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why

filter_available_targets(SAFESTACK_SUPPORTED_ARCH
${ALL_SAFESTACK_SUPPORTED_ARCH})
filter_available_targets(CFI_SUPPORTED_ARCH ${ALL_CFI_SUPPORTED_ARCH})
Expand Down Expand Up @@ -896,6 +897,8 @@ if (UBSAN_SUPPORTED_ARCH AND
(OS_NAME MATCHES "Linux|FreeBSD|NetBSD|Android|Darwin|SunOS" OR
"${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "amdgcn|nvptx"))
set(COMPILER_RT_HAS_UBSAN_MINIMAL TRUE)
elseif(COMPILER_RT_GPU_BUILD AND UBSAN_MINIMAL_SUPPORTED_ARCH)
set(COMPILER_RT_HAS_UBSAN_MINIMAL TRUE)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why duplicated?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed: 06ffdd6

else()
set(COMPILER_RT_HAS_UBSAN_MINIMAL FALSE)
endif()
Expand Down
12 changes: 11 additions & 1 deletion compiler-rt/lib/sanitizer_common/sanitizer_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@
#ifndef SANITIZER_PLATFORM_H
#define SANITIZER_PLATFORM_H

#if !defined(SANITIZER_GPU)
# define SANITIZER_GPU 0
#endif // !defined(SANITIZER_GPU)

#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && \
!defined(__APPLE__) && !defined(_WIN32) && !defined(__Fuchsia__) && \
!(defined(__sun__) && defined(__svr4__)) && !defined(__HAIKU__) && \
!defined(__wasi__) && !defined(__NVPTX__) && !defined(__AMDGPU__)
!defined(__wasi__) && !(SANITIZER_GPU)
# error "This operating system is not supported"
#endif

Expand Down Expand Up @@ -314,6 +318,12 @@
# define SANITIZER_NVPTX 0
#endif

#if defined(__SPIRV__)
# define SANITIZER_SPIRV 1
#else // defined(__SPIRV__)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not consistent

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed: 06ffdd6

# define SANITIZER_SPIRV 0
#endif // defined(__SPIRV__)

// By default we allow to use SizeClassAllocator64 on 64-bit platform.
// But in some cases SizeClassAllocator64 does not work well and we need to
// fallback to SizeClassAllocator32.
Expand Down
34 changes: 22 additions & 12 deletions compiler-rt/lib/ubsan_minimal/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ set(UBSAN_MINIMAL_SOURCES
ubsan_minimal_handlers.cpp
)

if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/targets/${UBSAN_MINIMAL_SUPPORTED_ARCH}.cpp)
list(APPEND UBSAN_MINIMAL_SOURCES targets/${UBSAN_MINIMAL_SUPPORTED_ARCH}.cpp)
else()
list(APPEND UBSAN_MINIMAL_SOURCES targets/generic.cpp)
endif()

include_directories(.)
include_directories(..)

set(UBSAN_CFLAGS
Expand All @@ -20,31 +27,34 @@ add_compiler_rt_component(ubsan-minimal)
# Common parts of minimal UBSan runtime.
add_compiler_rt_object_libraries(RTUbsan_minimal
OS ${SANITIZER_COMMON_SUPPORTED_OS}
ARCHS ${UBSAN_COMMON_SUPPORTED_ARCH}
ARCHS ${UBSAN_MINIMAL_SUPPORTED_ARCH}
DEFS $<IF:$<BOOL:${COMPILER_RT_GPU_BUILD}>,SANITIZER_GPU=1,SANITIZER_GPU=0>
SOURCES ${UBSAN_MINIMAL_SOURCES} CFLAGS ${UBSAN_CFLAGS})


# Standalone minimal UBSan runtimes.
add_compiler_rt_runtime(clang_rt.ubsan_minimal
STATIC
OS ${UBSAN_SUPPORTED_OS}
ARCHS ${UBSAN_SUPPORTED_ARCH}
ARCHS ${UBSAN_MINIMAL_SUPPORTED_ARCH}
OBJECT_LIBS RTUbsan_minimal
CFLAGS ${UBSAN_CFLAGS}
PARENT_TARGET ubsan-minimal)

add_compiler_rt_runtime(clang_rt.ubsan_minimal
SHARED
OS ${UBSAN_SUPPORTED_OS}
ARCHS ${UBSAN_SUPPORTED_ARCH}
OBJECT_LIBS RTUbsan_minimal
CFLAGS ${UBSAN_CFLAGS}
LINK_FLAGS ${UBSAN_LINK_FLAGS}
LINK_LIBS ${UBSAN_DYNAMIC_LIBS}
PARENT_TARGET ubsan-minimal)
if(NOT COMPILER_RT_GPU_BUILD)
add_compiler_rt_runtime(clang_rt.ubsan_minimal
SHARED
OS ${UBSAN_SUPPORTED_OS}
ARCHS ${UBSAN_MINIMAL_SUPPORTED_ARCH}
OBJECT_LIBS RTUbsan_minimal
CFLAGS ${UBSAN_CFLAGS}
LINK_FLAGS ${UBSAN_LINK_FLAGS}
LINK_LIBS ${UBSAN_DYNAMIC_LIBS}
PARENT_TARGET ubsan-minimal)
endif()

if (SANITIZER_USE_SYMBOLS AND NOT APPLE)
set(ARCHS_FOR_SYMBOLS ${UBSAN_SUPPORTED_ARCH})
set(ARCHS_FOR_SYMBOLS ${UBSAN_MINIMAL_SUPPORTED_ARCH})
list(REMOVE_ITEM ARCHS_FOR_SYMBOLS i386 i686)
add_sanitizer_rt_symbols(clang_rt.ubsan_minimal
ARCHS ${ARCHS_FOR_SYMBOLS}
Expand Down
76 changes: 76 additions & 0 deletions compiler-rt/lib/ubsan_minimal/targets/generic.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@

#include "ubsan_minimal_common.h"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not a fan of splitting this up so arbitrarily, I don't know what SPIR-V does that can't be done by improving the set of compiler builtins

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The __spirv_* functions are actually the compiler built-ins for the SPIRV target. So, I'm not sure I understand how we could improve the built-ins.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I guess I don't really understand how the SPIR-V build is supposed to work, but why do we have both C libcalls and stuff like __builtin_spirv_num_workgroups(0)? I generally prefer the latter.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've updated the file to use the __builtin_spirv_* functions when it's possible. Still, the __spirv_AbortKHR(...) is used, because it's handled by the backend. 8554443


#include <stdlib.h>
#include <string.h>

#if defined(__ANDROID__)
extern "C" __attribute__((weak)) void android_set_abort_message(const char *);
#endif // defined(__ANDROID__)

#ifdef KERNEL_USE
extern "C" void ubsan_message(const char *msg);
static void message(const char *msg) { ubsan_message(msg); }
#elif defined(SANITIZER_AMDGPU) || defined(SANITIZER_NVPTX)
#include <stdio.h>
static void message(const char *msg) { fprintf(stderr, "%s", msg); }
#else
#include <unistd.h>
static void message(const char *msg) { (void)write(2, msg, strlen(msg)); }
#endif

static char *append_str(const char *s, char *buf, const char *end) {
for (const char *p = s; (buf < end) && (*p != '\0'); ++p, ++buf)
*buf = *p;
return buf;
}

static char *append_hex(uintptr_t d, char *buf, const char *end) {
// Print the address by nibbles.
for (unsigned shift = sizeof(uintptr_t) * 8; shift && buf < end;) {
shift -= 4;
unsigned nibble = (d >> shift) & 0xf;
*(buf++) = nibble < 10 ? nibble + '0' : nibble - 10 + 'a';
}
return buf;
}

static void format_msg(const char *kind, uintptr_t caller, char *buf,
const char *end) {
buf = append_str("ubsan: ", buf, end);
buf = append_str(kind, buf, end);
buf = append_str(" by 0x", buf, end);
buf = append_hex(caller, buf, end);
buf = append_str("\n", buf, end);
if (buf == end)
--buf; // Make sure we don't cause a buffer overflow.
*buf = '\0';
}

void __ubsan_message(const char *msg) {
message(msg);
}

void __ubsan_message(const char *kind, uintptr_t caller) {
char buf[128];
format_msg(kind, caller, buf, buf + sizeof(buf));
message(buf);
}

void __ubsan_abort() {
abort();
}

void __ubsan_abort_with_message(const char *kind, uintptr_t caller) {
char buf[128];
format_msg(kind, caller, buf, buf + sizeof(buf));

#if defined(__ANDROID__)
if (&android_set_abort_message)
android_set_abort_message(buf);
#else // defined(__ANDROID__)
message(buf);
#endif // defined(__ANDROID__)

abort();
}
45 changes: 45 additions & 0 deletions compiler-rt/lib/ubsan_minimal/targets/spirv64.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include "ubsan_minimal_common.h"

using uint3 = unsigned __attribute__((ext_vector_type(3)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why is all of this necessary?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I believe, that the group ID and local ID info of the error should be useful for the GPU targets. Here is the OpenCL printf used. It supports vector printing -- the %v3u in the format string. Such parameters should be passed as vectors.

Reference: https://registry.khronos.org/OpenCL/specs/unified/html/OpenCL_C.html#printf-format-string


struct __ubsan_abort_info_t {
__attribute__((opencl_constant)) const char *fmt;
const char *kind;
uintptr_t caller;
uint3 gid;
uint3 lid;
};

// OpenCL printf maps to OpExtInst printf (OpenCL extended instruction set).
extern "C" int printf(__attribute__((opencl_constant)) const char *fmt, ...);

// OpenCL work-item builtins map to SPIR-V BuiltIn variables.
extern "C" unsigned get_group_id(unsigned dim);
extern "C" unsigned get_local_id(unsigned dim);

static __attribute__((opencl_constant)) const char ubsan_msg_simple[] = "%s";
static __attribute__((opencl_constant)) const char ubsan_msg_fmt[] =
"ubsan: %s by 0x%lx at gid=[%v3u] lid=[%v3u]\n";

void __ubsan_message(const char *msg) { printf(ubsan_msg_simple, msg); }

void __ubsan_message(const char *kind, uintptr_t caller) {
uint3 gid = {get_group_id(0), get_group_id(1), get_group_id(2)};
uint3 lid = {get_local_id(0), get_local_id(1), get_local_id(2)};

printf(ubsan_msg_fmt, kind, caller, gid, lid);
}

// SPV_KHR_abort: OpAbortKHR terminates the invocation and passes a message
// to the client API. The message is passed as a typed value.
[[noreturn]] void __spirv_AbortKHR(__ubsan_abort_info_t info);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This should not be defined here, it should be what __builtin_verbose_trap or __builtin_trap lowers to.

@vmustya vmustya May 6, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The __builtin_verbose_trap doesn't produce any semantic info in the SPIR-V (or any other generated code). It generates the llvm.trap intrinsic and some extra debug info. However, the __spirv_AbortKHR is lowered into the OpAbortKHR SPIR-V instruction with an arbitrary error information. Here the sanitizer message is passed as the abort operand, so the offload device compiler would be able to print it before the kernel termination.

See the SPIR-V extension spec for more info: https://github.khronos.org/SPIRV-Registry/extensions/KHR/SPV_KHR_abort.html

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Trapping isn't the intended way for the minimal runtime to work. If you just want to abort on first error you'd use -fsanitize-trap=all. There's not much value in having a minimal runtime that functions the same as the trapping version.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The idea is to report an error and continue, when the error is recoverable. There are two kinds of the error handlers: non-aborting and aborting. For the non-aborting handlers we just should the printf and continue.

Also, the -fsanitize-trap=all only emits the trap instruction, without any error message. It's hard to work with such traps, when the debugger support is limited.


[[noreturn]] void __ubsan_abort() { __ubsan_abort_with_message("abort", 0); }

[[noreturn]] void __ubsan_abort_with_message(const char *kind,
uintptr_t caller) {
uint3 gid = {get_group_id(0), get_group_id(1), get_group_id(2)};
uint3 lid = {get_local_id(0), get_local_id(1), get_local_id(2)};
__ubsan_abort_info_t info = {ubsan_msg_fmt, kind, caller, gid, lid};
__spirv_AbortKHR(info);
}
32 changes: 32 additions & 0 deletions compiler-rt/lib/ubsan_minimal/ubsan_minimal_common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//===-- ubsan_minimal_common.h ----------------------------------*- 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
//
//===----------------------------------------------------------------------===//
//
// This file is a part of the minimal UB sanitizer runtime.
//
//===----------------------------------------------------------------------===//

#ifndef UBSAN_MINIMAL_COMMON_H
#define UBSAN_MINIMAL_COMMON_H

#if defined(__UINTPTR_TYPE__)
typedef __UINTPTR_TYPE__ uintptr_t;

static_assert(sizeof(uintptr_t) == sizeof(void *),
"uintptr_t must be the same size as void*");
#else // defined(__UINTPTR_TYPE__)
#include <stdint.h>
#endif // defined(__UINTPTR_TYPE__)

void __ubsan_message(const char *msg);
void __ubsan_message(const char *kind, uintptr_t caller);

[[noreturn]] void __ubsan_abort();
[[noreturn]] void __ubsan_abort_with_message(const char *kind,
uintptr_t caller);

#endif // UBSAN_MINIMAL_COMMON_H
Loading
Loading