PageRenderTime 61ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/CS/branches/soc2011/lighter2ocl/plugins/misc/opencl/library.cpp

#
C++ | 410 lines | 307 code | 53 blank | 50 comment | 63 complexity | 6d1fe392c50cbe41ecd224cea09b73ec MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, LGPL-2.0
  1. #include "cssysdef.h"
  2. #include <csutil/regexp.h>
  3. #include <csutil/stringarray.h>
  4. #include "library.h"
  5. #include "manager.h"
  6. #include "context.h"
  7. #include "event.h"
  8. CS_PLUGIN_NAMESPACE_BEGIN(CL)
  9. {
  10. Library::Library(Manager* p, iStringArray* source) : scfImplementationType(this, nullptr),
  11. parent(p), source(source)
  12. {
  13. // base regex to find the kernel declarations
  14. csRegExpMatcher kernelExp("(__)?kernel[:space:]+(__attribute__\\({2}.+\\){2}[:space:]+)*void[:space:]+\\(.*?\\)[:space:]+{",true);
  15. // regex to find macros/etc.
  16. csRegExpMatcher macroExp("[:alpha:]+\\(.*\\)",true);
  17. // access mode expression
  18. csRegExpMatcher accessExp("(^|[[:space:]*])(__)?(write|read)_only([[:space:]*]|$)",true);
  19. // address mode expression
  20. csRegExpMatcher addressExp("(^|[[:space:]*])(__)?(global|local|constant|private)([[:space:]*]|$)",true);
  21. // type expressions
  22. csRegExpMatcher imageExp("(^|[[:space:]*])image[23]d_t([[:space:]*]|$)",true);
  23. csRegExpMatcher samplerExp("(^|[[:space:]*])sampler_t([[:space:]*]|$)",true);
  24. for(size_t i = 0; i < source->GetSize(); ++i)
  25. {
  26. csString part(source->Get(i));
  27. csArray<csRegExpMatch> matches;
  28. // find matches
  29. csRegExpMatchError error = kernelExp.Match(part.GetData(), matches);
  30. CS_ASSERT(error == csrxNoError || error == csrxNoMatch);
  31. // process matches further
  32. for(size_t j = 0; j < matches.GetSize(); ++j)
  33. {
  34. size_t start = matches[j].startOffset + 8; // skip leading __kernel
  35. size_t size = matches[j].endOffset - start - 1; // skip trailing {
  36. csString match = part.Slice(start, size);
  37. match.LTrim(); // strip leading whitespaces
  38. while(match[0] == '_')
  39. {
  40. // we got an attribute here - strip it
  41. match.DeleteAt(0,13); // remove "__attribute__"
  42. size_t end = 2;
  43. size_t brackets = 2;
  44. while(brackets > 0)
  45. {
  46. if(match[end] == '(')
  47. {
  48. ++brackets;
  49. }
  50. else if(match[end] == ')')
  51. {
  52. --brackets;
  53. }
  54. ++end;
  55. }
  56. match.DeleteAt(0,end); // remove the arguments for the attribute
  57. match.LTrim(); // remove whitespaces following the attribute
  58. }
  59. match.DeleteAt(0, 4); // strip leading "void"
  60. match.LTrim(); // strip whitespaces following the void
  61. // get name
  62. size_t open = match.FindFirst("(");
  63. CS_ASSERT(open != (size_t)-1); // regex error
  64. csString name = match.Slice(0, open);
  65. name.RTrim(); // strip trailing whitespaces from the name
  66. match.DeleteAt(0, open+1); // strip name and leading (
  67. match.DeleteAt(match.Length() - 1); // strip trailing )
  68. match.Trim();
  69. // check for macro calls
  70. if(macroExp.Match(match.GetData()) != csrxNoMatch)
  71. {
  72. // we don't allow macro function calls in the kernel defintion
  73. // (actually we don't even allow macros or even some typedefs,
  74. // but we can't check for those)
  75. CS_ASSERT(false);
  76. }
  77. // split arguments up
  78. csStringArray arguments(match.GetData(), ",");
  79. // create meta data
  80. KernelMetaData data;
  81. data.name = name;
  82. data.argCount = arguments.GetSize();
  83. data.args = new int[data.argCount];
  84. kernelData.PutUnique(data.name, data);
  85. // parse arguments
  86. for(size_t k = 0; k < arguments.GetSize(); ++k)
  87. {
  88. data.args[k] = 0;
  89. csString argString(arguments[k]);
  90. argString.Trim();
  91. csArray<csRegExpMatch> result;
  92. // parse access mode
  93. if(accessExp.Match(argString.GetData(), result) == csrxNoError)
  94. {
  95. if(argString.FindFirst("write", result[0].startOffset)
  96. < result[0].endOffset)
  97. {
  98. data.args[k] |= KernelMetaData::ARG_WRITE;
  99. }
  100. else
  101. {
  102. data.args[k] |= KernelMetaData::ARG_READ;
  103. }
  104. }
  105. else
  106. {
  107. data.args[k] |= KernelMetaData::ARG_READ_WRITE;
  108. }
  109. // check for image types
  110. if(imageExp.Match(argString.GetData(), result) == csrxNoError)
  111. {
  112. if(argString.FindFirst("image2d_t", result[0].startOffset)
  113. < result[0].endOffset)
  114. {
  115. data.args[k] |= KernelMetaData::ARG_IMAGE2D;
  116. }
  117. else
  118. {
  119. data.args[k] |= KernelMetaData::ARG_IMAGE3D;
  120. }
  121. }
  122. // check for sampler type
  123. else if(samplerExp.Match(argString.GetData()) == csrxNoError)
  124. {
  125. data.args[k] |= KernelMetaData::ARG_SAMPLER;
  126. }
  127. else if(argString.FindFirst("*") != (size_t)-1)
  128. {
  129. data.args[k] |= KernelMetaData::ARG_BUFFER;
  130. }
  131. else
  132. {
  133. data.args[k] |= KernelMetaData::ARG_DATA;
  134. }
  135. // parse address mode
  136. if(addressExp.Match(argString.GetData(), result) == csrxNoError)
  137. {
  138. size_t start = result[0].startOffset;
  139. size_t end = result[0].endOffset;
  140. if(argString.FindFirst("global", start) < end)
  141. {
  142. data.args[k] |= KernelMetaData::ARG_GLOBAL;
  143. }
  144. else if(argString.FindFirst("local", start) < end)
  145. {
  146. data.args[k] |= KernelMetaData::ARG_LOCAL;
  147. }
  148. else if(argString.FindFirst("constant", start) < end)
  149. {
  150. data.args[k] |= KernelMetaData::ARG_CONST;
  151. }
  152. else
  153. {
  154. data.args[k] &= ~KernelMetaData::ARG_STORAGE_MASK; // ARG_PRIVATE
  155. }
  156. }
  157. else
  158. {
  159. switch(data.args[k] & KernelMetaData::ARG_TYPE_MASK)
  160. {
  161. case KernelMetaData::ARG_IMAGE2D:
  162. case KernelMetaData::ARG_IMAGE3D:
  163. data.args[k] |= KernelMetaData::ARG_GLOBAL;
  164. break;
  165. default:
  166. data.args[k] &= ~KernelMetaData::ARG_STORAGE_MASK; // ARG_PRIVATE
  167. }
  168. }
  169. }
  170. }
  171. }
  172. }
  173. Library::~Library()
  174. {
  175. csHash<cl_program, csRef<Context> >::GlobalIterator it = handles.GetIterator();
  176. while(it.HasNext())
  177. {
  178. cl_program p = it.Next();
  179. cl_int error = clReleaseProgram(p);
  180. CS_ASSERT(error == CL_SUCCESS);
  181. }
  182. csHash<KernelMetaData, csString>::GlobalIterator kernelIt = kernelData.GetIterator();
  183. while(kernelIt.HasNext())
  184. {
  185. delete [] kernelIt.Next().args;
  186. }
  187. }
  188. csRef<Event> Library::GetHandle(Context* c, cl_program& p)
  189. {
  190. if(handles.Contains(c))
  191. {
  192. p = *handles.GetElementPointer(c);
  193. return *buildEvents.GetElementPointer(p);
  194. }
  195. else
  196. {
  197. const char** strings = new const char*[source->GetSize()];
  198. if(strings == nullptr)
  199. {
  200. // OOM
  201. return csRef<Event>(nullptr);
  202. }
  203. for(size_t i = 0; i < source->GetSize(); ++i)
  204. {
  205. strings[i] = source->Get(i);
  206. }
  207. cl_int error;
  208. p = clCreateProgramWithSource(c->GetHandle(), source->GetSize(),
  209. strings, nullptr, &error);
  210. if(error != CL_SUCCESS)
  211. {
  212. return csRef<Event>(nullptr);
  213. }
  214. // build for all devices in context
  215. //@@@todo: maybe we want to somehow specify options here?
  216. csRef<Event> e = csPtr<Event>(new Event());
  217. error = clBuildProgram(p, /* deviceCount */ 0, /* devices */ nullptr,
  218. /* options */ "", BuildHandler, (void*)e);
  219. if(error != CL_SUCCESS)
  220. {
  221. clReleaseProgram(p);
  222. return csRef<Event>(nullptr);
  223. }
  224. // the event has to stay alive until the build is done
  225. e->IncRef();
  226. handles.PutUnique(c, p);
  227. buildEvents.PutUnique(p, e);
  228. return e;
  229. }
  230. }
  231. void Library::Precache()
  232. {
  233. // create the program in all contexts
  234. // (will also start the builds for all devices)
  235. const csRefArray<Context>& contexts = parent->GetContexts();
  236. csRefArray<Context>::ConstIterator it = contexts.GetIterator();
  237. while(it.HasNext())
  238. {
  239. cl_program p;
  240. GetHandle(it.Next(), p);
  241. }
  242. }
  243. csPtr<iKernel> Library::CreateKernel(const char* name)
  244. {
  245. if(!kernelData.Contains(name))
  246. {
  247. // unknown kernel
  248. csPtr<iKernel>(nullptr);
  249. }
  250. const KernelMetaData& data = *kernelData.GetElementPointer(name);
  251. // create new kernel
  252. csRef<iKernel> kernel;
  253. kernel.AttachNew(new Kernel(this, data));
  254. return csPtr<iKernel>(kernel);
  255. }
  256. void Library::BuildHandler(cl_program p, void* data)
  257. {
  258. csRef<Event> e = static_cast<Event*>(data);
  259. // release the reference we retained
  260. // to keep the event alive during build
  261. // (it's kept alive by the ref while this function
  262. // is executed)
  263. e->DecRef();
  264. // keep track whether the build
  265. // was successful
  266. bool success = true;
  267. // obtain device list
  268. cl_uint deviceCount;
  269. cl_device_id* devices = GetDevices(p, deviceCount);
  270. if(devices == nullptr)
  271. {
  272. // error retrieving device list
  273. success = false;
  274. }
  275. // check whether it built properly on all devices
  276. for(cl_uint i = 0; i < deviceCount; ++i)
  277. {
  278. cl_build_status status;
  279. cl_int error = clGetProgramBuildInfo(p, devices[i], CL_PROGRAM_BUILD_STATUS,
  280. sizeof(cl_build_status), &status, nullptr);
  281. if(error != CL_SUCCESS)
  282. {
  283. success = false;
  284. continue;
  285. }
  286. if(status != CL_BUILD_SUCCESS)
  287. {
  288. // build failed, try to retrieve the error
  289. success = false;
  290. PrintLog(p, devices[i]);
  291. }
  292. }
  293. // free device list
  294. delete [] devices;
  295. // fire the build event
  296. e->Fire(success);
  297. }
  298. cl_device_id* Library::GetDevices(cl_program p, cl_uint& deviceCount)
  299. {
  300. // get devices this program is associated with
  301. cl_int error;
  302. error = clGetProgramInfo(p, CL_PROGRAM_NUM_DEVICES, sizeof(cl_uint),
  303. &deviceCount, nullptr);
  304. if(error != CL_SUCCESS)
  305. {
  306. // failed to retrieve the number of devices
  307. deviceCount = 0;
  308. return nullptr;
  309. }
  310. cl_device_id* devices = new cl_device_id[deviceCount];
  311. if(devices == nullptr)
  312. {
  313. // OOM
  314. deviceCount = 0;
  315. return nullptr;
  316. }
  317. error = clGetProgramInfo(p, CL_PROGRAM_DEVICES, deviceCount*sizeof(cl_device_id),
  318. devices, nullptr);
  319. if(error != CL_SUCCESS)
  320. {
  321. // failed to retrieve device list
  322. delete [] devices;
  323. deviceCount = 0;
  324. return nullptr;
  325. }
  326. return devices;
  327. }
  328. void Library::PrintLog(cl_program p, cl_device_id d)
  329. {
  330. // get the size of the build log
  331. size_t logSize;
  332. cl_int error;
  333. error = clGetProgramBuildInfo(p, d, CL_PROGRAM_BUILD_LOG,
  334. 0, nullptr, &logSize);
  335. if(error != CL_SUCCESS)
  336. {
  337. return;
  338. }
  339. // allocate resssources to hold the log
  340. char* buildLog = (char*)cs_malloc(logSize);
  341. if(buildLog == nullptr)
  342. {
  343. // OOM
  344. return;
  345. }
  346. // obtain the log
  347. error = clGetProgramBuildInfo(p, d, CL_PROGRAM_BUILD_LOG,
  348. logSize, buildLog, nullptr);
  349. if(error == CL_SUCCESS)
  350. {
  351. // print the build log here - maybe only if verbose?
  352. }
  353. // free ressources allocated for the build log
  354. cs_free(buildLog);
  355. }
  356. }
  357. CS_PLUGIN_NAMESPACE_END(CL)