Skip to content

Commit de61d12

Browse files
author
kr-2003
committed
oop redirection in interop + win fix
1 parent 63993d1 commit de61d12

File tree

7 files changed

+177
-35
lines changed

7 files changed

+177
-35
lines changed

include/CppInterOp/CppInterOp.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -668,8 +668,7 @@ CPPINTEROP_API void GetOperator(TCppScope_t scope, Operator op,
668668
///\returns nullptr on failure.
669669
CPPINTEROP_API TInterp_t
670670
CreateInterpreter(const std::vector<const char*>& Args = {},
671-
const std::vector<const char*>& GpuArgs = {},
672-
int stdin_fd = 0, int stdout_fd = 1, int stderr_fd = 2);
671+
const std::vector<const char*>& GpuArgs = {});
673672

674673
/// Deletes an instance of an interpreter.
675674
///\param[in] I - the interpreter to be deleted, if nullptr, deletes the last.
@@ -905,9 +904,11 @@ CPPINTEROP_API void CodeComplete(std::vector<std::string>& Results,
905904
///\returns 0 on success, non-zero on failure.
906905
CPPINTEROP_API int Undo(unsigned N = 1);
907906

907+
#ifndef _WIN32
908908
/// Returns the process ID of the executor process.
909909
/// \returns the PID of the executor process.
910910
CPPINTEROP_API pid_t GetExecutorPID();
911+
#endif
911912

912913
} // end namespace Cpp
913914

lib/CppInterOp/Compatibility.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,9 +438,11 @@ inline void codeComplete(std::vector<std::string>& Results,
438438
#endif
439439
}
440440

441+
#ifndef _WIN32
441442
#ifdef CPPINTEROP_VERSION_PATCH
442443
inline pid_t getExecutorPID() { return /*llvm*/ getLastLaunchedExecutorPID(); }
443444
#endif
445+
#endif
444446

445447
} // namespace compat
446448

lib/CppInterOp/CppInterOp.cpp

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,23 @@
6363
#include <sstream>
6464
#include <stack>
6565
#include <string>
66+
#ifndef _WIN32
67+
#include <unistd.h>
68+
#endif
6669

6770
// Stream redirect.
6871
#ifdef _WIN32
6972
#include <io.h>
7073
#ifndef STDOUT_FILENO
7174
#define STDOUT_FILENO 1
75+
#define STDERR_FILENO 2
7276
// For exec().
7377
#include <stdio.h>
7478
#define popen(x, y) (_popen(x, y))
7579
#define pclose (_pclose)
7680
#endif
7781
#else
7882
#include <dlfcn.h>
79-
#include <unistd.h>
8083
#endif // WIN32
8184

8285
namespace Cpp {
@@ -3060,9 +3063,7 @@ static std::string MakeResourcesPath() {
30603063
} // namespace
30613064

30623065
TInterp_t CreateInterpreter(const std::vector<const char*>& Args /*={}*/,
3063-
const std::vector<const char*>& GpuArgs /*={}*/,
3064-
int stdin_fd /*=0*/, int stdout_fd /*=1*/,
3065-
int stderr_fd /*=2*/) {
3066+
const std::vector<const char*>& GpuArgs /*={}*/) {
30663067
std::string MainExecutableName = sys::fs::getMainExecutable(nullptr, nullptr);
30673068
std::string ResourceDir = MakeResourcesPath();
30683069
std::vector<const char*> ClingArgv = {"-resource-dir", ResourceDir.c_str(),
@@ -3107,7 +3108,7 @@ TInterp_t CreateInterpreter(const std::vector<const char*>& Args /*={}*/,
31073108
#else
31083109
auto Interp = compat::Interpreter::create(
31093110
static_cast<int>(ClingArgv.size()), ClingArgv.data(), nullptr, {},
3110-
nullptr, true, stdin_fd, stdout_fd, stderr_fd);
3111+
nullptr, true);
31113112
if (!Interp)
31123113
return nullptr;
31133114
auto* I = Interp.release();
@@ -3831,12 +3832,12 @@ void Destruct(TCppObject_t This, TCppScope_t scope, bool withFree /*=true*/,
38313832
}
38323833

38333834
class StreamCaptureInfo {
3834-
struct file_deleter {
3835-
void operator()(FILE* fp) { pclose(fp); }
3836-
};
3837-
std::unique_ptr<FILE, file_deleter> m_TempFile;
3835+
3836+
FILE* m_TempFile;
38383837
int m_FD = -1;
38393838
int m_DupFD = -1;
3839+
int mode = -1;
3840+
bool m_OwnsFile = false;
38403841

38413842
public:
38423843
#ifdef _MSC_VER
@@ -3851,28 +3852,54 @@ class StreamCaptureInfo {
38513852
}()},
38523853
m_FD(FD) {
38533854
#else
3854-
StreamCaptureInfo(int FD) : m_TempFile{tmpfile()}, m_FD(FD) {
3855+
StreamCaptureInfo(int FD): mode(FD) {
38553856
#endif
3857+
auto& I = getInterp();
3858+
if(I.isOutOfProcess() && FD == STDOUT_FILENO) {
3859+
m_TempFile = I.getTempFileForOOP(FD);
3860+
::fflush(m_TempFile);
3861+
m_FD = fileno(m_TempFile);
3862+
m_OwnsFile = false;
3863+
} else {
3864+
m_TempFile = tmpfile();
3865+
m_FD = FD;
3866+
m_OwnsFile = true;
3867+
}
38563868
if (!m_TempFile) {
38573869
perror("StreamCaptureInfo: Unable to create temp file");
38583870
return;
38593871
}
38603872

3861-
m_DupFD = dup(FD);
3873+
m_DupFD = dup(m_FD);
38623874

38633875
// Flush now or can drop the buffer when dup2 is called with Fd later.
38643876
// This seems only necessary when piping stdout or stderr, but do it
38653877
// for ttys to avoid over complicated code for minimal benefit.
3866-
::fflush(FD == STDOUT_FILENO ? stdout : stderr);
3867-
if (dup2(fileno(m_TempFile.get()), FD) < 0)
3878+
if(m_FD == STDOUT_FILENO) {
3879+
::fflush(stdout);
3880+
} else if(m_FD == STDERR_FILENO) {
3881+
::fflush(stderr);
3882+
} else {
3883+
#ifndef _WIN32
3884+
fsync(m_FD);
3885+
#endif
3886+
}
3887+
// ::fflush(FD == STDOUT_FILENO ? stdout : stderr);
3888+
if (dup2(fileno(m_TempFile), m_FD) < 0)
38683889
perror("StreamCaptureInfo:");
3890+
38693891
}
38703892
StreamCaptureInfo(const StreamCaptureInfo&) = delete;
38713893
StreamCaptureInfo& operator=(const StreamCaptureInfo&) = delete;
38723894
StreamCaptureInfo(StreamCaptureInfo&&) = delete;
38733895
StreamCaptureInfo& operator=(StreamCaptureInfo&&) = delete;
38743896

3875-
~StreamCaptureInfo() { assert(m_DupFD == -1 && "Captured output not used?"); }
3897+
~StreamCaptureInfo() {
3898+
assert(m_DupFD == -1 && "Captured output not used?");
3899+
if(m_TempFile && m_OwnsFile) {
3900+
fclose(m_TempFile);
3901+
}
3902+
}
38763903

38773904
std::string GetCapturedString() {
38783905
assert(m_DupFD != -1 && "Multiple calls to GetCapturedString");
@@ -3881,32 +3908,39 @@ class StreamCaptureInfo {
38813908
if (dup2(m_DupFD, m_FD) < 0)
38823909
perror("StreamCaptureInfo:");
38833910
// Go to the end of the file.
3884-
if (fseek(m_TempFile.get(), 0L, SEEK_END) != 0)
3911+
if (fseek(m_TempFile, 0L, SEEK_END) != 0)
38853912
perror("StreamCaptureInfo:");
38863913

38873914
// Get the size of the file.
3888-
long bufsize = ftell(m_TempFile.get());
3915+
long bufsize = ftell(m_TempFile);
38893916
if (bufsize == -1)
38903917
perror("StreamCaptureInfo:");
38913918

38923919
// Allocate our buffer to that size.
38933920
std::unique_ptr<char[]> content(new char[bufsize + 1]);
38943921

38953922
// Go back to the start of the file.
3896-
if (fseek(m_TempFile.get(), 0L, SEEK_SET) != 0)
3923+
if (fseek(m_TempFile, 0L, SEEK_SET) != 0)
38973924
perror("StreamCaptureInfo:");
38983925

38993926
// Read the entire file into memory.
39003927
size_t newLen =
3901-
fread(content.get(), sizeof(char), bufsize, m_TempFile.get());
3902-
if (ferror(m_TempFile.get()) != 0)
3928+
fread(content.get(), sizeof(char), bufsize, m_TempFile);
3929+
if (ferror(m_TempFile) != 0)
39033930
fputs("Error reading file", stderr);
39043931
else
39053932
content[newLen++] = '\0'; // Just to be safe.
39063933

39073934
std::string result = content.get();
39083935
close(m_DupFD);
39093936
m_DupFD = -1;
3937+
auto& I = getInterp();
3938+
if (I.isOutOfProcess() && mode != STDERR_FILENO) {
3939+
if (ftruncate(m_FD, 0) != 0)
3940+
perror("ftruncate");
3941+
if (lseek(m_FD, 0, SEEK_SET) == -1)
3942+
perror("lseek");
3943+
}
39103944
return result;
39113945
}
39123946
};
@@ -3946,12 +3980,14 @@ int Undo(unsigned N) {
39463980
#endif
39473981
}
39483982

3983+
#ifndef _WIN32
39493984
pid_t GetExecutorPID() {
39503985
#ifdef CPPINTEROP_VERSION_PATCH
39513986
return compat::getExecutorPID();
39523987
#endif
39533988
return -1;
39543989
}
3990+
#endif
39553991

39563992

39573993
} // end namespace Cpp

lib/CppInterOp/CppInterOpInterpreter.h

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,13 @@
4040
#include "llvm/TargetParser/Triple.h"
4141

4242
#include <fcntl.h>
43+
#ifndef _WIN32
4344
#include <unistd.h>
45+
#endif
4446
#include <utility>
4547
#include <vector>
48+
#include <stdio.h>
49+
#include <iostream>
4650

4751
namespace clang {
4852
class CompilerInstance;
@@ -147,20 +151,80 @@ class Interpreter {
147151

148152
Interpreter(std::unique_ptr<clang::Interpreter> CI) : inner(std::move(CI)) {}
149153

154+
public:
155+
struct FileDeleter {
156+
void operator()(FILE* f) {
157+
if (f) fclose(f);
158+
}
159+
};
160+
161+
private:
162+
static std::unique_ptr<FILE, FileDeleter>& getStdinFile() {
163+
static std::unique_ptr<FILE, FileDeleter> stdin_file = nullptr;
164+
return stdin_file;
165+
}
166+
167+
static std::unique_ptr<FILE, FileDeleter>& getStdoutFile() {
168+
static std::unique_ptr<FILE, FileDeleter> stdout_file = nullptr;
169+
return stdout_file;
170+
}
171+
172+
static std::unique_ptr<FILE, FileDeleter>& getStderrFile() {
173+
static std::unique_ptr<FILE, FileDeleter> stderr_file = nullptr;
174+
return stderr_file;
175+
}
176+
177+
static bool& getOutOfProcess() {
178+
static bool outOfProcess = false;
179+
return outOfProcess;
180+
}
181+
182+
static bool initializeTempFiles() {
183+
getStdinFile().reset(tmpfile());
184+
getStdoutFile().reset(tmpfile());
185+
getStderrFile().reset(tmpfile());
186+
187+
return getStdinFile() && getStdoutFile() && getStderrFile();
188+
}
189+
190+
150191
public:
151192
static std::unique_ptr<Interpreter>
152193
create(int argc, const char* const* argv, const char* llvmdir = nullptr,
153194
const std::vector<std::shared_ptr<clang::ModuleFileExtension>>&
154195
moduleExtensions = {},
155-
void* extraLibHandle = nullptr, bool noRuntime = true,
156-
int stdin_fd = 0, int stdout_fd = 1, int stderr_fd = 2) {
196+
void* extraLibHandle = nullptr, bool noRuntime = true) {
157197
// Initialize all targets (required for device offloading)
158198
llvm::InitializeAllTargetInfos();
159199
llvm::InitializeAllTargets();
160200
llvm::InitializeAllTargetMCs();
161201
llvm::InitializeAllAsmPrinters();
162202

163203
std::vector<const char*> vargs(argv + 1, argv + argc);
204+
205+
#if defined(_WIN32)
206+
getOutOfProcess() = false;
207+
#else
208+
getOutOfProcess() =
209+
std::any_of(vargs.begin(), vargs.end(), [](const char* arg) {
210+
return llvm::StringRef(arg).trim() == "--use-oop-jit";
211+
});
212+
int stdin_fd = 0;
213+
int stdout_fd = 1;
214+
int stderr_fd = 2;
215+
if(getOutOfProcess()) {
216+
bool init = initializeTempFiles();
217+
if(!init) {
218+
llvm::errs() << "Can't start out-of-process JIT execution. Continuing with in-process JIT execution.\n";
219+
getOutOfProcess() = false;
220+
} else {
221+
stdin_fd = fileno(getStdinFile().get());
222+
stdout_fd = fileno(getStdoutFile().get());
223+
stderr_fd = fileno(getStderrFile().get());
224+
}
225+
}
226+
#endif
227+
164228
auto CI =
165229
compat::createClangInterpreter(vargs, stdin_fd, stdout_fd, stderr_fd);
166230
if (!CI) {
@@ -176,6 +240,24 @@ class Interpreter {
176240
operator const clang::Interpreter&() const { return *inner; }
177241
operator clang::Interpreter&() { return *inner; }
178242

243+
static bool isOutOfProcess() {
244+
return getOutOfProcess();
245+
}
246+
247+
FILE* getTempFileForOOP(int FD) {
248+
switch(FD) {
249+
case(STDIN_FILENO):
250+
return getStdinFile().get();
251+
case(STDOUT_FILENO):
252+
return getStdoutFile().get();
253+
case(STDERR_FILENO):
254+
return getStderrFile().get();
255+
default:
256+
llvm::errs() << "No temp file for the FD\n";
257+
return nullptr;
258+
}
259+
}
260+
179261
///\brief Describes the return result of the different routines that do the
180262
/// incremental compilation.
181263
///

unittests/CppInterOp/JitTest.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ TEST(JitTest, InsertOrReplaceJitSymbol) {
2020
#ifdef _WIN32
2121
GTEST_SKIP() << "Disabled on Windows. Needs fixing.";
2222
#endif
23+
if (TestUtils::g_use_oop_jit) {
24+
GTEST_SKIP() << "Test fails for OOP JIT builds";
25+
}
2326
std::vector<Decl*> Decls;
2427
std::string code = R"(
2528
extern "C" int printf(const char*,...);
@@ -42,6 +45,9 @@ TEST(JitTest, InsertOrReplaceJitSymbol) {
4245
TEST(Streams, StreamRedirect) {
4346
// printf and etc are fine here.
4447
// NOLINTBEGIN(cppcoreguidelines-pro-type-vararg)
48+
if (TestUtils::g_use_oop_jit) {
49+
GTEST_SKIP() << "Test fails for OOP JIT builds";
50+
}
4551
Cpp::BeginStdStreamCapture(Cpp::kStdOut);
4652
Cpp::BeginStdStreamCapture(Cpp::kStdErr);
4753
printf("StdOut is captured\n");
@@ -70,3 +76,26 @@ TEST(Streams, StreamRedirect) {
7076
EXPECT_STREQ(cerrs.c_str(), "Err\nStdErr\n");
7177
// NOLINTEND(cppcoreguidelines-pro-type-vararg)
7278
}
79+
80+
TEST(Streams, StreamRedirectJIT) {
81+
#ifdef EMSCRIPTEN
82+
GTEST_SKIP() << "Test fails for Emscipten builds";
83+
#endif
84+
if (llvm::sys::RunningOnValgrind())
85+
GTEST_SKIP() << "XFAIL due to Valgrind report";
86+
#ifdef _WIN32
87+
GTEST_SKIP() << "Disabled on Windows. Needs fixing.";
88+
#endif
89+
TestUtils::CreateInterpreter();
90+
91+
Cpp::BeginStdStreamCapture(Cpp::kStdOut);
92+
Cpp::BeginStdStreamCapture(Cpp::kStdErr);
93+
Interp->process(R"(
94+
#include <iostream>
95+
std::cout << "Hello World" << std::endl;
96+
)");
97+
std::string CapturedStringErr = Cpp::EndStdStreamCapture();
98+
std::string CapturedStringOut = Cpp::EndStdStreamCapture();
99+
100+
EXPECT_STREQ(CapturedStringOut.c_str(), "Hello World\n");
101+
}

0 commit comments

Comments
 (0)