1 //===-- examples/ParallelJIT/ParallelJIT.cpp - Exercise threaded-safe JIT -===//
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
7 //===----------------------------------------------------------------------===//
11 // This test program creates two LLVM functions then calls them from three
12 // separate threads. It requires the pthreads library.
13 // The three threads are created and then block waiting on a condition variable.
14 // Once all threads are blocked on the conditional variable, the main thread
15 // wakes them up. This complicated work is performed so that all three threads
16 // call into the JIT at the same time (or the best possible approximation of the
17 // same time). This test had assertion errors until I got the locking right.
19 //===----------------------------------------------------------------------===//
21 #include "llvm/ADT/APInt.h"
22 #include "llvm/ADT/STLExtras.h"
23 #include "llvm/ExecutionEngine/ExecutionEngine.h"
24 #include "llvm/ExecutionEngine/GenericValue.h"
25 #include "llvm/IR/Argument.h"
26 #include "llvm/IR/BasicBlock.h"
27 #include "llvm/IR/Constants.h"
28 #include "llvm/IR/DerivedTypes.h"
29 #include "llvm/IR/Function.h"
30 #include "llvm/IR/InstrTypes.h"
31 #include "llvm/IR/Instruction.h"
32 #include "llvm/IR/Instructions.h"
33 #include "llvm/IR/LLVMContext.h"
34 #include "llvm/IR/Module.h"
35 #include "llvm/IR/Type.h"
36 #include "llvm/Support/Casting.h"
37 #include "llvm/Support/TargetSelect.h"
49 static Function
* createAdd1(Module
*M
) {
50 LLVMContext
&Context
= M
->getContext();
51 // Create the add1 function entry and insert this entry into module M. The
52 // function will have a return type of "int" and take an argument of "int".
54 Function::Create(FunctionType::get(Type::getInt32Ty(Context
),
55 {Type::getInt32Ty(Context
)}, false),
56 Function::ExternalLinkage
, "add1", M
);
58 // Add a basic block to the function. As before, it automatically inserts
59 // because of the last argument.
60 BasicBlock
*BB
= BasicBlock::Create(Context
, "EntryBlock", Add1F
);
62 // Get pointers to the constant `1'.
63 Value
*One
= ConstantInt::get(Type::getInt32Ty(Context
), 1);
65 // Get pointers to the integer argument of the add1 function...
66 assert(Add1F
->arg_begin() != Add1F
->arg_end()); // Make sure there's an arg
67 Argument
*ArgX
= &*Add1F
->arg_begin(); // Get the arg
68 ArgX
->setName("AnArg"); // Give it a nice symbolic name for fun.
70 // Create the add instruction, inserting it into the end of BB.
71 Instruction
*Add
= BinaryOperator::CreateAdd(One
, ArgX
, "addresult", BB
);
73 // Create the return instruction and add it to the basic block
74 ReturnInst::Create(Context
, Add
, BB
);
76 // Now, function add1 is ready.
80 static Function
*CreateFibFunction(Module
*M
) {
81 LLVMContext
&Context
= M
->getContext();
82 // Create the fib function and insert it into module M. This function is said
83 // to return an int and take an int parameter.
84 FunctionType
*FibFTy
= FunctionType::get(Type::getInt32Ty(Context
),
85 {Type::getInt32Ty(Context
)}, false);
87 Function::Create(FibFTy
, Function::ExternalLinkage
, "fib", M
);
89 // Add a basic block to the function.
90 BasicBlock
*BB
= BasicBlock::Create(Context
, "EntryBlock", FibF
);
92 // Get pointers to the constants.
93 Value
*One
= ConstantInt::get(Type::getInt32Ty(Context
), 1);
94 Value
*Two
= ConstantInt::get(Type::getInt32Ty(Context
), 2);
96 // Get pointer to the integer argument of the add1 function...
97 Argument
*ArgX
= &*FibF
->arg_begin(); // Get the arg.
98 ArgX
->setName("AnArg"); // Give it a nice symbolic name for fun.
100 // Create the true_block.
101 BasicBlock
*RetBB
= BasicBlock::Create(Context
, "return", FibF
);
102 // Create an exit block.
103 BasicBlock
*RecurseBB
= BasicBlock::Create(Context
, "recurse", FibF
);
105 // Create the "if (arg < 2) goto exitbb"
106 Value
*CondInst
= new ICmpInst(*BB
, ICmpInst::ICMP_SLE
, ArgX
, Two
, "cond");
107 BranchInst::Create(RetBB
, RecurseBB
, CondInst
, BB
);
110 ReturnInst::Create(Context
, One
, RetBB
);
113 Value
*Sub
= BinaryOperator::CreateSub(ArgX
, One
, "arg", RecurseBB
);
114 Value
*CallFibX1
= CallInst::Create(FibF
, Sub
, "fibx1", RecurseBB
);
117 Sub
= BinaryOperator::CreateSub(ArgX
, Two
, "arg", RecurseBB
);
118 Value
*CallFibX2
= CallInst::Create(FibF
, Sub
, "fibx2", RecurseBB
);
122 BinaryOperator::CreateAdd(CallFibX1
, CallFibX2
, "addresult", RecurseBB
);
124 // Create the return instruction and add it to the basic block
125 ReturnInst::Create(Context
, Sum
, RecurseBB
);
130 struct threadParams
{
136 // We block the subthreads just before they begin to execute:
137 // we want all of them to call into the JIT at the same time,
138 // to verify that the locking is working correctly.
147 int result
= pthread_cond_init( &condition
, nullptr );
149 assert( result
== 0 );
151 result
= pthread_mutex_init( &mutex
, nullptr );
152 assert( result
== 0 );
157 int result
= pthread_cond_destroy( &condition
);
159 assert( result
== 0 );
161 result
= pthread_mutex_destroy( &mutex
);
162 assert( result
== 0 );
165 // All threads will stop here until another thread calls releaseThreads
168 int result
= pthread_mutex_lock( &mutex
);
170 assert( result
== 0 );
172 //~ std::cout << "block() n " << n << " waitFor " << waitFor << std::endl;
174 assert( waitFor
== 0 || n
<= waitFor
);
175 if ( waitFor
> 0 && n
== waitFor
)
177 // There are enough threads blocked that we can release all of them
178 std::cout
<< "Unblocking threads from block()" << std::endl
;
183 // We just need to wait until someone unblocks us
184 result
= pthread_cond_wait( &condition
, &mutex
);
185 assert( result
== 0 );
188 // unlock the mutex before returning
189 result
= pthread_mutex_unlock( &mutex
);
190 assert( result
== 0 );
193 // If there are num or more threads blocked, it will signal them all
194 // Otherwise, this thread blocks until there are enough OTHER threads
196 void releaseThreads( size_t num
)
198 int result
= pthread_mutex_lock( &mutex
);
200 assert( result
== 0 );
203 std::cout
<< "Unblocking threads from releaseThreads()" << std::endl
;
209 pthread_cond_wait( &condition
, &mutex
);
212 // unlock the mutex before returning
213 result
= pthread_mutex_unlock( &mutex
);
214 assert( result
== 0 );
218 void unblockThreads()
220 // Reset the counters to zero: this way, if any new threads
221 // enter while threads are exiting, they will block instead
222 // of triggering a new release of threads
225 // Reset waitFor to zero: this way, if waitFor threads enter
226 // while threads are exiting, they will block instead of
227 // triggering a new release of threads
230 int result
= pthread_cond_broadcast( &condition
);
237 pthread_cond_t condition
;
238 pthread_mutex_t mutex
;
241 static WaitForThreads synchronize
;
243 void* callFunc( void* param
)
245 struct threadParams
* p
= (struct threadParams
*) param
;
247 // Call the `foo' function with no arguments:
248 std::vector
<GenericValue
> Args(1);
249 Args
[0].IntVal
= APInt(32, p
->value
);
251 synchronize
.block(); // wait until other threads are at this point
252 GenericValue gv
= p
->EE
->runFunction(p
->F
, Args
);
254 return (void*)(intptr_t)gv
.IntVal
.getZExtValue();
258 InitializeNativeTarget();
261 // Create some module to put our function into it.
262 std::unique_ptr
<Module
> Owner
= std::make_unique
<Module
>("test", Context
);
263 Module
*M
= Owner
.get();
265 Function
* add1F
= createAdd1( M
);
266 Function
* fibF
= CreateFibFunction( M
);
268 // Now we create the JIT.
269 ExecutionEngine
* EE
= EngineBuilder(std::move(Owner
)).create();
271 //~ std::cout << "We just constructed this LLVM module:\n\n" << *M;
272 //~ std::cout << "\n\nRunning foo: " << std::flush;
274 // Create one thread for add1 and two threads for fib
275 struct threadParams add1
= { EE
, add1F
, 1000 };
276 struct threadParams fib1
= { EE
, fibF
, 39 };
277 struct threadParams fib2
= { EE
, fibF
, 42 };
279 pthread_t add1Thread
;
280 int result
= pthread_create( &add1Thread
, nullptr, callFunc
, &add1
);
282 std::cerr
<< "Could not create thread" << std::endl
;
286 pthread_t fibThread1
;
287 result
= pthread_create( &fibThread1
, nullptr, callFunc
, &fib1
);
289 std::cerr
<< "Could not create thread" << std::endl
;
293 pthread_t fibThread2
;
294 result
= pthread_create( &fibThread2
, nullptr, callFunc
, &fib2
);
296 std::cerr
<< "Could not create thread" << std::endl
;
300 synchronize
.releaseThreads(3); // wait until other threads are at this point
303 result
= pthread_join( add1Thread
, &returnValue
);
305 std::cerr
<< "Could not join thread" << std::endl
;
308 std::cout
<< "Add1 returned " << intptr_t(returnValue
) << std::endl
;
310 result
= pthread_join( fibThread1
, &returnValue
);
312 std::cerr
<< "Could not join thread" << std::endl
;
315 std::cout
<< "Fib1 returned " << intptr_t(returnValue
) << std::endl
;
317 result
= pthread_join( fibThread2
, &returnValue
);
319 std::cerr
<< "Could not join thread" << std::endl
;
322 std::cout
<< "Fib2 returned " << intptr_t(returnValue
) << std::endl
;