15
votes

How does one declare stdin, stout, and stderr (preferably the C versions) in LLVM? I am trying to use some stdio functions in a toy language I am creating. One such function was fgets:

char * fgets ( char * str, int num, FILE * stream );

In order to use that I needed stdin. So I wrote some LLVM API code to generate the definition of FILE that I found, and declared stdin a external global. The code generated this:

%file = type { i32, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, %marker*, %file*, i32, i32, i64, i16, i8, [1 x i8], i8*, i64, i8*, i8*, i8*, i8*, i64, i32, [20 x i8] }
%marker = type { %marker*, %file*, i32 }

@stdin = external global %file*

However, when I ran the resulting module, it gave me this error:

Undefined symbols for architecture x86_64:
"_stdin", referenced from:
    _main in cc9A5m3z.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status

Apparently, what I wrote didn't work. So my question is what do I have to write in the LLVM API to declare stdin, stout, and stderr for functions like fgets in something like a toy language compiler?

3
You can write helper functions in C which will return stdin/stdout/stderr and link them with your program. - Piotr Praszmo
I'll try that, though I would prefer to just use what the LLVM API gives us to do it, if I can. - tophat

3 Answers

11
votes

If anyone is interested, I found an answer to my question. After some intense searching I found a way to get the stdin stream without having to make a C extension: fdopen and making FILE an opaque struct.

FILE* fdopen (int fildes, const char *mode)

When fdopen is passed 0 for a file descriptor (fildes) It returns the stdin stream. Using the LLVM API, I generated the following LLVM assembly:

%FILE = type opaque
declare %FILE* @fdopen(i32, i8*)
@r = constant [2 x i8] c"r\00"

Then I was able to retrieve stdin with this call statement:

%stdin = call %FILE* @fdopen(i32 0, i8* getelementptr inbounds ([2 x i8]* @r, i32 0, i32 0))
4
votes

If you use functions like putchar, printf, gets, strtol, puts, fflush you won't need stdin and stdout. I wrote a toy compiler and those were enough for I/O with strings and integers. fflush is called with null and stdout gets flushed.

%struct._IO_FILE = type opaque
declare i32 @fflush(%struct._IO_FILE*)
...
call i32 @fflush(%struct._IO_FILE* null)
...
1
votes

It is platform specific. Sometimes stdin is macro to different symbol name.

On Android, for example, stdin is #define stdin (&__sF[0]).

For Microsoft Visual C++, stdin is #define stdin (&__iob_func()[0])

So you really need to look into your platform stdio.h header to figure that out.