Skip to content

Commit

Permalink
[libc] Implement (v|f)printf on the GPU (#96369)
Browse files Browse the repository at this point in the history
Summary:
This patch implements the `printf` family of functions on the GPU using
the new variadic support. This patch adapts the old handling in the
`rpc_fprintf` placeholder, but adds an extra RPC call to get the size of
the buffer to copy. This prevents the GPU from needing to parse the
string. While it's theoretically possible for the pass to know the size
of the struct, it's prohibitively difficult to do while maintaining ABI
compatibility with NVIDIA's varargs.

Depends on #96015.
  • Loading branch information
jhuber6 committed Jul 13, 2024
1 parent ee4661e commit 40effc7
Show file tree
Hide file tree
Showing 18 changed files with 387 additions and 89 deletions.
4 changes: 4 additions & 0 deletions libc/config/gpu/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ set(TARGET_LIBC_ENTRYPOINTS
# stdio.h entrypoints
libc.src.stdio.clearerr
libc.src.stdio.fclose
libc.src.stdio.printf
libc.src.stdio.vprintf
libc.src.stdio.fprintf
libc.src.stdio.vfprintf
libc.src.stdio.sprintf
libc.src.stdio.snprintf
libc.src.stdio.vsprintf
Expand Down
3 changes: 3 additions & 0 deletions libc/include/llvm-libc-types/rpc_opcodes_t.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ typedef enum {
RPC_PRINTF_TO_STDOUT,
RPC_PRINTF_TO_STDERR,
RPC_PRINTF_TO_STREAM,
RPC_PRINTF_TO_STDOUT_PACKED,
RPC_PRINTF_TO_STDERR_PACKED,
RPC_PRINTF_TO_STREAM_PACKED,
RPC_REMOVE,
RPC_LAST = 0xFFFF,
} rpc_opcode_t;
Expand Down
51 changes: 43 additions & 8 deletions libc/src/__support/arg_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
namespace LIBC_NAMESPACE_DECL {
namespace internal {

template <typename V, typename A>
LIBC_INLINE constexpr V align_up(V val, A align) {
return ((val + V(align) - 1) / V(align)) * V(align);
}

class ArgList {
va_list vlist;

Expand Down Expand Up @@ -55,7 +60,34 @@ class MockArgList {
}

template <class T> LIBC_INLINE T next_var() {
++arg_counter;
arg_counter++;
return T(arg_counter);
}

size_t read_count() const { return arg_counter; }
};

// Used by the GPU implementation to parse how many bytes need to be read from
// the variadic argument buffer.
template <bool packed> class DummyArgList {
size_t arg_counter = 0;

public:
LIBC_INLINE DummyArgList() = default;
LIBC_INLINE DummyArgList(va_list) { ; }
LIBC_INLINE DummyArgList(DummyArgList &other) {
arg_counter = other.arg_counter;
}
LIBC_INLINE ~DummyArgList() = default;

LIBC_INLINE DummyArgList &operator=(DummyArgList &rhs) {
arg_counter = rhs.arg_counter;
return *this;
}

template <class T> LIBC_INLINE T next_var() {
arg_counter = packed ? arg_counter + sizeof(T)
: align_up(arg_counter, alignof(T)) + sizeof(T);
return T(arg_counter);
}

Expand All @@ -64,7 +96,7 @@ class MockArgList {

// Used for the GPU implementation of `printf`. This models a variadic list as a
// simple array of pointers that are built manually by the implementation.
class StructArgList {
template <bool packed> class StructArgList {
void *ptr;
void *end;

Expand All @@ -86,15 +118,18 @@ class StructArgList {
LIBC_INLINE void *get_ptr() const { return ptr; }

template <class T> LIBC_INLINE T next_var() {
ptr = reinterpret_cast<void *>(
((reinterpret_cast<uintptr_t>(ptr) + alignof(T) - 1) / alignof(T)) *
alignof(T));

if (!packed)
ptr = reinterpret_cast<void *>(
align_up(reinterpret_cast<uintptr_t>(ptr), alignof(T)));
if (ptr >= end)
return T(-1);

T val = *reinterpret_cast<T *>(ptr);
ptr = reinterpret_cast<unsigned char *>(ptr) + sizeof(T);
// Memcpy because pointer alignment may be illegal given a packed struct.
T val;
__builtin_memcpy(&val, ptr, sizeof(T));

ptr =
reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(ptr) + sizeof(T));
return val;
}
};
Expand Down
5 changes: 4 additions & 1 deletion libc/src/gpu/rpc_fprintf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ int fprintf_impl(::FILE *__restrict file, const char *__restrict format,
}

port.send_n(format, format_size);
port.recv([&](rpc::Buffer *buffer) {
args_size = static_cast<size_t>(buffer->data[0]);
});
port.send_n(args, args_size);

uint32_t ret = 0;
Expand All @@ -51,7 +54,7 @@ int fprintf_impl(::FILE *__restrict file, const char *__restrict format,
return ret;
}

// TODO: This is a stand-in function that uses a struct pointer and size in
// TODO: Delete this and port OpenMP to use `printf`.
// place of varargs. Once varargs support is added we will use that to
// implement the real version.
LLVM_LIBC_FUNCTION(int, rpc_fprintf,
Expand Down
26 changes: 2 additions & 24 deletions libc/src/stdio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -163,18 +163,6 @@ add_entrypoint_object(
libc.src.stdio.printf_core.writer
)

add_entrypoint_object(
fprintf
SRCS
fprintf.cpp
HDRS
fprintf.h
DEPENDS
libc.hdr.types.FILE
libc.src.__support.arg_list
libc.src.stdio.printf_core.vfprintf_internal
)

add_entrypoint_object(
vsprintf
SRCS
Expand All @@ -197,18 +185,6 @@ add_entrypoint_object(
libc.src.stdio.printf_core.writer
)

add_entrypoint_object(
vfprintf
SRCS
vfprintf.cpp
HDRS
vfprintf.h
DEPENDS
libc.hdr.types.FILE
libc.src.__support.arg_list
libc.src.stdio.printf_core.vfprintf_internal
)

add_subdirectory(printf_core)
add_subdirectory(scanf_core)

Expand Down Expand Up @@ -258,6 +234,7 @@ add_stdio_entrypoint_object(fputc)
add_stdio_entrypoint_object(putc)
add_stdio_entrypoint_object(putchar)
add_stdio_entrypoint_object(printf)
add_stdio_entrypoint_object(fprintf)
add_stdio_entrypoint_object(fgetc)
add_stdio_entrypoint_object(fgetc_unlocked)
add_stdio_entrypoint_object(getc)
Expand All @@ -270,3 +247,4 @@ add_stdio_entrypoint_object(stdin)
add_stdio_entrypoint_object(stdout)
add_stdio_entrypoint_object(stderr)
add_stdio_entrypoint_object(vprintf)
add_stdio_entrypoint_object(vfprintf)
39 changes: 26 additions & 13 deletions libc/src/stdio/generic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -363,19 +363,6 @@ add_entrypoint_object(
libc.src.__support.File.platform_file
)

list(APPEND printf_deps
libc.src.__support.arg_list
libc.src.stdio.printf_core.vfprintf_internal
)

if(LLVM_LIBC_FULL_BUILD)
list(APPEND printf_deps
libc.src.__support.File.file
libc.src.__support.File.platform_file
libc.src.__support.File.platform_stdout
)
endif()

add_entrypoint_object(
printf
SRCS
Expand All @@ -396,6 +383,32 @@ add_entrypoint_object(
${printf_deps}
)

add_entrypoint_object(
fprintf
SRCS
fprintf.cpp
HDRS
../fprintf.h
DEPENDS
libc.hdr.types.FILE
libc.src.__support.arg_list
libc.src.stdio.printf_core.vfprintf_internal
${printf_deps}
)

add_entrypoint_object(
vfprintf
SRCS
vfprintf.cpp
HDRS
../vfprintf.h
DEPENDS
libc.hdr.types.FILE
libc.src.__support.arg_list
libc.src.stdio.printf_core.vfprintf_internal
${printf_deps}
)

add_entrypoint_object(
fgets
SRCS
Expand Down
File renamed without changes.
File renamed without changes.
48 changes: 48 additions & 0 deletions libc/src/stdio/gpu/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ add_header_library(
.stderr
)

add_header_library(
vfprintf_utils
HDRS
vfprintf_utils.h
DEPENDS
.gpu_file
)

add_entrypoint_object(
feof
SRCS
Expand Down Expand Up @@ -246,6 +254,46 @@ add_entrypoint_object(
.gpu_file
)

add_entrypoint_object(
printf
SRCS
printf.cpp
HDRS
../printf.h
DEPENDS
.vfprintf_utils
)

add_entrypoint_object(
vprintf
SRCS
vprintf.cpp
HDRS
../vprintf.h
DEPENDS
.vfprintf_utils
)

add_entrypoint_object(
fprintf
SRCS
fprintf.cpp
HDRS
../fprintf.h
DEPENDS
.vfprintf_utils
)

add_entrypoint_object(
vfprintf
SRCS
vfprintf.cpp
HDRS
../vfprintf.h
DEPENDS
.vfprintf_utils
)

add_entrypoint_object(
stdin
SRCS
Expand Down
31 changes: 31 additions & 0 deletions libc/src/stdio/gpu/fprintf.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//===-- GPU Implementation of fprintf -------------------------------------===//
//
// 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/stdio/fprintf.h"

#include "src/__support/CPP/string_view.h"
#include "src/__support/arg_list.h"
#include "src/errno/libc_errno.h"
#include "src/stdio/gpu/vfprintf_utils.h"

#include <stdio.h>

namespace LIBC_NAMESPACE {

LLVM_LIBC_FUNCTION(int, fprintf,
(::FILE *__restrict stream, const char *__restrict format,
...)) {
va_list vlist;
va_start(vlist, format);
cpp::string_view str_view(format);
int ret_val = vfprintf_internal(stream, format, str_view.size() + 1, vlist);
va_end(vlist);
return ret_val;
}

} // namespace LIBC_NAMESPACE
29 changes: 29 additions & 0 deletions libc/src/stdio/gpu/printf.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//===-- GPU Implementation of printf --------------------------------------===//
//
// 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/stdio/printf.h"

#include "src/__support/CPP/string_view.h"
#include "src/__support/arg_list.h"
#include "src/errno/libc_errno.h"
#include "src/stdio/gpu/vfprintf_utils.h"

#include <stdio.h>

namespace LIBC_NAMESPACE {

LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
va_list vlist;
va_start(vlist, format);
cpp::string_view str_view(format);
int ret_val = vfprintf_internal(stdout, format, str_view.size() + 1, vlist);
va_end(vlist);
return ret_val;
}

} // namespace LIBC_NAMESPACE
28 changes: 28 additions & 0 deletions libc/src/stdio/gpu/vfprintf.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//===-- GPU Implementation of vfprintf ------------------------------------===//
//
// 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/stdio/vfprintf.h"

#include "src/__support/CPP/string_view.h"
#include "src/__support/arg_list.h"
#include "src/errno/libc_errno.h"
#include "src/stdio/gpu/vfprintf_utils.h"

#include <stdio.h>

namespace LIBC_NAMESPACE {

LLVM_LIBC_FUNCTION(int, vfprintf,
(::FILE *__restrict stream, const char *__restrict format,
va_list vlist)) {
cpp::string_view str_view(format);
int ret_val = vfprintf_internal(stream, format, str_view.size() + 1, vlist);
return ret_val;
}

} // namespace LIBC_NAMESPACE
Loading

0 comments on commit 40effc7

Please sign in to comment.