BfCodeGen.cpp 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153
  1. #pragma warning(push)
  2. #pragma warning(disable:4141)
  3. #pragma warning(disable:4146)
  4. #pragma warning(disable:4291)
  5. #pragma warning(disable:4244)
  6. #pragma warning(disable:4267)
  7. #pragma warning(disable:4624)
  8. #pragma warning(disable:4800)
  9. #pragma warning(disable:4996)
  10. #include "BfCompiler.h"
  11. #include "BfCodeGen.h"
  12. #include "BfIRCodeGen.h"
  13. #include "BfModule.h"
  14. #include "BeefySysLib/FileStream.h"
  15. #include "BeefySysLib/util/PerfTimer.h"
  16. #include "BeefySysLib/util/BeefPerf.h"
  17. #ifdef BF_PLATFORM_WINDOWS
  18. #include "../Backend/BeIRCodeGen.h"
  19. #include "../Backend/BeCOFFObject.h"
  20. #include "../Backend/BeLibManger.h"
  21. #endif
  22. //#define BF_IGNORE_BACKEND_HASH
  23. //#include "../X86.h"
  24. #ifdef _DEBUG
  25. //#define MAX_THREADS 3
  26. //#define MAX_THREADS 1
  27. //#define MAX_THREADS 6
  28. //#define BF_USE_CODEGEN_RELEASE_THUNK
  29. #else
  30. #define MAX_THREADS 6
  31. //#define MAX_THREADS 8
  32. //#define MAX_THREADS 1
  33. #endif
  34. //#define CODEGEN_DISABLE_CACHE
  35. //#define CODEGEN_DISABLE_CACHE_BEEFBACKEND
  36. // Emergency debugging only! Slow!
  37. //#define DBG_FORCE_SYNCHRONIZED
  38. // This is used for the Release DLL thunk and the build.dat file
  39. #define BF_CODEGEN_VERSION 14
  40. #undef DEBUG
  41. #pragma warning(disable:4267)
  42. #include "BeefySysLib/util/AllocDebug.h"
  43. #pragma warning(pop)
  44. USING_NS_BF;
  45. using namespace llvm;
  46. String BfCodeGenDirectoryData::GetDataFileName()
  47. {
  48. return mDirectoryName + "/build.dat";
  49. }
  50. void BfCodeGenDirectoryData::Read()
  51. {
  52. FileStream fileStream;
  53. String fileName = GetDataFileName();
  54. if (!fileStream.Open(fileName, "rb"))
  55. return;
  56. int fileId = fileStream.ReadInt32();
  57. if (fileId != 0xBEEF0100)
  58. return;
  59. int version = fileStream.ReadInt32();
  60. if (version != BF_CODEGEN_VERSION)
  61. return;
  62. Val128 backendHash;
  63. fileStream.ReadT(backendHash);
  64. #ifndef BF_IGNORE_BACKEND_HASH
  65. if (mCodeGen->mBackendHash != backendHash)
  66. return;
  67. #endif
  68. int numFiles = fileStream.ReadInt32();
  69. for (int fileIdx = 0; fileIdx < numFiles; fileIdx++)
  70. {
  71. String fileName = fileStream.ReadAscii32SizedString();
  72. BfCodeGenFileData fileData;
  73. fileStream.Read(&fileData.mIRHash, sizeof(Val128));
  74. fileStream.Read(&fileData.mIROrderedHash, sizeof(Val128));
  75. fileStream.Read(&fileData.mLastWasObjectWrite, sizeof(bool));
  76. mFileMap[fileName] = fileData;
  77. }
  78. int numValues = fileStream.ReadInt32();
  79. for (int valIdx = 0; valIdx < numValues; valIdx++)
  80. {
  81. String key = fileStream.ReadAscii32SizedString();
  82. String value = fileStream.ReadAscii32SizedString();
  83. mBuildSettings[key] = value;
  84. }
  85. mFileTime = GetFileTimeWrite(fileName);
  86. }
  87. void BfCodeGenDirectoryData::Write()
  88. {
  89. if (mFileFailed)
  90. {
  91. // We could be smarter about write failures, but we just nuke eveything here
  92. // to ensure that the next rebuild attempts to generate the files again
  93. mFileMap.Clear();
  94. mFileFailed = false;
  95. }
  96. FileStream fileStream;
  97. String fileName = GetDataFileName();
  98. if (!fileStream.Open(fileName, "wb"))
  99. {
  100. String directory = GetFileDir(fileName);
  101. if (!DirectoryExists(directory))
  102. {
  103. // Someone else (or the user) cleared this directory
  104. DoBfLog(2, "BfCodeGen cleared cache because '%s' was removed\n", directory.c_str());
  105. mFileMap.Clear();
  106. return;
  107. }
  108. mError = "Failed to write to " + fileName;
  109. return;
  110. }
  111. fileStream.Write((int)0xBEEF0100);
  112. fileStream.Write(BF_CODEGEN_VERSION);
  113. fileStream.WriteT(mCodeGen->mBackendHash);
  114. fileStream.Write((int)mFileMap.size());
  115. for (auto& pair : mFileMap)
  116. {
  117. fileStream.Write(pair.mKey);
  118. fileStream.Write(&pair.mValue.mIRHash, sizeof(Val128));
  119. fileStream.Write(&pair.mValue.mIROrderedHash, sizeof(Val128));
  120. fileStream.Write(&pair.mValue.mLastWasObjectWrite, sizeof(bool));
  121. }
  122. fileStream.Write((int)mBuildSettings.size());
  123. for (auto& pair : mBuildSettings)
  124. {
  125. fileStream.Write(pair.mKey);
  126. fileStream.Write(pair.mValue);
  127. }
  128. fileStream.Close();
  129. mFileTime = GetFileTimeWrite(fileName);
  130. }
  131. void BfCodeGenDirectoryData::Verify()
  132. {
  133. if (!mError.empty())
  134. return;
  135. String fileName = GetDataFileName();
  136. int64 fileTimeWrite = GetFileTimeWrite(fileName);
  137. if ((fileTimeWrite == mFileTime) || (mFileTime == 0))
  138. {
  139. mVerified = true;
  140. }
  141. else
  142. {
  143. mError = "Build directory corrupted, perform clean";
  144. }
  145. }
  146. void BfCodeGenDirectoryData::Clear()
  147. {
  148. mFileMap.Clear();
  149. mBuildSettings.Clear();
  150. }
  151. bool BfCodeGenDirectoryData::CheckCache(const StringImpl& fileName, Val128 hash, Val128* outOrderedHash, bool disallowObjectWrite)
  152. {
  153. if (!mVerified)
  154. Verify();
  155. BfCodeGenFileData* fileData = NULL;
  156. if (!mFileMap.TryAdd(fileName, NULL, &fileData))
  157. {
  158. if ((fileData->mLastWasObjectWrite) && (disallowObjectWrite))
  159. return false;
  160. if (outOrderedHash != NULL)
  161. *outOrderedHash = fileData->mIROrderedHash;
  162. if (fileData->mIRHash == hash)
  163. return true;
  164. fileData->mIRHash = hash;
  165. return false;
  166. }
  167. fileData->mLastWasObjectWrite = false;
  168. fileData->mIRHash = hash;
  169. return false;
  170. }
  171. void BfCodeGenDirectoryData::SetHash(const StringImpl& fileName, Val128 hash, Val128 orderedHash, bool isObjectWrite)
  172. {
  173. if (!mVerified)
  174. Verify();
  175. BfCodeGenFileData* fileData = NULL;
  176. mFileMap.TryAdd(fileName, NULL, &fileData);
  177. fileData->mIRHash = hash;
  178. fileData->mIROrderedHash = orderedHash;
  179. fileData->mLastWasObjectWrite = isObjectWrite;
  180. }
  181. void BfCodeGenDirectoryData::ClearHash(const StringImpl& fileName)
  182. {
  183. mFileMap.Remove(fileName);
  184. }
  185. void BfCodeGenDirectoryData::FileFailed()
  186. {
  187. mFileFailed = true;
  188. }
  189. String BfCodeGenDirectoryData::GetValue(const StringImpl& key)
  190. {
  191. String* valuePtr = NULL;
  192. if (mBuildSettings.TryGetValue(key, &valuePtr))
  193. {
  194. return *valuePtr;
  195. }
  196. return String();
  197. }
  198. void BfCodeGenDirectoryData::SetValue(const StringImpl& key, const StringImpl& value)
  199. {
  200. mBuildSettings[key] = value;
  201. }
  202. //////////////////////////////////////////////////////////////////////////
  203. void BfCodeGenRequest::DbgSaveData()
  204. {
  205. /*FILE* fp = fopen("c:\\temp\\dbgOut.bc", "wb");
  206. if (fp == NULL)
  207. {
  208. Beefy::OutputDebugStrF("Failed to write\n");
  209. }
  210. fwrite(mData.begin(), mData.size(), 1, fp);
  211. fclose(fp);*/
  212. }
  213. void DbgSaveData(BfCodeGenRequest* genRequest)
  214. {
  215. genRequest->DbgSaveData();
  216. }
  217. //////////////////////////////////////////////////////////////////////////
  218. BfCodeGenThread::BfCodeGenThread()
  219. {
  220. mShuttingDown = false;
  221. mRunning = false;
  222. mThreadIdx = 0;
  223. mCodeGen = NULL;
  224. }
  225. BfCodeGenThread::~BfCodeGenThread()
  226. {
  227. Shutdown();
  228. }
  229. void BfCodeGenThread::RunLoop()
  230. {
  231. String threadName = StrFormat("CodeGen/Worker %d", mThreadIdx);
  232. BpSetThreadName(threadName.c_str());
  233. BfpThread_SetName(NULL, threadName.c_str(), NULL);
  234. while (!mShuttingDown)
  235. {
  236. BfCodeGenRequest* request = NULL;
  237. {
  238. AutoCrit autoCrit(mCodeGen->mPendingRequestCritSect);
  239. if (!mCodeGen->mPendingRequests.IsEmpty())
  240. {
  241. request = mCodeGen->mPendingRequests[0];
  242. mCodeGen->mPendingRequests.RemoveAt(0);
  243. }
  244. }
  245. if (request == NULL)
  246. {
  247. mCodeGen->mRequestEvent.WaitFor(20);
  248. continue;
  249. }
  250. auto cacheDir = GetFileDir(request->mOutFileName);
  251. auto cacheFileName = GetFileName(request->mOutFileName);
  252. String objFileName = request->mOutFileName + BF_OBJ_EXT;
  253. bool hasCacheMatch = false;
  254. BfCodeGenDirectoryData* dirCache = NULL;
  255. Val128 hash;
  256. Val128 orderedHash;
  257. DoBfLog(2, "BfCodeGen handle %s\n", request->mOutFileName.c_str());
  258. bool isLibWrite = (request->mOptions.mOptLevel == BfOptLevel_OgPlus) && (request->mOptions.mWriteToLib) && (!request->mOptions.mIsHotCompile);
  259. #ifndef CODEGEN_DISABLE_CACHE
  260. {
  261. AutoCrit autoCrit(mCodeGen->mCacheCritSect);
  262. dirCache = mCodeGen->GetDirCache(cacheDir);
  263. //For testing only!
  264. /*{
  265. FileStream fileStr;
  266. fileStr.Open(request->mOutFileName + ".bfir", "wb");
  267. fileStr.Write(request->mData.mVals, request->mData.mSize);
  268. }*/
  269. HashContext hashCtx;
  270. hashCtx.Mixin(request->mOptions.mHash);
  271. hashCtx.Mixin(request->mData.mVals, request->mData.mSize);
  272. hash = hashCtx.Finish128();
  273. hasCacheMatch = dirCache->CheckCache(cacheFileName, hash, &orderedHash, isLibWrite);
  274. #ifdef BF_PLATFORM_WINDOWS
  275. if ((!hash.IsZero()) && (request->mOptions.mWriteToLib) && (request->mOptions.mOptLevel == BfOptLevel_OgPlus))
  276. {
  277. if (!BeLibManager::Get()->AddUsedFileName(objFileName))
  278. {
  279. // Oops, this library doesn't actually have this file so we can't use it from the cache. This can happen if we use a type,
  280. // delete it, and then add it back (for example). The file info will still be in the cache but the lib won't have it.
  281. orderedHash = Val128();
  282. hash = Val128();
  283. hasCacheMatch = false;
  284. }
  285. }
  286. #endif
  287. }
  288. #endif
  289. //TODO: Temporary, to make sure it keeps getting rebuilt
  290. #ifdef CODEGEN_DISABLE_CACHE_BEEFBACKEND
  291. if (request->mOptions.mOptLevel == BfOptLevel_OgPlus)
  292. hasCacheMatch = false;
  293. #endif
  294. String errorMsg;
  295. bool doBEProcessing = true; // TODO: Normally 'true' so we do ordered cache check for LLVM too
  296. if (request->mOptions.mOptLevel == BfOptLevel_OgPlus)
  297. doBEProcessing = true; // Must do it for this
  298. BfCodeGenResult result;
  299. result.mType = BfCodeGenResult_Done;
  300. result.mErrorMsgBufLen = 0;
  301. if (hasCacheMatch)
  302. {
  303. result.mType = BfCodeGenResult_DoneCached;
  304. }
  305. else
  306. {
  307. #ifdef BF_PLATFORM_WINDOWS
  308. BeIRCodeGen* beIRCodeGen = new BeIRCodeGen();
  309. defer ( delete beIRCodeGen; );
  310. beIRCodeGen->SetConfigConst(BfIRConfigConst_VirtualMethodOfs, request->mOptions.mVirtualMethodOfs);
  311. beIRCodeGen->SetConfigConst(BfIRConfigConst_DynSlotOfs, request->mOptions.mDynSlotOfs);
  312. if (doBEProcessing)
  313. {
  314. BP_ZONE("ProcessBfIRData");
  315. beIRCodeGen->Init(request->mData);
  316. BeHashContext hashCtx;
  317. hashCtx.Mixin(request->mOptions.mHash);
  318. beIRCodeGen->Hash(hashCtx);
  319. auto newOrderedHash = hashCtx.Finish128();
  320. DoBfLog(2, "Ordered hash for %s New:%s Old:%s\n", cacheFileName.c_str(), newOrderedHash.ToString().c_str(), orderedHash.ToString().c_str());
  321. hasCacheMatch = newOrderedHash == orderedHash;
  322. errorMsg = beIRCodeGen->mErrorMsg;
  323. orderedHash = newOrderedHash;
  324. }
  325. if (hasCacheMatch)
  326. {
  327. result.mType = BfCodeGenResult_DoneCached;
  328. }
  329. #ifndef CODEGEN_DISABLE_CACHE
  330. {
  331. AutoCrit autoCrit(mCodeGen->mCacheCritSect);
  332. dirCache->SetHash(cacheFileName, hash, orderedHash, !isLibWrite);
  333. }
  334. #endif
  335. #endif
  336. if (request->mOutFileName.Contains("RuntimeThreadInit"))
  337. {
  338. NOP;
  339. }
  340. if ((hasCacheMatch) || (!errorMsg.IsEmpty()))
  341. {
  342. //
  343. }
  344. else if (request->mOptions.mOptLevel == BfOptLevel_OgPlus)
  345. {
  346. #ifdef BF_PLATFORM_WINDOWS
  347. BP_ZONE("BfCodeGen::RunLoop.Beef");
  348. if (request->mOptions.mWriteLLVMIR)
  349. {
  350. BP_ZONE("BfCodeGen::RunLoop.Beef.IR");
  351. std::error_code ec;
  352. String fileName = request->mOutFileName + ".beir";
  353. String str = beIRCodeGen->mBeModule->ToString();
  354. FileStream fs;
  355. if (!fs.Open(fileName, "w"))
  356. {
  357. if (!errorMsg.empty())
  358. errorMsg += "\n";
  359. errorMsg += "Failed writing IR '" + fileName + "': " + ec.message();
  360. }
  361. fs.WriteSNZ(str);
  362. }
  363. if (!hasCacheMatch)
  364. beIRCodeGen->Process();
  365. errorMsg = beIRCodeGen->mErrorMsg;
  366. DoBfLog(2, "Generating obj %s\n", request->mOutFileName.c_str());
  367. BeCOFFObject coffObject;
  368. coffObject.mWriteToLib = (request->mOptions.mWriteToLib) && (!request->mOptions.mIsHotCompile);
  369. if (!coffObject.Generate(beIRCodeGen->mBeModule, objFileName))
  370. errorMsg = StrFormat("Failed to write object file: %s", objFileName.c_str());
  371. if (!beIRCodeGen->mErrorMsg.IsEmpty())
  372. {
  373. if (!errorMsg.IsEmpty())
  374. errorMsg += "\n";
  375. errorMsg += beIRCodeGen->mErrorMsg;
  376. }
  377. #else
  378. errorMsg = "Failed to generate object file";
  379. #endif
  380. }
  381. else
  382. {
  383. BP_ZONE_F("BfCodeGen::RunLoop.LLVM %s", request->mOutFileName.c_str());
  384. BfIRCodeGen* llvmIRCodeGen = new BfIRCodeGen();
  385. llvmIRCodeGen->SetConfigConst(BfIRConfigConst_VirtualMethodOfs, request->mOptions.mVirtualMethodOfs);
  386. llvmIRCodeGen->SetConfigConst(BfIRConfigConst_DynSlotOfs, request->mOptions.mDynSlotOfs);
  387. llvmIRCodeGen->ProcessBfIRData(request->mData);
  388. errorMsg = llvmIRCodeGen->mErrorMsg;
  389. llvmIRCodeGen->mErrorMsg.Clear();
  390. if (errorMsg.IsEmpty())
  391. {
  392. if (request->mOptions.mWriteLLVMIR)
  393. {
  394. BP_ZONE("BfCodeGen::RunLoop.LLVM.IR");
  395. String fileName = request->mOutFileName + ".ll";
  396. String irError;
  397. if (!llvmIRCodeGen->WriteIR(fileName, irError))
  398. {
  399. if (!errorMsg.empty())
  400. errorMsg += "\n";
  401. errorMsg += "Failed writing IR '" + fileName + "': " + irError;
  402. dirCache->FileFailed();
  403. }
  404. }
  405. if (request->mOptions.mWriteObj)
  406. {
  407. BP_ZONE("BfCodeGen::RunLoop.LLVM.OBJ");
  408. String outFileName;
  409. if (request->mOptions.mAsmKind != BfAsmKind_None)
  410. outFileName = request->mOutFileName + ".s";
  411. else
  412. outFileName = request->mOutFileName + BF_OBJ_EXT;
  413. if (!llvmIRCodeGen->WriteObjectFile(outFileName, request->mOptions))
  414. {
  415. result.mType = BfCodeGenResult_Failed;
  416. dirCache->FileFailed();
  417. }
  418. }
  419. }
  420. if (!llvmIRCodeGen->mErrorMsg.IsEmpty())
  421. {
  422. if (!errorMsg.IsEmpty())
  423. errorMsg += "\n";
  424. errorMsg += llvmIRCodeGen->mErrorMsg;
  425. }
  426. delete llvmIRCodeGen;
  427. }
  428. }
  429. if (!errorMsg.IsEmpty())
  430. {
  431. result.mType = BfCodeGenResult_Failed;
  432. int errorStrLen = (int)errorMsg.length();
  433. if ((result.mErrorMsgBufLen + errorStrLen + 1) < BfCodeGenResult::kErrorMsgBufSize)
  434. {
  435. memcpy(&result.mErrorMsgBuf[result.mErrorMsgBufLen], errorMsg.c_str(), errorStrLen + 1);
  436. result.mErrorMsgBufLen += errorStrLen + 1;
  437. }
  438. if (dirCache != NULL)
  439. {
  440. AutoCrit autoCrit(mCodeGen->mCacheCritSect);
  441. dirCache->ClearHash(cacheFileName);
  442. }
  443. }
  444. {
  445. // It's an extern request, so we own this
  446. bool deleteRequest = false;
  447. if (request->mExternResultPtr != NULL)
  448. {
  449. *request->mExternResultPtr = result;
  450. deleteRequest = true;
  451. }
  452. // We need this fence for BF_USE_CODEGEN_RELEASE_THUNK usage- because we can't access the request anymore after setting
  453. // request->mIsDone
  454. BF_FULL_MEMORY_FENCE();
  455. AutoCrit autoCrit(mCodeGen->mPendingRequestCritSect);
  456. request->mResult = result;
  457. mCodeGen->mDoneEvent.Set();
  458. if (deleteRequest)
  459. delete request;
  460. }
  461. }
  462. mRunning = false;
  463. mCodeGen->mDoneEvent.Set();
  464. }
  465. void BfCodeGenThread::Shutdown()
  466. {
  467. mShuttingDown = true;
  468. if (mRunning)
  469. mCodeGen->mRequestEvent.Set(true);
  470. while (mRunning)
  471. {
  472. mCodeGen->mDoneEvent.WaitFor(20);
  473. }
  474. }
  475. static void BFP_CALLTYPE RunLoopThunk(void* codeGenThreadP)
  476. {
  477. auto codeGenThread = (BfCodeGenThread*)codeGenThreadP;
  478. BfpThread_SetName(NULL, StrFormat("BfCodeGenThread%d", codeGenThread->mThreadIdx).c_str(), NULL);
  479. codeGenThread->RunLoop();
  480. }
  481. void BfCodeGenThread::Start()
  482. {
  483. mRunning = true;
  484. //TODO: How much mem do we need? WTF- we have 32MB set before!
  485. auto mThread = BfpThread_Create(RunLoopThunk, (void*)this, 1024 * 1024);
  486. BfpThread_SetPriority(mThread, BfpThreadPriority_Low, NULL);
  487. BfpThread_Release(mThread);
  488. }
  489. //////////////////////////////////////////////////////////////////////////
  490. BfCodeGen::BfCodeGen()
  491. {
  492. mAttemptedReleaseThunkLoad = false;
  493. mIsUsingReleaseThunk = false;
  494. mReleaseModule = NULL;
  495. mClearCacheFunc = NULL;
  496. mGetVersionFunc = NULL;
  497. mKillFunc = NULL;
  498. mCancelFunc = NULL;
  499. mFinishFunc = NULL;
  500. mGenerateObjFunc = NULL;
  501. mRequestIdx = 0;
  502. #ifdef MAX_THREADS
  503. mMaxThreadCount = MAX_THREADS;
  504. #else
  505. mMaxThreadCount = 6;
  506. #endif
  507. mQueuedCount = 0;
  508. mCompletionCount = 0;
  509. mDisableCacheReads = false;
  510. HashContext hashCtx;
  511. hashCtx.Mixin(BF_CODEGEN_VERSION);
  512. #ifdef BF_PLATFORM_WINDOWS
  513. char path[MAX_PATH];
  514. ::GetModuleFileNameA(NULL, path, MAX_PATH);
  515. if (_strnicmp(path + 2, "\\Beef\\", 6) == 0)
  516. {
  517. path[8] = 0;
  518. char* checkFileNames[] =
  519. {
  520. "IDEHelper/Backend/BeCOFFObject.cpp",
  521. "IDEHelper/Backend/BeCOFFObject.h",
  522. "IDEHelper/Backend/BeContext.cpp",
  523. "IDEHelper/Backend/BeContext.h",
  524. "IDEHelper/Backend/BeDbgModule.h",
  525. "IDEHelper/Backend/BeIRCodeGen.cpp",
  526. "IDEHelper/Backend/BeIRCodeGen.h",
  527. "IDEHelper/Backend/BeLibManager.cpp",
  528. "IDEHelper/Backend/BeLibManager.h",
  529. "IDEHelper/Backend/BeMCContext.cpp",
  530. "IDEHelper/Backend/BeMCContext.h",
  531. "IDEHelper/Backend/BeModule.cpp",
  532. "IDEHelper/Backend/BeModule.h",
  533. "IDEHelper/Compiler/BfCodeGen.cpp",
  534. "IDEHelper/Compiler/BfCodeGen.h",
  535. "IDEHelper/Compiler/BfIRBuilder.cpp",
  536. "IDEHelper/Compiler/BfIRBuilder.h",
  537. "IDEHelper/Compiler/BfIRCodeGen.cpp",
  538. "IDEHelper/Compiler/BfIRCodeGen.h",
  539. };
  540. for (int i = 0; i < BF_ARRAY_COUNT(checkFileNames); i++)
  541. {
  542. String filePath;
  543. filePath += path;
  544. filePath += checkFileNames[i];
  545. auto writeTime = GetFileTimeWrite(filePath);
  546. hashCtx.Mixin(writeTime);
  547. }
  548. }
  549. #endif
  550. mBackendHash = hashCtx.Finish128();
  551. }
  552. BfCodeGen::~BfCodeGen()
  553. {
  554. if (mIsUsingReleaseThunk)
  555. mKillFunc();
  556. if (mReleaseModule != NULL)
  557. BfpDynLib_Release(mReleaseModule);
  558. ClearOldThreads(true);
  559. for (auto thread : mThreads)
  560. {
  561. thread->mShuttingDown = true;
  562. }
  563. mRequestEvent.Set(true);
  564. for (auto thread : mThreads)
  565. {
  566. thread->Shutdown();
  567. delete thread;
  568. }
  569. for (auto request : mRequests)
  570. {
  571. delete request;
  572. }
  573. for (auto& entry : mDirectoryCache)
  574. delete entry.mValue;
  575. }
  576. void BfCodeGen::ResetStats()
  577. {
  578. mQueuedCount = 0;
  579. mCompletionCount = 0;
  580. }
  581. void BfCodeGen::UpdateStats()
  582. {
  583. while (mRequests.size() != 0)
  584. {
  585. auto request = mRequests[0];
  586. if (request->mResult.mType == BfCodeGenResult_NotDone)
  587. return;
  588. RequestComplete(request);
  589. delete request;
  590. mRequests.RemoveAt(0);
  591. return;
  592. }
  593. }
  594. void BfCodeGen::ClearOldThreads(bool waitForThread)
  595. {
  596. while (mOldThreads.size() != 0)
  597. {
  598. auto thread = mOldThreads[0];
  599. if (waitForThread)
  600. thread->Shutdown();
  601. if (thread->mRunning)
  602. return;
  603. delete thread;
  604. mOldThreads.RemoveAt(0);
  605. }
  606. }
  607. void BfCodeGen::ClearBuildCache()
  608. {
  609. AutoCrit autoCrit(mCacheCritSect);
  610. for (auto& dirCachePair : mDirectoryCache)
  611. {
  612. auto dirData = dirCachePair.mValue;
  613. dirData->Clear();
  614. }
  615. // This just disables reading the cache file, but it does not disable creating
  616. // the cache structes in memory and writing them out, thus it's valid to leave
  617. // this 'true' forever
  618. mDisableCacheReads = true;
  619. #ifdef BF_USE_CODEGEN_RELEASE_THUNK
  620. BindReleaseThunks();
  621. if (mClearCacheFunc != NULL)
  622. mClearCacheFunc();
  623. #endif
  624. }
  625. void BfCodeGen::DoWriteObjectFile(BfCodeGenRequest* codeGenRequest, const void* ptr, int size, const StringImpl& outFileName, BfCodeGenResult* externResultPtr)
  626. {
  627. codeGenRequest->mData.mVals = (uint8*)ptr;
  628. codeGenRequest->mData.mSize = size;
  629. codeGenRequest->mOutFileName = outFileName;
  630. codeGenRequest->mResult.mType = BfCodeGenResult_NotDone;
  631. codeGenRequest->mResult.mErrorMsgBufLen = 0;
  632. codeGenRequest->mExternResultPtr = externResultPtr;
  633. int threadIdx = mRequestIdx % mMaxThreadCount;
  634. if (threadIdx >= (int) mThreads.size())
  635. {
  636. AutoCrit autoCrit(mThreadsCritSect);
  637. BfCodeGenThread* thread = new BfCodeGenThread();
  638. thread->mThreadIdx = threadIdx;
  639. thread->mCodeGen = this;
  640. mThreads.push_back(thread);
  641. thread->Start();
  642. }
  643. auto thread = mThreads[threadIdx];
  644. BP_ZONE("WriteObjectFile_CritSect");
  645. AutoCrit autoCrit(mPendingRequestCritSect);
  646. mPendingRequests.push_back(codeGenRequest);
  647. #ifdef BF_PLATFORM_WINDOWS
  648. BF_ASSERT(!mRequestEvent.mEvent->mManualReset); // Make sure it's out of the SignalAll state
  649. #endif
  650. mRequestEvent.Set();
  651. mRequestIdx++;
  652. }
  653. void BfCodeGen::SetMaxThreads(int maxThreads)
  654. {
  655. #ifndef MAX_THREADS
  656. mMaxThreadCount = BF_CLAMP(maxThreads, 2, 64);
  657. #endif
  658. }
  659. void BfCodeGen::BindReleaseThunks()
  660. {
  661. if (mAttemptedReleaseThunkLoad)
  662. return;
  663. mAttemptedReleaseThunkLoad = true;
  664. if (mReleaseModule == NULL)
  665. {
  666. #ifdef BF32
  667. mReleaseModule = BfpDynLib_Load("./IDEHelper32.dll");
  668. #else
  669. mReleaseModule = BfpDynLib_Load("./IDEHelper64.dll");
  670. #endif
  671. if (mReleaseModule == NULL)
  672. {
  673. BF_FATAL("Unable to locate release DLL. Please rebuild Release configuration.\n");
  674. return;
  675. }
  676. #ifdef BF32
  677. mClearCacheFunc = (GetVersionFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "_BfCodeGen_ClearCache@0");
  678. mGetVersionFunc = (GetVersionFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "_BfCodeGen_GetVersion@0");
  679. mKillFunc = (KillFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "_BfCodeGen_Kill@0");
  680. mCancelFunc = (KillFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "_BfCodeGen_Cancel@0");
  681. mFinishFunc = (FinishFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "_BfCodeGen_Finish@0");
  682. mGenerateObjFunc = (GenerateObjFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "_BfCodeGen_GenerateObj@20");
  683. #else
  684. mClearCacheFunc = (GetVersionFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "BfCodeGen_ClearCache");
  685. mGetVersionFunc = (GetVersionFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "BfCodeGen_GetVersion");
  686. mKillFunc = (KillFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "BfCodeGen_Kill");
  687. mCancelFunc = (KillFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "BfCodeGen_Cancel");
  688. mFinishFunc = (FinishFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "BfCodeGen_Finish");
  689. mGenerateObjFunc = (GenerateObjFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "BfCodeGen_GenerateObj");
  690. #endif
  691. if ((mGetVersionFunc == NULL) || (mKillFunc == NULL) || (mCancelFunc == NULL) || (mFinishFunc == NULL) || (mGenerateObjFunc == NULL))
  692. {
  693. BF_FATAL("Invalid signature in IDEHelper release DLL. Please rebuild Release configuration.\n");
  694. return;
  695. }
  696. if (mGetVersionFunc() != BF_CODEGEN_VERSION)
  697. {
  698. BF_FATAL("Invalid BF_CODEGEN_VERSION in IDEHelper release DLL. Please rebuild Release configuration.\n");
  699. return;
  700. }
  701. mIsUsingReleaseThunk = true;
  702. }
  703. }
  704. bool BfCodeGen::ExternWriteObjectFile(BfCodeGenRequest* codeGenRequest)
  705. {
  706. #ifndef BF_USE_CODEGEN_RELEASE_THUNK
  707. return false;
  708. #endif
  709. BindReleaseThunks();
  710. if (!mIsUsingReleaseThunk)
  711. return false;
  712. mGenerateObjFunc(codeGenRequest->mData.mVals, codeGenRequest->mData.mSize, codeGenRequest->mOutFileName.c_str(), &codeGenRequest->mResult, codeGenRequest->mOptions);
  713. return true;
  714. }
  715. void BfCodeGen::WriteObjectFile(BfModule* bfModule, const StringImpl& outFileName, const BfCodeGenOptions& options)
  716. {
  717. mQueuedCount++;
  718. BfLogSys(bfModule->mSystem, "WriteObjectFile %s\n", outFileName.c_str());
  719. BfCodeGenRequest* codeGenRequest = new BfCodeGenRequest();
  720. mRequests.push_back(codeGenRequest);
  721. {
  722. BP_ZONE("WriteObjectFile_GetBufferData");
  723. bfModule->mBfIRBuilder->GetBufferData(codeGenRequest->mOutBuffer);
  724. }
  725. auto rootModule = bfModule;
  726. while (rootModule->mParentModule != NULL)
  727. rootModule = rootModule->mParentModule;
  728. codeGenRequest->mSrcModule = rootModule;
  729. codeGenRequest->mOutFileName = outFileName;
  730. codeGenRequest->mData = codeGenRequest->mOutBuffer;
  731. codeGenRequest->mOptions = options;
  732. if (ExternWriteObjectFile(codeGenRequest))
  733. return;
  734. DoWriteObjectFile(codeGenRequest, (void*)&codeGenRequest->mOutBuffer[0], (int)codeGenRequest->mOutBuffer.size(), codeGenRequest->mOutFileName, NULL);
  735. #ifdef DBG_FORCE_SYNCHRONIZED
  736. while (mRequests.size() != 0)
  737. {
  738. UpdateStats();
  739. }
  740. BfLogSys(bfModule->mSystem, "WriteObjectFile Done\n");
  741. #endif
  742. }
  743. String BfCodeGen::GetBuildValue(const StringImpl& buildDir, const StringImpl& key)
  744. {
  745. AutoCrit autoCrit(mCacheCritSect);
  746. BfCodeGenDirectoryData* dirCache = GetDirCache(buildDir);
  747. return dirCache->GetValue(key);
  748. }
  749. void BfCodeGen::SetBuildValue(const StringImpl& buildDir, const StringImpl & key, const StringImpl & value)
  750. {
  751. AutoCrit autoCrit(mCacheCritSect);
  752. BfCodeGenDirectoryData* dirCache = GetDirCache(buildDir);
  753. dirCache->SetValue(key, value);
  754. }
  755. void BfCodeGen::WriteBuildCache(const StringImpl& buildDir)
  756. {
  757. AutoCrit autoCrit(mCacheCritSect);
  758. BfCodeGenDirectoryData* dirCache = GetDirCache(buildDir);
  759. dirCache->Write();
  760. }
  761. void BfCodeGen::RequestComplete(BfCodeGenRequest* request)
  762. {
  763. mCompletionCount++;
  764. if ((request->mResult.mType == BfCodeGenResult_Failed) || (request->mResult.mType == BfCodeGenResult_Aborted))
  765. {
  766. BfCodeGenErrorEntry errorEntry;
  767. errorEntry.mSrcModule = request->mSrcModule;
  768. errorEntry.mOutFileName = request->mOutFileName;
  769. int errorPos = 0;
  770. while (errorPos < request->mResult.mErrorMsgBufLen)
  771. {
  772. char* errorStr = &request->mResult.mErrorMsgBuf[errorPos];
  773. errorEntry.mErrorMessages.push_back(errorStr);
  774. errorPos += (int)strlen(errorStr) + 1;
  775. }
  776. mFailedRequests.push_back(errorEntry);
  777. }
  778. else
  779. {
  780. BfCodeGenFileEntry entry;
  781. entry.mFileName = request->mOutFileName;
  782. entry.mModule = request->mSrcModule;
  783. entry.mProject = request->mSrcModule->mProject;
  784. entry.mWasCached = request->mResult.mType == BfCodeGenResult_DoneCached;
  785. entry.mModuleHotReferenced = false;
  786. mCodeGenFiles.push_back(entry);
  787. }
  788. }
  789. void BfCodeGen::ProcessErrors(BfPassInstance* passInstance, bool canceled)
  790. {
  791. for (auto& errorEntry : mFailedRequests)
  792. {
  793. if (!errorEntry.mErrorMessages.IsEmpty())
  794. {
  795. for (auto const & errorMsg : errorEntry.mErrorMessages)
  796. passInstance->Fail(StrFormat("Module '%s' failed during codegen with: %s", errorEntry.mSrcModule->mModuleName.c_str(), errorMsg.c_str()));
  797. }
  798. else if (!canceled)
  799. passInstance->Fail(StrFormat("Failed to create %s", errorEntry.mOutFileName.c_str()));
  800. // mHadBuildError forces it to build again
  801. errorEntry.mSrcModule->mHadBuildError = true;
  802. }
  803. mFailedRequests.Clear();
  804. bool showedCacheError = false;
  805. for (auto& dirEntryPair : mDirectoryCache)
  806. {
  807. auto dirEntry = dirEntryPair.mValue;
  808. if (!dirEntry->mError.empty())
  809. {
  810. if (!showedCacheError)
  811. {
  812. passInstance->Fail(dirEntry->mError);
  813. showedCacheError = true;
  814. }
  815. dirEntry->mError.clear();
  816. }
  817. }
  818. }
  819. BfCodeGenDirectoryData * BfCodeGen::GetDirCache(const StringImpl & cacheDir)
  820. {
  821. BfCodeGenDirectoryData* dirCache = NULL;
  822. BfCodeGenDirectoryData** dirCachePtr = NULL;
  823. if (mDirectoryCache.TryAdd(cacheDir, NULL, &dirCachePtr))
  824. {
  825. dirCache = new BfCodeGenDirectoryData();
  826. *dirCachePtr = dirCache;
  827. dirCache->mCodeGen = this;
  828. dirCache->mDirectoryName = cacheDir;
  829. if (!mDisableCacheReads)
  830. dirCache->Read();
  831. }
  832. else
  833. {
  834. dirCache = *dirCachePtr;
  835. }
  836. return dirCache;
  837. }
  838. void BfCodeGen::Cancel()
  839. {
  840. for (auto thread : mThreads)
  841. {
  842. thread->mShuttingDown = true;
  843. }
  844. mRequestEvent.Set(true);
  845. if (mIsUsingReleaseThunk)
  846. mCancelFunc();
  847. }
  848. void BfCodeGen::ClearResults()
  849. {
  850. mFailedRequests.Clear();
  851. mCodeGenFiles.Clear();
  852. }
  853. bool BfCodeGen::Finish()
  854. {
  855. BP_ZONE("BfCodeGen::Finish");
  856. while (mRequests.size() != 0)
  857. {
  858. auto request = mRequests[0];
  859. if (request->mResult.mType == BfCodeGenResult_NotDone)
  860. {
  861. bool hasRunningThreads = false;
  862. for (auto thread : mThreads)
  863. {
  864. if (thread->mRunning)
  865. hasRunningThreads = true;
  866. }
  867. if (!hasRunningThreads)
  868. {
  869. mPendingRequests.Clear();
  870. request->mResult.mType = BfCodeGenResult_Aborted;
  871. continue;
  872. }
  873. mDoneEvent.WaitFor(20);
  874. continue;
  875. }
  876. RequestComplete(request);
  877. delete request;
  878. mRequests.RemoveAt(0);
  879. return false;
  880. }
  881. if (mIsUsingReleaseThunk)
  882. {
  883. // Make the thunk release its threads (and more important, its LLVM contexts)
  884. mFinishFunc();
  885. }
  886. // We need to shut down these threads to remove their memory
  887. for (auto thread : mThreads)
  888. {
  889. thread->mShuttingDown = true;
  890. mOldThreads.push_back(thread);
  891. }
  892. mThreads.Clear();
  893. mRequestEvent.Set(true);
  894. ClearOldThreads(false);
  895. // for (auto request : mPendingRequests)
  896. // {
  897. // if (request->mExternResultPtr != NULL)
  898. // {
  899. // request->mExternResultPtr->mType = BfCodeGenResult_Aborted;
  900. // //delete request;
  901. // }
  902. // //request->mResult.mType = BfCodeGenResult_Aborted;
  903. // //delete request;
  904. // }
  905. // mPendingRequests.Clear();
  906. ///
  907. for (auto& cachePair : mDirectoryCache)
  908. {
  909. auto cacheDir = cachePair.mValue;
  910. cacheDir->Write();
  911. cacheDir->mVerified = false;
  912. }
  913. mRequestEvent.Reset();
  914. mRequestIdx = 0;
  915. return true;
  916. }
  917. //////////////////////////////////////////////////////////////////////////
  918. static BfCodeGen* gExternCodeGen = NULL;
  919. BF_EXPORT void BF_CALLTYPE Targets_Create();
  920. BF_EXPORT void BF_CALLTYPE Targets_Delete();
  921. static void GetExternCodeGen()
  922. {
  923. if (gExternCodeGen == NULL)
  924. {
  925. gExternCodeGen = new BfCodeGen();
  926. Targets_Create();
  927. }
  928. }
  929. BF_EXPORT int BF_CALLTYPE BfCodeGen_GetVersion()
  930. {
  931. return BF_CODEGEN_VERSION;
  932. }
  933. BF_EXPORT void BF_CALLTYPE BfCodeGen_ClearCache()
  934. {
  935. GetExternCodeGen();
  936. gExternCodeGen->ClearBuildCache();
  937. }
  938. BF_EXPORT void BF_CALLTYPE BfCodeGen_Finish()
  939. {
  940. if (gExternCodeGen != NULL)
  941. {
  942. gExternCodeGen->Finish();
  943. gExternCodeGen->ClearResults();
  944. }
  945. }
  946. BF_EXPORT void BF_CALLTYPE BfCodeGen_Kill()
  947. {
  948. delete gExternCodeGen;
  949. gExternCodeGen = NULL;
  950. Targets_Delete();
  951. }
  952. BF_EXPORT void BF_CALLTYPE BfCodeGen_Cancel()
  953. {
  954. if (gExternCodeGen != NULL)
  955. gExternCodeGen->Cancel();
  956. }
  957. BF_EXPORT void BF_CALLTYPE BfCodeGen_GenerateObj(const void* ptr, int size, const char* outFileName, BfCodeGenResult* resultPtr, const BfCodeGenOptions& options)
  958. {
  959. GetExternCodeGen();
  960. BfCodeGenRequest* codeGenRequest = new BfCodeGenRequest();
  961. codeGenRequest->mOptions = options;
  962. gExternCodeGen->DoWriteObjectFile(codeGenRequest, ptr, size, outFileName, resultPtr);
  963. }