1 | //===- unittests/Interpreter/InterpreterExtensionsTest.cpp ----------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // Unit tests for Clang's Interpreter library. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "clang/Interpreter/Interpreter.h" |
14 | |
15 | #include "clang/AST/Expr.h" |
16 | #include "clang/Frontend/CompilerInstance.h" |
17 | #include "clang/Sema/Lookup.h" |
18 | #include "clang/Sema/Sema.h" |
19 | |
20 | #include "llvm/ExecutionEngine/Orc/LLJIT.h" |
21 | #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" |
22 | #include "llvm/MC/TargetRegistry.h" |
23 | #include "llvm/Support/Error.h" |
24 | #include "llvm/Support/TargetSelect.h" |
25 | #include "llvm/Support/Threading.h" |
26 | #include "llvm/Testing/Support/Error.h" |
27 | |
28 | #include "gmock/gmock.h" |
29 | #include "gtest/gtest.h" |
30 | |
31 | #include <system_error> |
32 | |
33 | #if defined(_AIX) || defined(__MVS__) |
34 | #define CLANG_INTERPRETER_PLATFORM_CANNOT_CREATE_LLJIT |
35 | #endif |
36 | |
37 | using namespace clang; |
38 | namespace { |
39 | |
40 | static bool HostSupportsJit() { |
41 | auto J = llvm::orc::LLJITBuilder().create(); |
42 | if (J) |
43 | return true; |
44 | LLVMConsumeError(Err: llvm::wrap(Err: J.takeError())); |
45 | return false; |
46 | } |
47 | |
48 | // Some tests require a arm-registered-target |
49 | static bool IsARMTargetRegistered() { |
50 | llvm::Triple TT; |
51 | TT.setArch(Kind: llvm::Triple::arm); |
52 | TT.setVendor(llvm::Triple::UnknownVendor); |
53 | TT.setOS(llvm::Triple::UnknownOS); |
54 | |
55 | std::string UnusedErr; |
56 | return llvm::TargetRegistry::lookupTarget(Triple: TT.str(), Error&: UnusedErr); |
57 | } |
58 | |
59 | struct LLVMInitRAII { |
60 | LLVMInitRAII() { |
61 | llvm::InitializeAllTargets(); |
62 | llvm::InitializeAllTargetInfos(); |
63 | llvm::InitializeAllTargetMCs(); |
64 | llvm::InitializeAllAsmPrinters(); |
65 | } |
66 | ~LLVMInitRAII() { llvm::llvm_shutdown(); } |
67 | } LLVMInit; |
68 | |
69 | class TestCreateResetExecutor : public Interpreter { |
70 | public: |
71 | TestCreateResetExecutor(std::unique_ptr<CompilerInstance> CI, |
72 | llvm::Error &Err) |
73 | : Interpreter(std::move(CI), Err) {} |
74 | |
75 | llvm::Error testCreateJITBuilderError() { |
76 | JB = nullptr; |
77 | return Interpreter::CreateExecutor(); |
78 | } |
79 | |
80 | llvm::Error testCreateExecutor() { |
81 | JB = std::make_unique<llvm::orc::LLJITBuilder>(); |
82 | return Interpreter::CreateExecutor(); |
83 | } |
84 | |
85 | void resetExecutor() { Interpreter::ResetExecutor(); } |
86 | |
87 | private: |
88 | llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>> |
89 | CreateJITBuilder(CompilerInstance &CI) override { |
90 | if (JB) |
91 | return std::move(JB); |
92 | return llvm::make_error<llvm::StringError>(Args: "TestError" , Args: std::error_code()); |
93 | } |
94 | |
95 | std::unique_ptr<llvm::orc::LLJITBuilder> JB; |
96 | }; |
97 | |
98 | #ifdef CLANG_INTERPRETER_PLATFORM_CANNOT_CREATE_LLJIT |
99 | TEST(InterpreterExtensionsTest, DISABLED_ExecutorCreateReset) { |
100 | #else |
101 | TEST(InterpreterExtensionsTest, ExecutorCreateReset) { |
102 | #endif |
103 | // Make sure we can create the executer on the platform. |
104 | if (!HostSupportsJit()) |
105 | GTEST_SKIP(); |
106 | |
107 | clang::IncrementalCompilerBuilder CB; |
108 | llvm::Error ErrOut = llvm::Error::success(); |
109 | TestCreateResetExecutor Interp(cantFail(ValOrErr: CB.CreateCpp()), ErrOut); |
110 | cantFail(Err: std::move(ErrOut)); |
111 | EXPECT_THAT_ERROR(Interp.testCreateJITBuilderError(), |
112 | llvm::FailedWithMessage("TestError" )); |
113 | cantFail(Err: Interp.testCreateExecutor()); |
114 | Interp.resetExecutor(); |
115 | cantFail(Err: Interp.testCreateExecutor()); |
116 | EXPECT_THAT_ERROR(Interp.testCreateExecutor(), |
117 | llvm::FailedWithMessage("Operation failed. " |
118 | "Execution engine exists" )); |
119 | } |
120 | |
121 | class RecordRuntimeIBMetrics : public Interpreter { |
122 | struct NoopRuntimeInterfaceBuilder : public RuntimeInterfaceBuilder { |
123 | NoopRuntimeInterfaceBuilder(Sema &S) : S(S) {} |
124 | |
125 | TransformExprFunction *getPrintValueTransformer() override { |
126 | TransformerQueries += 1; |
127 | return &noop; |
128 | } |
129 | |
130 | static ExprResult noop(RuntimeInterfaceBuilder *Builder, Expr *E, |
131 | ArrayRef<Expr *> FixedArgs) { |
132 | auto *B = static_cast<NoopRuntimeInterfaceBuilder *>(Builder); |
133 | B->TransformedExprs += 1; |
134 | return B->S.ActOnFinishFullExpr(Expr: E, /*DiscardedValue=*/false); |
135 | } |
136 | |
137 | Sema &S; |
138 | size_t TransformedExprs = 0; |
139 | size_t TransformerQueries = 0; |
140 | }; |
141 | |
142 | public: |
143 | // Inherit with using wouldn't make it public |
144 | RecordRuntimeIBMetrics(std::unique_ptr<CompilerInstance> CI, llvm::Error &Err) |
145 | : Interpreter(std::move(CI), Err) {} |
146 | |
147 | std::unique_ptr<RuntimeInterfaceBuilder> FindRuntimeInterface() override { |
148 | assert(RuntimeIBPtr == nullptr && "We create the builder only once" ); |
149 | Sema &S = getCompilerInstance()->getSema(); |
150 | auto RuntimeIB = std::make_unique<NoopRuntimeInterfaceBuilder>(args&: S); |
151 | RuntimeIBPtr = RuntimeIB.get(); |
152 | return RuntimeIB; |
153 | } |
154 | |
155 | NoopRuntimeInterfaceBuilder *RuntimeIBPtr = nullptr; |
156 | }; |
157 | |
158 | TEST(InterpreterExtensionsTest, FindRuntimeInterface) { |
159 | clang::IncrementalCompilerBuilder CB; |
160 | llvm::Error ErrOut = llvm::Error::success(); |
161 | RecordRuntimeIBMetrics Interp(cantFail(ValOrErr: CB.CreateCpp()), ErrOut); |
162 | cantFail(Err: std::move(ErrOut)); |
163 | cantFail(ValOrErr: Interp.Parse(Code: "int a = 1; a" )); |
164 | cantFail(ValOrErr: Interp.Parse(Code: "int b = 2; b" )); |
165 | cantFail(ValOrErr: Interp.Parse(Code: "int c = 3; c" )); |
166 | EXPECT_EQ(3U, Interp.RuntimeIBPtr->TransformedExprs); |
167 | EXPECT_EQ(1U, Interp.RuntimeIBPtr->TransformerQueries); |
168 | } |
169 | |
170 | class CustomJBInterpreter : public Interpreter { |
171 | using CustomJITBuilderCreatorFunction = |
172 | std::function<llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>()>; |
173 | CustomJITBuilderCreatorFunction JBCreator = nullptr; |
174 | |
175 | public: |
176 | CustomJBInterpreter(std::unique_ptr<CompilerInstance> CI, llvm::Error &ErrOut) |
177 | : Interpreter(std::move(CI), ErrOut) {} |
178 | |
179 | ~CustomJBInterpreter() override { |
180 | // Skip cleanUp() because it would trigger LLJIT default dtors |
181 | Interpreter::ResetExecutor(); |
182 | } |
183 | |
184 | void setCustomJITBuilderCreator(CustomJITBuilderCreatorFunction Fn) { |
185 | JBCreator = std::move(Fn); |
186 | } |
187 | |
188 | llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>> |
189 | CreateJITBuilder(CompilerInstance &CI) override { |
190 | if (JBCreator) |
191 | return JBCreator(); |
192 | return Interpreter::CreateJITBuilder(CI); |
193 | } |
194 | |
195 | llvm::Error CreateExecutor() { return Interpreter::CreateExecutor(); } |
196 | }; |
197 | |
198 | #ifdef CLANG_INTERPRETER_PLATFORM_CANNOT_CREATE_LLJIT |
199 | TEST(InterpreterExtensionsTest, DISABLED_DefaultCrossJIT) { |
200 | #else |
201 | TEST(InterpreterExtensionsTest, DefaultCrossJIT) { |
202 | #endif |
203 | if (!IsARMTargetRegistered()) |
204 | GTEST_SKIP(); |
205 | |
206 | IncrementalCompilerBuilder CB; |
207 | CB.SetTargetTriple("armv6-none-eabi" ); |
208 | auto CI = cantFail(ValOrErr: CB.CreateCpp()); |
209 | llvm::Error ErrOut = llvm::Error::success(); |
210 | CustomJBInterpreter Interp(std::move(CI), ErrOut); |
211 | cantFail(Err: std::move(ErrOut)); |
212 | cantFail(Err: Interp.CreateExecutor()); |
213 | } |
214 | |
215 | #ifdef CLANG_INTERPRETER_PLATFORM_CANNOT_CREATE_LLJIT |
216 | TEST(InterpreterExtensionsTest, DISABLED_CustomCrossJIT) { |
217 | #else |
218 | TEST(InterpreterExtensionsTest, CustomCrossJIT) { |
219 | #endif |
220 | if (!IsARMTargetRegistered()) |
221 | GTEST_SKIP(); |
222 | |
223 | std::string TargetTriple = "armv6-none-eabi" ; |
224 | |
225 | IncrementalCompilerBuilder CB; |
226 | CB.SetTargetTriple(TargetTriple); |
227 | auto CI = cantFail(ValOrErr: CB.CreateCpp()); |
228 | llvm::Error ErrOut = llvm::Error::success(); |
229 | CustomJBInterpreter Interp(std::move(CI), ErrOut); |
230 | cantFail(Err: std::move(ErrOut)); |
231 | |
232 | using namespace llvm::orc; |
233 | LLJIT *JIT = nullptr; |
234 | std::vector<std::unique_ptr<llvm::MemoryBuffer>> Objs; |
235 | Interp.setCustomJITBuilderCreator([&]() { |
236 | auto JTMB = JITTargetMachineBuilder(llvm::Triple(TargetTriple)); |
237 | JTMB.setCPU("cortex-m0plus" ); |
238 | auto JB = std::make_unique<LLJITBuilder>(); |
239 | JB->setJITTargetMachineBuilder(JTMB); |
240 | JB->setPlatformSetUp(setUpInactivePlatform); |
241 | JB->setNotifyCreatedCallback([&](LLJIT &J) { |
242 | ObjectLayer &ObjLayer = J.getObjLinkingLayer(); |
243 | auto *JITLinkObjLayer = llvm::dyn_cast<ObjectLinkingLayer>(Val: &ObjLayer); |
244 | JITLinkObjLayer->setReturnObjectBuffer( |
245 | [&Objs](std::unique_ptr<llvm::MemoryBuffer> MB) { |
246 | Objs.push_back(x: std::move(MB)); |
247 | }); |
248 | JIT = &J; |
249 | return llvm::Error::success(); |
250 | }); |
251 | return JB; |
252 | }); |
253 | |
254 | EXPECT_EQ(0U, Objs.size()); |
255 | cantFail(Err: Interp.CreateExecutor()); |
256 | cantFail(Err: Interp.ParseAndExecute(Code: "int a = 1;" )); |
257 | ExecutorAddr Addr = cantFail(ValOrErr: JIT->lookup(UnmangledName: "a" )); |
258 | EXPECT_NE(0U, Addr.getValue()); |
259 | EXPECT_EQ(1U, Objs.size()); |
260 | } |
261 | |
262 | } // end anonymous namespace |
263 | |