| //===--- GPU helper functions for printf using RPC ------------------------===// | |
| // | |
| // 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 "hdr/types/FILE.h" | |
| #include "src/__support/GPU/utils.h" | |
| #include "src/__support/RPC/rpc_client.h" | |
| #include "src/__support/arg_list.h" | |
| #include "src/stdio/gpu/file.h" | |
| #include "src/string/string_utils.h" | |
| namespace LIBC_NAMESPACE_DECL { | |
| template <uint32_t opcode> | |
| LIBC_INLINE int vfprintf_impl(::FILE *__restrict file, | |
| const char *__restrict format, size_t format_size, | |
| va_list vlist) { | |
| uint64_t mask = gpu::get_lane_mask(); | |
| rpc::Client::Port port = rpc::client.open<opcode>(); | |
| if constexpr (opcode == LIBC_PRINTF_TO_STREAM || | |
| opcode == LIBC_PRINTF_TO_STREAM_PACKED) { | |
| port.send([&](rpc::Buffer *buffer, uint32_t) { | |
| buffer->data[0] = reinterpret_cast<uintptr_t>(file); | |
| }); | |
| } | |
| size_t args_size = 0; | |
| port.send_n(format, format_size); | |
| port.recv([&](rpc::Buffer *buffer, uint32_t) { | |
| args_size = static_cast<size_t>(buffer->data[0]); | |
| }); | |
| // If the underlying argument buffer is of known size we use it directly. A | |
| // value of one uniquely indicates an empty argument list. | |
| if (size_t arg_max = __builtin_object_size(vlist, /*max=*/0b00); | |
| arg_max == __builtin_object_size(vlist, /*min=*/0b10)) | |
| args_size = arg_max; | |
| port.send_n(vlist, args_size); | |
| uint32_t ret = 0; | |
| for (;;) { | |
| const char *str = nullptr; | |
| port.recv([&](rpc::Buffer *buffer, uint32_t) { | |
| ret = static_cast<uint32_t>(buffer->data[0]); | |
| str = reinterpret_cast<const char *>(buffer->data[1]); | |
| }); | |
| // If any lanes have a string argument it needs to be copied back. | |
| if (!gpu::ballot(mask, str && args_size != 1)) | |
| break; | |
| uint64_t size = str ? internal::string_length(str) + 1 : 0; | |
| port.send_n(str, size); | |
| } | |
| return ret; | |
| } | |
| LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream, | |
| const char *__restrict format, | |
| size_t format_size, va_list vlist) { | |
| // The AMDPGU backend uses a packed struct for its varargs. We pass it as a | |
| // separate opcode so the server knows how much to advance the pointers. | |
| #if defined(LIBC_TARGET_ARCH_IS_AMDGPU) | |
| if (stream == stdout) | |
| return vfprintf_impl<LIBC_PRINTF_TO_STDOUT_PACKED>(stream, format, | |
| format_size, vlist); | |
| else if (stream == stderr) | |
| return vfprintf_impl<LIBC_PRINTF_TO_STDERR_PACKED>(stream, format, | |
| format_size, vlist); | |
| else | |
| return vfprintf_impl<LIBC_PRINTF_TO_STREAM_PACKED>(stream, format, | |
| format_size, vlist); | |
| #else | |
| if (stream == stdout) | |
| return vfprintf_impl<LIBC_PRINTF_TO_STDOUT>(stream, format, format_size, | |
| vlist); | |
| else if (stream == stderr) | |
| return vfprintf_impl<LIBC_PRINTF_TO_STDERR>(stream, format, format_size, | |
| vlist); | |
| else | |
| return vfprintf_impl<LIBC_PRINTF_TO_STREAM>(stream, format, format_size, | |
| vlist); | |
| #endif | |
| } | |
| } // namespace LIBC_NAMESPACE_DECL |