Skip to content

[libc] Extend the baremetal I/O vendor ABI #98683

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 14, 2024

Conversation

petrhosek
Copy link
Member

This refines and extends the external ABI for I/O, later changes will update the baremetal implementations of I/O functions to use these.

This refines and extends the external ABI for I/O, later changes will
update the baremetal implementations of I/O functions to use these.
@llvmbot
Copy link
Member

llvmbot commented Jul 12, 2024

@llvm/pr-subscribers-libc

Author: Petr Hosek (petrhosek)

Changes

This refines and extends the external ABI for I/O, later changes will update the baremetal implementations of I/O functions to use these.


Full diff: https://github.com/llvm/llvm-project/pull/98683.diff

2 Files Affected:

  • (modified) libc/src/__support/OSUtil/baremetal/io.cpp (+37-6)
  • (modified) libc/src/__support/OSUtil/baremetal/io.h (+1)
diff --git a/libc/src/__support/OSUtil/baremetal/io.cpp b/libc/src/__support/OSUtil/baremetal/io.cpp
index c97bd5ae65b13..bd5946f267328 100644
--- a/libc/src/__support/OSUtil/baremetal/io.cpp
+++ b/libc/src/__support/OSUtil/baremetal/io.cpp
@@ -12,20 +12,51 @@
 
 namespace LIBC_NAMESPACE {
 
-// This is intended to be provided by the vendor.
+// These are intended to be provided by the vendor.
+//
+// The signature of these types and functions intentionally match `fopencookie`
+// which allows the following:
+//
+// ```
+// struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie;
+// cookie_io_functions_t stdin_func = { .read = __llvm_libc_stdio_read };
+// FILE *stdin = fopencookie(&__llvm_libc_stdin_cookie, "r", stdin_func);
+// ...
+// struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie;
+// cookie_io_functions_t stdout_func = { .write = __llvm_libc_stdio_write };
+// FILE *stdout = fopencookie(&__llvm_libc_stdout_cookie, "w", stdout_func);
+// ...
+// struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie;
+// cookie_io_functions_t stdout_func = { .write = __llvm_libc_stdio_write };
+// FILE *stderr = fopencookie(&__llvm_libc_stderr_cookie, "w", stderr_func);
+// ```
+//
+// At the same time, implementation of functions like `printf` and `scanf` can
+// use `__llvm_libc_stdio_read` and `__llvm_libc_stdio_write` directly to avoid
+// the extra indirection.
 
-extern struct __llvm_libc_stdin __llvm_libc_stdin;
-extern "C" ssize_t __llvm_libc_stdin_read(void *cookie, char *buf, size_t size);
+struct __llvm_libc_stdio_cookie;
 
-extern "C" void __llvm_libc_log_write(const char *msg, size_t len);
+extern struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie;
+extern struct __llvm_libc_stdio_cookie __llvm_libc_stdout_cookie;
+extern struct __llvm_libc_stdio_cookie __llvm_libc_stderr_cookie;
+
+extern "C" ssize_t __llvm_libc_stdio_read(void *cookie, char *buf, size_t size);
+extern "C" ssize_t __llvm_libc_stdio_write(void *cookie, const char *buf, size_t size);
 
 ssize_t read_from_stdin(char *buf, size_t size) {
-  return __llvm_libc_stdin_read(reinterpret_cast<void *>(&__llvm_libc_stdin),
+  return __llvm_libc_stdio_read(static_cast<void *>(&__llvm_libc_stdin_cookie),
                                 buf, size);
 }
 
+void write_to_stdout(cpp::string_view msg) {
+  __llvm_libc_stdio_write(static_cast<void *>(&__llvm_libc_stdout_cookie),
+                                 msg.data(), msg.size());
+}
+
 void write_to_stderr(cpp::string_view msg) {
-  __llvm_libc_log_write(msg.data(), msg.size());
+  __llvm_libc_stdio_write(static_cast<void *>(&__llvm_libc_stderr_cookie),
+                                 msg.data(), msg.size());
 }
 
 } // namespace LIBC_NAMESPACE
diff --git a/libc/src/__support/OSUtil/baremetal/io.h b/libc/src/__support/OSUtil/baremetal/io.h
index b9ae0bde502be..95e8277860a82 100644
--- a/libc/src/__support/OSUtil/baremetal/io.h
+++ b/libc/src/__support/OSUtil/baremetal/io.h
@@ -17,6 +17,7 @@ namespace LIBC_NAMESPACE {
 
 ssize_t read_from_stdin(char *buf, size_t size);
 void write_to_stderr(cpp::string_view msg);
+void write_to_stdout(cpp::string_view msg);
 
 } // namespace LIBC_NAMESPACE
 

Copy link

github-actions bot commented Jul 12, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Contributor

@frobtech frobtech left a comment

Choose a reason for hiding this comment

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

This is a good incremental step and we can continue the cleanup and formalization with later changes.


ssize_t read_from_stdin(char *buf, size_t size) {
return __llvm_libc_stdin_read(reinterpret_cast<void *>(&__llvm_libc_stdin),
return __llvm_libc_stdio_read(static_cast<void *>(&__llvm_libc_stdin_cookie),
Copy link
Contributor

Choose a reason for hiding this comment

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

The cast is superfluous but does no harm.


extern "C" void __llvm_libc_log_write(const char *msg, size_t len);
extern struct __llvm_libc_stdio_cookie __llvm_libc_stdin_cookie;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the comments here need to be a bit more clear that these three symbols must be provided, even if they don't point at anything, and give examples of how to meet the ABI.

All these declarations belong in some header file. Eventually such a header file should be installed somewhere so embedders can use it for the ABI contract they're meeting. But that can be a later cleanup, a comment here is enough for now.

buf, size);
}

void write_to_stdout(cpp::string_view msg) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should change these to propagate return values, and leave it to the embedder to decide if they want to return apparent success in error (or bit-bucket) cases.
But that can be a follow-on cleanup after this change lands.

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree, but I'd like to do that in a follow-up change since I'll also need to update the callers.

Copy link
Contributor

@michaelrj-google michaelrj-google left a comment

Choose a reason for hiding this comment

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

LGTM, I agree with the comments. For return types I think we should generally match what fread and fwrite do: return the number of characters read/written.

@petrhosek
Copy link
Member Author

LGTM, I agree with the comments. For return types I think we should generally match what fread and fwrite do: return the number of characters read/written.

I plan to change the signatures and update all callers in a follow-up change.

@frobtech
Copy link
Contributor

LGTM, I agree with the comments. For return types I think we should generally match what fread and fwrite do: return the number of characters read/written.

The precise protocol here is already set by the fopencookie callbacks' APIs, which have the same return value semantics as their POSIX counterparts.

@petrhosek petrhosek merged commit 00895ef into llvm:main Jul 14, 2024
4 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants