llvm.org GIT mirror llvm / e8cdc8b
For PR418: Add an example program that utilizes multiple threads in the JIT to process work. This was used by Evan Jones as the original test case for ensuring that the ExecutionEngine was thread safe. Original source by Evan Jones (adapted from other LLVM JIT examples) and made LLVM style compliant by Reid Spencer. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@22411 91177308-0d34-0410-b5e6-96231b3b80d8 Reid Spencer 14 years ago
3 changed file(s) with 312 addition(s) and 1 deletion(s). Raw diff Collapse all Expand all
1010 include $(LEVEL)/Makefile.config
1111
1212 #PARALLEL_DIRS:= $(patsubst %/Makefile,%,$(wildcard $(SourceDir)/*/Makefile))
13 PARALLEL_DIRS:= Fibonacci HowToUseJIT ModuleMaker BFtoLLVM
13 PARALLEL_DIRS:= ParallelJIT Fibonacci HowToUseJIT ModuleMaker BFtoLLVM
1414
1515 include $(LEVEL)/Makefile.common
0 ##===- examples/HowToUseJIT/Makefile -----------------------*- Makefile -*-===##
1 #
2 # The LLVM Compiler Infrastructure
3 #
4 # This file was developed by Valery A. Khamenya and is distributed under
5 # the University of Illinois Open Source License. See LICENSE.TXT for details.
6 #
7 ##===----------------------------------------------------------------------===##
8 LEVEL = ../..
9 TOOLNAME = ParallelJIT
10 EXAMPLE_TOOL = 1
11
12 # Enable JIT support
13 LLVMLIBS := JIT
14
15 include $(LEVEL)/Makefile.common
0 //===-- examples/ParallelJIT/ParallelJIT.cpp - Exercise threaded-safe JIT -===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file was developed by Evan Jones and is distributed under the
5 // University of Illinois Open Source License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Parallel JIT
10 //
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.
18
19 #include
20 #include "llvm/Module.h"
21 #include "llvm/Constants.h"
22 #include "llvm/Type.h"
23 #include "llvm/Instructions.h"
24 #include "llvm/ModuleProvider.h"
25 #include "llvm/ExecutionEngine/ExecutionEngine.h"
26 #include "llvm/ExecutionEngine/GenericValue.h"
27 #include
28 using namespace llvm;
29
30 static Function* createAdd1( Module* M )
31 {
32 // Create the add1 function entry and insert this entry into module M. The
33 // function will have a return type of "int" and take an argument of "int".
34 // The '0' terminates the list of argument types.
35 Function *Add1F = M->getOrInsertFunction("add1", Type::IntTy, Type::IntTy, 0);
36
37 // Add a basic block to the function. As before, it automatically inserts
38 // because of the last argument.
39 BasicBlock *BB = new BasicBlock("EntryBlock", Add1F);
40
41 // Get pointers to the constant `1'.
42 Value *One = ConstantSInt::get(Type::IntTy, 1);
43
44 // Get pointers to the integer argument of the add1 function...
45 assert(Add1F->arg_begin() != Add1F->arg_end()); // Make sure there's an arg
46 Argument *ArgX = Add1F->arg_begin(); // Get the arg
47 ArgX->setName("AnArg"); // Give it a nice symbolic name for fun.
48
49 // Create the add instruction, inserting it into the end of BB.
50 Instruction *Add = BinaryOperator::createAdd(One, ArgX, "addresult", BB);
51
52 // Create the return instruction and add it to the basic block
53 new ReturnInst(Add, BB);
54
55 // Now, function add1 is ready.
56 return Add1F;
57 }
58
59 static Function *CreateFibFunction(Module *M)
60 {
61 // Create the fib function and insert it into module M. This function is said
62 // to return an int and take an int parameter.
63 Function *FibF = M->getOrInsertFunction("fib", Type::IntTy, Type::IntTy, 0);
64
65 // Add a basic block to the function.
66 BasicBlock *BB = new BasicBlock("EntryBlock", FibF);
67
68 // Get pointers to the constants.
69 Value *One = ConstantSInt::get(Type::IntTy, 1);
70 Value *Two = ConstantSInt::get(Type::IntTy, 2);
71
72 // Get pointer to the integer argument of the add1 function...
73 Argument *ArgX = FibF->arg_begin(); // Get the arg.
74 ArgX->setName("AnArg"); // Give it a nice symbolic name for fun.
75
76 // Create the true_block.
77 BasicBlock *RetBB = new BasicBlock("return", FibF);
78 // Create an exit block.
79 BasicBlock* RecurseBB = new BasicBlock("recurse", FibF);
80
81 // Create the "if (arg < 2) goto exitbb"
82 Value *CondInst = BinaryOperator::createSetLE(ArgX, Two, "cond", BB);
83 new BranchInst(RetBB, RecurseBB, CondInst, BB);
84
85 // Create: ret int 1
86 new ReturnInst(One, RetBB);
87
88 // create fib(x-1)
89 Value *Sub = BinaryOperator::createSub(ArgX, One, "arg", RecurseBB);
90 Value *CallFibX1 = new CallInst(FibF, Sub, "fibx1", RecurseBB);
91
92 // create fib(x-2)
93 Sub = BinaryOperator::createSub(ArgX, Two, "arg", RecurseBB);
94 Value *CallFibX2 = new CallInst(FibF, Sub, "fibx2", RecurseBB);
95
96 // fib(x-1)+fib(x-2)
97 Value *Sum =
98 BinaryOperator::createAdd(CallFibX1, CallFibX2, "addresult", RecurseBB);
99
100 // Create the return instruction and add it to the basic block
101 new ReturnInst(Sum, RecurseBB);
102
103 return FibF;
104 }
105
106 struct threadParams {
107 ExecutionEngine* EE;
108 Function* F;
109 int value;
110 };
111
112 // We block the subthreads just before they begin to execute:
113 // we want all of them to call into the JIT at the same time,
114 // to verify that the locking is working correctly.
115 class WaitForThreads
116 {
117 public:
118 WaitForThreads()
119 {
120 n = 0;
121 waitFor = 0;
122
123 int result = pthread_cond_init( &condition, NULL );
124 assert( result == 0 );
125
126 result = pthread_mutex_init( &mutex, NULL );
127 assert( result == 0 );
128 }
129
130 ~WaitForThreads()
131 {
132 int result = pthread_cond_destroy( &condition );
133 assert( result == 0 );
134
135 result = pthread_mutex_destroy( &mutex );
136 assert( result == 0 );
137 }
138
139 // All threads will stop here until another thread calls releaseThreads
140 void block()
141 {
142 int result = pthread_mutex_lock( &mutex );
143 assert( result == 0 );
144 n ++;
145 //~ std::cout << "block() n " << n << " waitFor " << waitFor << std::endl;
146
147 assert( waitFor == 0 || n <= waitFor );
148 if ( waitFor > 0 && n == waitFor )
149 {
150 // There are enough threads blocked that we can release all of them
151 std::cout << "Unblocking threads from block()" << std::endl;
152 unblockThreads();
153 }
154 else
155 {
156 // We just need to wait until someone unblocks us
157 result = pthread_cond_wait( &condition, &mutex );
158 assert( result == 0 );
159 }
160
161 // unlock the mutex before returning
162 result = pthread_mutex_unlock( &mutex );
163 assert( result == 0 );
164 }
165
166 // If there are num or more threads blocked, it will signal them all
167 // Otherwise, this thread blocks until there are enough OTHER threads
168 // blocked
169 void releaseThreads( size_t num )
170 {
171 int result = pthread_mutex_lock( &mutex );
172 assert( result == 0 );
173
174 if ( n >= num ) {
175 std::cout << "Unblocking threads from releaseThreads()" << std::endl;
176 unblockThreads();
177 }
178 else
179 {
180 waitFor = num;
181 pthread_cond_wait( &condition, &mutex );
182 }
183
184 // unlock the mutex before returning
185 result = pthread_mutex_unlock( &mutex );
186 assert( result == 0 );
187 }
188
189 private:
190 void unblockThreads()
191 {
192 // Reset the counters to zero: this way, if any new threads
193 // enter while threads are exiting, they will block instead
194 // of triggering a new release of threads
195 n = 0;
196
197 // Reset waitFor to zero: this way, if waitFor threads enter
198 // while threads are exiting, they will block instead of
199 // triggering a new release of threads
200 waitFor = 0;
201
202 int result = pthread_cond_broadcast( &condition );
203 assert( result == 0 );
204 }
205
206 size_t n;
207 size_t waitFor;
208 pthread_cond_t condition;
209 pthread_mutex_t mutex;
210 };
211
212 static WaitForThreads synchronize;
213
214 void* callFunc( void* param )
215 {
216 struct threadParams* p = (struct threadParams*) param;
217
218 // Call the `foo' function with no arguments:
219 std::vector Args(1);
220 Args[0].IntVal = p->value;
221
222 synchronize.block(); // wait until other threads are at this point
223 GenericValue gv = p->EE->runFunction(p->F, Args);
224
225 return (void*) gv.IntVal;
226 }
227
228 int main()
229 {
230 // Create some module to put our function into it.
231 Module *M = new Module("test");
232
233 Function* add1F = createAdd1( M );
234 Function* fibF = CreateFibFunction( M );
235
236 // Now we create the JIT.
237 ExistingModuleProvider* MP = new ExistingModuleProvider(M);
238 ExecutionEngine* EE = ExecutionEngine::create(MP, false);
239
240 //~ std::cout << "We just constructed this LLVM module:\n\n" << *M;
241 //~ std::cout << "\n\nRunning foo: " << std::flush;
242
243 // Create one thread for add1 and two threads for fib
244 struct threadParams add1 = { EE, add1F, 1000 };
245 struct threadParams fib1 = { EE, fibF, 39 };
246 struct threadParams fib2 = { EE, fibF, 42 };
247
248 pthread_t add1Thread;
249 int result = pthread_create( &add1Thread, NULL, callFunc, &add1 );
250 if ( result != 0 ) {
251 std::cerr << "Could not create thread" << std::endl;
252 return 1;
253 }
254
255 pthread_t fibThread1;
256 result = pthread_create( &fibThread1, NULL, callFunc, &fib1 );
257 if ( result != 0 ) {
258 std::cerr << "Could not create thread" << std::endl;
259 return 1;
260 }
261
262 pthread_t fibThread2;
263 result = pthread_create( &fibThread2, NULL, callFunc, &fib2 );
264 if ( result != 0 ) {
265 std::cerr << "Could not create thread" << std::endl;
266 return 1;
267 }
268
269 synchronize.releaseThreads(3); // wait until other threads are at this point
270
271 void* returnValue;
272 result = pthread_join( add1Thread, &returnValue );
273 if ( result != 0 ) {
274 std::cerr << "Could not join thread" << std::endl;
275 return 1;
276 }
277 std::cout << "Add1 returned " << (int) returnValue << std::endl;
278
279 result = pthread_join( fibThread1, &returnValue );
280 if ( result != 0 ) {
281 std::cerr << "Could not join thread" << std::endl;
282 return 1;
283 }
284 std::cout << "Fib1 returned " << (int) returnValue << std::endl;
285
286 result = pthread_join( fibThread2, &returnValue );
287 if ( result != 0 ) {
288 std::cerr << "Could not join thread" << std::endl;
289 return 1;
290 }
291 std::cout << "Fib2 returned " << (int) returnValue << std::endl;
292
293 return 0;
294 }