4ntoine
4ntoine

Reputation: 20420

How to intercept LLVM lli tool input?

I'd like to use LLVM lli tool as static library (rename main() to lli() and export it in libLLi.a) - to create rich UI for it. How can i modify it (or use without modifications) in order to intercept stdin?

Assume i know how to generate LLVM assembly file (using clang -S -emit-llvm .. -o output.ll) and how to execute it using lli tool (lli output.ll).

Common use case:

Source code of simple app to be interpreted by lli:

#include <iostream>

using namespace std;

int main() {
    char name[128]; 
    cout << "type your name: ";
    cin.getline(name, sizeof(name));
    cout << "hi, " << name << endl;

    return 0;
}

I need to interpret LLVM assembly for it and to show InputBox when cin.getline invoked and show TextBox when cout << invoked (InputBox and TextBox are rich UI controls).

PS. I can't fork process and forward stdin/stdout of the whole child process.

Upvotes: 2

Views: 604

Answers (1)

Nick Lewycky
Nick Lewycky

Reputation: 146

lli is already a thin wrapper around llvm library functions, just use those instead. The main() function in tools/lli/lli.cpp is long only because it supports tons of flags to control every possible setting. After you strip it down it should be less than 10 lines to create an ExecutionEngine using an EngineBuilder and use it to run a llvm::Function.

You might also find chapter 4 of the Kaleidoscope tutorial helpful, where they add JIT support to the language. This also demonstrates how to use an EngineBuilder, though the ExecutionEngine they choose to build is a JIT instead of an Interpreter, you can customize it for your use case.

Now for the other part of your question, how do you trap stdin and stdout? LLVM is not a VM, the code is running in your process and using your stdin and stdout. My first suggestion is that since you already have the function in LLVM IR format, just run a transformation pass that replaces standard I/O functions with your own I/O functions. A simpler way to do that if you can get the ABI to line up, is to do the remapping with the ExecutionEngine. Call EE->updateGlobalMapping(functionDeclInIR, &replacementFunctionInNativeCode) to tell the ExecutionEngine that the Function* for functionDeclInIR is represented in native code by replacementFunctionInNativeCode. You would need to provide your own implementation of _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc (aka. ostream::operator<<) which uses your GUI.

Upvotes: 4

Related Questions