2
0

BfCodeGen.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146
  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. BfLogX(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. StringT<256> objFileName = request->mOutFileName + BF_OBJ_EXT;
  253. bool hasCacheMatch = false;
  254. BfCodeGenDirectoryData* dirCache = NULL;
  255. Val128 hash;
  256. Val128 orderedHash;
  257. BfLogX(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. BfLogX(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 ((hasCacheMatch) || (!errorMsg.IsEmpty()))
  337. {
  338. //
  339. }
  340. else if (request->mOptions.mOptLevel == BfOptLevel_OgPlus)
  341. {
  342. #ifdef BF_PLATFORM_WINDOWS
  343. BP_ZONE("BfCodeGen::RunLoop.Beef");
  344. if (request->mOptions.mWriteLLVMIR)
  345. {
  346. BP_ZONE("BfCodeGen::RunLoop.Beef.IR");
  347. std::error_code ec;
  348. String fileName = request->mOutFileName + ".beir";
  349. String str = beIRCodeGen->mBeModule->ToString();
  350. FileStream fs;
  351. if (!fs.Open(fileName, "w"))
  352. {
  353. if (!errorMsg.empty())
  354. errorMsg += "\n";
  355. errorMsg += "Failed writing IR '" + fileName + "': " + ec.message();
  356. }
  357. else
  358. fs.WriteSNZ(str);
  359. }
  360. if (!hasCacheMatch)
  361. beIRCodeGen->Process();
  362. errorMsg = beIRCodeGen->mErrorMsg;
  363. BfLogX(2, "Generating obj %s\n", request->mOutFileName.c_str());
  364. BeCOFFObject coffObject;
  365. coffObject.mWriteToLib = request->mOptions.mWriteToLib;
  366. if (!coffObject.Generate(beIRCodeGen->mBeModule, objFileName))
  367. errorMsg = StrFormat("Failed to write object file: %s", objFileName.c_str());
  368. if (!beIRCodeGen->mErrorMsg.IsEmpty())
  369. {
  370. if (!errorMsg.IsEmpty())
  371. errorMsg += "\n";
  372. errorMsg += beIRCodeGen->mErrorMsg;
  373. }
  374. #else
  375. errorMsg = "Failed to generate object file";
  376. #endif
  377. }
  378. else
  379. {
  380. BP_ZONE_F("BfCodeGen::RunLoop.LLVM %s", request->mOutFileName.c_str());
  381. BfIRCodeGen* llvmIRCodeGen = new BfIRCodeGen();
  382. llvmIRCodeGen->SetCodeGenOptions(request->mOptions);
  383. llvmIRCodeGen->SetConfigConst(BfIRConfigConst_VirtualMethodOfs, request->mOptions.mVirtualMethodOfs);
  384. llvmIRCodeGen->SetConfigConst(BfIRConfigConst_DynSlotOfs, request->mOptions.mDynSlotOfs);
  385. llvmIRCodeGen->ProcessBfIRData(request->mData);
  386. errorMsg = llvmIRCodeGen->mErrorMsg;
  387. llvmIRCodeGen->mErrorMsg.Clear();
  388. if (errorMsg.IsEmpty())
  389. {
  390. if (request->mOptions.mWriteLLVMIR)
  391. {
  392. BP_ZONE("BfCodeGen::RunLoop.LLVM.IR");
  393. String fileName = request->mOutFileName + ".ll";
  394. String irError;
  395. if (!llvmIRCodeGen->WriteIR(fileName, irError))
  396. {
  397. if (!errorMsg.empty())
  398. errorMsg += "\n";
  399. errorMsg += "Failed writing IR '" + fileName + "': " + irError;
  400. dirCache->FileFailed();
  401. }
  402. }
  403. if (request->mOptions.mWriteObj)
  404. {
  405. BP_ZONE("BfCodeGen::RunLoop.LLVM.OBJ");
  406. String outFileName;
  407. if (request->mOptions.mAsmKind != BfAsmKind_None)
  408. outFileName = request->mOutFileName + ".s";
  409. else
  410. outFileName = request->mOutFileName + BF_OBJ_EXT;
  411. if (!llvmIRCodeGen->WriteObjectFile(outFileName))
  412. {
  413. result.mType = BfCodeGenResult_Failed;
  414. dirCache->FileFailed();
  415. }
  416. }
  417. }
  418. if (!llvmIRCodeGen->mErrorMsg.IsEmpty())
  419. {
  420. if (!errorMsg.IsEmpty())
  421. errorMsg += "\n";
  422. errorMsg += llvmIRCodeGen->mErrorMsg;
  423. }
  424. delete llvmIRCodeGen;
  425. }
  426. }
  427. if (!errorMsg.IsEmpty())
  428. {
  429. result.mType = BfCodeGenResult_Failed;
  430. int errorStrLen = (int)errorMsg.length();
  431. if ((result.mErrorMsgBufLen + errorStrLen + 1) < BfCodeGenResult::kErrorMsgBufSize)
  432. {
  433. memcpy(&result.mErrorMsgBuf[result.mErrorMsgBufLen], errorMsg.c_str(), errorStrLen + 1);
  434. result.mErrorMsgBufLen += errorStrLen + 1;
  435. }
  436. if (dirCache != NULL)
  437. {
  438. AutoCrit autoCrit(mCodeGen->mCacheCritSect);
  439. dirCache->ClearHash(cacheFileName);
  440. }
  441. }
  442. {
  443. // It's an extern request, so we own this
  444. bool deleteRequest = false;
  445. if (request->mExternResultPtr != NULL)
  446. {
  447. *request->mExternResultPtr = result;
  448. deleteRequest = true;
  449. }
  450. // We need this fence for BF_USE_CODEGEN_RELEASE_THUNK usage- because we can't access the request anymore after setting
  451. // request->mIsDone
  452. BF_FULL_MEMORY_FENCE();
  453. AutoCrit autoCrit(mCodeGen->mPendingRequestCritSect);
  454. request->mResult = result;
  455. mCodeGen->mDoneEvent.Set();
  456. if (deleteRequest)
  457. delete request;
  458. }
  459. }
  460. mRunning = false;
  461. mCodeGen->mDoneEvent.Set();
  462. }
  463. void BfCodeGenThread::Shutdown()
  464. {
  465. mShuttingDown = true;
  466. if (mRunning)
  467. mCodeGen->mRequestEvent.Set(true);
  468. while (mRunning)
  469. {
  470. mCodeGen->mDoneEvent.WaitFor(20);
  471. }
  472. }
  473. static void BFP_CALLTYPE RunLoopThunk(void* codeGenThreadP)
  474. {
  475. auto codeGenThread = (BfCodeGenThread*)codeGenThreadP;
  476. BfpThread_SetName(NULL, StrFormat("BfCodeGenThread%d", codeGenThread->mThreadIdx).c_str(), NULL);
  477. codeGenThread->RunLoop();
  478. }
  479. void BfCodeGenThread::Start()
  480. {
  481. mRunning = true;
  482. //TODO: How much mem do we need? WTF- we have 32MB set before!
  483. auto mThread = BfpThread_Create(RunLoopThunk, (void*)this, 1024 * 1024, BfpThreadCreateFlag_StackSizeReserve);
  484. BfpThread_SetPriority(mThread, BfpThreadPriority_Low, NULL);
  485. BfpThread_Release(mThread);
  486. }
  487. //////////////////////////////////////////////////////////////////////////
  488. BfCodeGen::BfCodeGen()
  489. {
  490. mAttemptedReleaseThunkLoad = false;
  491. mIsUsingReleaseThunk = false;
  492. mReleaseModule = NULL;
  493. mClearCacheFunc = NULL;
  494. mGetVersionFunc = NULL;
  495. mKillFunc = NULL;
  496. mCancelFunc = NULL;
  497. mFinishFunc = NULL;
  498. mGenerateObjFunc = NULL;
  499. mRequestIdx = 0;
  500. #ifdef MAX_THREADS
  501. mMaxThreadCount = MAX_THREADS;
  502. #else
  503. mMaxThreadCount = 6;
  504. #endif
  505. mQueuedCount = 0;
  506. mCompletionCount = 0;
  507. mDisableCacheReads = false;
  508. HashContext hashCtx;
  509. hashCtx.Mixin(BF_CODEGEN_VERSION);
  510. #ifdef BF_PLATFORM_WINDOWS
  511. char path[MAX_PATH];
  512. ::GetModuleFileNameA(NULL, path, MAX_PATH);
  513. if (_strnicmp(path + 2, "\\Beef\\", 6) == 0)
  514. {
  515. path[8] = 0;
  516. char* checkFileNames[] =
  517. {
  518. "IDEHelper/Backend/BeCOFFObject.cpp",
  519. "IDEHelper/Backend/BeCOFFObject.h",
  520. "IDEHelper/Backend/BeContext.cpp",
  521. "IDEHelper/Backend/BeContext.h",
  522. "IDEHelper/Backend/BeDbgModule.h",
  523. "IDEHelper/Backend/BeIRCodeGen.cpp",
  524. "IDEHelper/Backend/BeIRCodeGen.h",
  525. "IDEHelper/Backend/BeLibManager.cpp",
  526. "IDEHelper/Backend/BeLibManager.h",
  527. "IDEHelper/Backend/BeMCContext.cpp",
  528. "IDEHelper/Backend/BeMCContext.h",
  529. "IDEHelper/Backend/BeModule.cpp",
  530. "IDEHelper/Backend/BeModule.h",
  531. "IDEHelper/Compiler/BfCodeGen.cpp",
  532. "IDEHelper/Compiler/BfCodeGen.h",
  533. "IDEHelper/Compiler/BfIRBuilder.cpp",
  534. "IDEHelper/Compiler/BfIRBuilder.h",
  535. "IDEHelper/Compiler/BfIRCodeGen.cpp",
  536. "IDEHelper/Compiler/BfIRCodeGen.h",
  537. };
  538. for (int i = 0; i < BF_ARRAY_COUNT(checkFileNames); i++)
  539. {
  540. String filePath;
  541. filePath += path;
  542. filePath += checkFileNames[i];
  543. auto writeTime = GetFileTimeWrite(filePath);
  544. hashCtx.Mixin(writeTime);
  545. }
  546. }
  547. #endif
  548. mBackendHash = hashCtx.Finish128();
  549. }
  550. BfCodeGen::~BfCodeGen()
  551. {
  552. if (mIsUsingReleaseThunk)
  553. mKillFunc();
  554. if (mReleaseModule != NULL)
  555. BfpDynLib_Release(mReleaseModule);
  556. ClearOldThreads(true);
  557. for (auto thread : mThreads)
  558. {
  559. thread->mShuttingDown = true;
  560. }
  561. mRequestEvent.Set(true);
  562. for (auto thread : mThreads)
  563. {
  564. thread->Shutdown();
  565. delete thread;
  566. }
  567. for (auto request : mRequests)
  568. {
  569. delete request;
  570. }
  571. for (auto& entry : mDirectoryCache)
  572. delete entry.mValue;
  573. }
  574. void BfCodeGen::ResetStats()
  575. {
  576. mQueuedCount = 0;
  577. mCompletionCount = 0;
  578. }
  579. void BfCodeGen::UpdateStats()
  580. {
  581. while (mRequests.size() != 0)
  582. {
  583. auto request = mRequests[0];
  584. if (request->mResult.mType == BfCodeGenResult_NotDone)
  585. return;
  586. RequestComplete(request);
  587. delete request;
  588. mRequests.RemoveAt(0);
  589. return;
  590. }
  591. }
  592. void BfCodeGen::ClearOldThreads(bool waitForThread)
  593. {
  594. while (mOldThreads.size() != 0)
  595. {
  596. auto thread = mOldThreads[0];
  597. if (waitForThread)
  598. thread->Shutdown();
  599. if (thread->mRunning)
  600. return;
  601. delete thread;
  602. mOldThreads.RemoveAt(0);
  603. }
  604. }
  605. void BfCodeGen::ClearBuildCache()
  606. {
  607. AutoCrit autoCrit(mCacheCritSect);
  608. for (auto& dirCachePair : mDirectoryCache)
  609. {
  610. auto dirData = dirCachePair.mValue;
  611. dirData->Clear();
  612. }
  613. // This just disables reading the cache file, but it does not disable creating
  614. // the cache structes in memory and writing them out, thus it's valid to leave
  615. // this 'true' forever
  616. mDisableCacheReads = true;
  617. #ifdef BF_USE_CODEGEN_RELEASE_THUNK
  618. BindReleaseThunks();
  619. if (mClearCacheFunc != NULL)
  620. mClearCacheFunc();
  621. #endif
  622. }
  623. void BfCodeGen::DoWriteObjectFile(BfCodeGenRequest* codeGenRequest, const void* ptr, int size, const StringImpl& outFileName, BfCodeGenResult* externResultPtr)
  624. {
  625. codeGenRequest->mData.mVals = (uint8*)ptr;
  626. codeGenRequest->mData.mSize = size;
  627. codeGenRequest->mOutFileName = outFileName;
  628. codeGenRequest->mResult.mType = BfCodeGenResult_NotDone;
  629. codeGenRequest->mResult.mErrorMsgBufLen = 0;
  630. codeGenRequest->mExternResultPtr = externResultPtr;
  631. int threadIdx = mRequestIdx % mMaxThreadCount;
  632. if (threadIdx >= (int) mThreads.size())
  633. {
  634. AutoCrit autoCrit(mThreadsCritSect);
  635. BfCodeGenThread* thread = new BfCodeGenThread();
  636. thread->mThreadIdx = threadIdx;
  637. thread->mCodeGen = this;
  638. mThreads.push_back(thread);
  639. thread->Start();
  640. }
  641. auto thread = mThreads[threadIdx];
  642. BP_ZONE("WriteObjectFile_CritSect");
  643. AutoCrit autoCrit(mPendingRequestCritSect);
  644. mPendingRequests.push_back(codeGenRequest);
  645. #ifdef BF_PLATFORM_WINDOWS
  646. BF_ASSERT(!mRequestEvent.mEvent->mManualReset); // Make sure it's out of the SignalAll state
  647. #endif
  648. mRequestEvent.Set();
  649. mRequestIdx++;
  650. }
  651. void BfCodeGen::SetMaxThreads(int maxThreads)
  652. {
  653. #ifndef MAX_THREADS
  654. mMaxThreadCount = BF_CLAMP(maxThreads, 2, 64);
  655. #endif
  656. }
  657. void BfCodeGen::BindReleaseThunks()
  658. {
  659. if (mAttemptedReleaseThunkLoad)
  660. return;
  661. mAttemptedReleaseThunkLoad = true;
  662. if (mReleaseModule == NULL)
  663. {
  664. #ifdef BF32
  665. mReleaseModule = BfpDynLib_Load("./IDEHelper32.dll");
  666. #else
  667. mReleaseModule = BfpDynLib_Load("./IDEHelper64.dll");
  668. #endif
  669. if (mReleaseModule == NULL)
  670. {
  671. BF_FATAL("Unable to locate release DLL. Please rebuild Release configuration.\n");
  672. return;
  673. }
  674. #ifdef BF32
  675. mClearCacheFunc = (GetVersionFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "_BfCodeGen_ClearCache@0");
  676. mGetVersionFunc = (GetVersionFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "_BfCodeGen_GetVersion@0");
  677. mKillFunc = (KillFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "_BfCodeGen_Kill@0");
  678. mCancelFunc = (KillFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "_BfCodeGen_Cancel@0");
  679. mFinishFunc = (FinishFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "_BfCodeGen_Finish@0");
  680. mGenerateObjFunc = (GenerateObjFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "_BfCodeGen_GenerateObj@20");
  681. #else
  682. mClearCacheFunc = (GetVersionFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "BfCodeGen_ClearCache");
  683. mGetVersionFunc = (GetVersionFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "BfCodeGen_GetVersion");
  684. mKillFunc = (KillFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "BfCodeGen_Kill");
  685. mCancelFunc = (KillFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "BfCodeGen_Cancel");
  686. mFinishFunc = (FinishFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "BfCodeGen_Finish");
  687. mGenerateObjFunc = (GenerateObjFunc)::BfpDynLib_GetProcAddress(mReleaseModule, "BfCodeGen_GenerateObj");
  688. #endif
  689. if ((mGetVersionFunc == NULL) || (mKillFunc == NULL) || (mCancelFunc == NULL) || (mFinishFunc == NULL) || (mGenerateObjFunc == NULL))
  690. {
  691. BF_FATAL("Invalid signature in IDEHelper release DLL. Please rebuild Release configuration.\n");
  692. return;
  693. }
  694. if (mGetVersionFunc() != BF_CODEGEN_VERSION)
  695. {
  696. BF_FATAL("Invalid BF_CODEGEN_VERSION in IDEHelper release DLL. Please rebuild Release configuration.\n");
  697. return;
  698. }
  699. mIsUsingReleaseThunk = true;
  700. }
  701. }
  702. bool BfCodeGen::ExternWriteObjectFile(BfCodeGenRequest* codeGenRequest)
  703. {
  704. #ifndef BF_USE_CODEGEN_RELEASE_THUNK
  705. return false;
  706. #endif
  707. BindReleaseThunks();
  708. if (!mIsUsingReleaseThunk)
  709. return false;
  710. mGenerateObjFunc(codeGenRequest->mData.mVals, codeGenRequest->mData.mSize, codeGenRequest->mOutFileName.c_str(), &codeGenRequest->mResult, codeGenRequest->mOptions);
  711. return true;
  712. }
  713. void BfCodeGen::WriteObjectFile(BfModule* bfModule, const StringImpl& outFileName, const BfCodeGenOptions& options)
  714. {
  715. mQueuedCount++;
  716. BfLogSys(bfModule->mSystem, "WriteObjectFile %s\n", outFileName.c_str());
  717. BfCodeGenRequest* codeGenRequest = new BfCodeGenRequest();
  718. mRequests.push_back(codeGenRequest);
  719. {
  720. BP_ZONE("WriteObjectFile_GetBufferData");
  721. bfModule->mBfIRBuilder->GetBufferData(codeGenRequest->mOutBuffer);
  722. }
  723. auto rootModule = bfModule;
  724. while (rootModule->mParentModule != NULL)
  725. rootModule = rootModule->mParentModule;
  726. codeGenRequest->mSrcModule = rootModule;
  727. codeGenRequest->mOutFileName = outFileName;
  728. codeGenRequest->mData = codeGenRequest->mOutBuffer;
  729. codeGenRequest->mOptions = options;
  730. if (ExternWriteObjectFile(codeGenRequest))
  731. return;
  732. DoWriteObjectFile(codeGenRequest, (void*)&codeGenRequest->mOutBuffer[0], (int)codeGenRequest->mOutBuffer.size(), codeGenRequest->mOutFileName, NULL);
  733. #ifdef DBG_FORCE_SYNCHRONIZED
  734. while (mRequests.size() != 0)
  735. {
  736. UpdateStats();
  737. }
  738. BfLogSys(bfModule->mSystem, "WriteObjectFile Done\n");
  739. #endif
  740. }
  741. String BfCodeGen::GetBuildValue(const StringImpl& buildDir, const StringImpl& key)
  742. {
  743. AutoCrit autoCrit(mCacheCritSect);
  744. BfCodeGenDirectoryData* dirCache = GetDirCache(buildDir);
  745. return dirCache->GetValue(key);
  746. }
  747. void BfCodeGen::SetBuildValue(const StringImpl& buildDir, const StringImpl & key, const StringImpl & value)
  748. {
  749. AutoCrit autoCrit(mCacheCritSect);
  750. BfCodeGenDirectoryData* dirCache = GetDirCache(buildDir);
  751. dirCache->SetValue(key, value);
  752. }
  753. void BfCodeGen::WriteBuildCache(const StringImpl& buildDir)
  754. {
  755. AutoCrit autoCrit(mCacheCritSect);
  756. BfCodeGenDirectoryData* dirCache = GetDirCache(buildDir);
  757. dirCache->Write();
  758. }
  759. void BfCodeGen::RequestComplete(BfCodeGenRequest* request)
  760. {
  761. mCompletionCount++;
  762. if ((request->mResult.mType == BfCodeGenResult_Failed) || (request->mResult.mType == BfCodeGenResult_Aborted))
  763. {
  764. BfCodeGenErrorEntry errorEntry;
  765. errorEntry.mSrcModule = request->mSrcModule;
  766. errorEntry.mOutFileName = request->mOutFileName;
  767. int errorPos = 0;
  768. while (errorPos < request->mResult.mErrorMsgBufLen)
  769. {
  770. char* errorStr = &request->mResult.mErrorMsgBuf[errorPos];
  771. errorEntry.mErrorMessages.push_back(errorStr);
  772. errorPos += (int)strlen(errorStr) + 1;
  773. }
  774. mFailedRequests.push_back(errorEntry);
  775. }
  776. else
  777. {
  778. BfCodeGenFileEntry entry;
  779. entry.mFileName = request->mOutFileName;
  780. entry.mModule = request->mSrcModule;
  781. entry.mProject = request->mSrcModule->mProject;
  782. entry.mWasCached = request->mResult.mType == BfCodeGenResult_DoneCached;
  783. entry.mModuleHotReferenced = false;
  784. mCodeGenFiles.push_back(entry);
  785. }
  786. }
  787. void BfCodeGen::ProcessErrors(BfPassInstance* passInstance, bool canceled)
  788. {
  789. for (auto& errorEntry : mFailedRequests)
  790. {
  791. if (!errorEntry.mErrorMessages.IsEmpty())
  792. {
  793. for (auto const & errorMsg : errorEntry.mErrorMessages)
  794. passInstance->Fail(StrFormat("Module '%s' failed during codegen with: %s", errorEntry.mSrcModule->mModuleName.c_str(), errorMsg.c_str()));
  795. }
  796. else if (!canceled)
  797. passInstance->Fail(StrFormat("Failed to create %s", errorEntry.mOutFileName.c_str()));
  798. // mHadBuildError forces it to build again
  799. errorEntry.mSrcModule->mHadBuildError = true;
  800. }
  801. mFailedRequests.Clear();
  802. bool showedCacheError = false;
  803. for (auto& dirEntryPair : mDirectoryCache)
  804. {
  805. auto dirEntry = dirEntryPair.mValue;
  806. if (!dirEntry->mError.empty())
  807. {
  808. if (!showedCacheError)
  809. {
  810. passInstance->Fail(dirEntry->mError);
  811. showedCacheError = true;
  812. }
  813. dirEntry->mError.clear();
  814. }
  815. }
  816. }
  817. BfCodeGenDirectoryData * BfCodeGen::GetDirCache(const StringImpl & cacheDir)
  818. {
  819. BfCodeGenDirectoryData* dirCache = NULL;
  820. BfCodeGenDirectoryData** dirCachePtr = NULL;
  821. if (mDirectoryCache.TryAdd(cacheDir, NULL, &dirCachePtr))
  822. {
  823. dirCache = new BfCodeGenDirectoryData();
  824. *dirCachePtr = dirCache;
  825. dirCache->mCodeGen = this;
  826. dirCache->mDirectoryName = cacheDir;
  827. if (!mDisableCacheReads)
  828. dirCache->Read();
  829. }
  830. else
  831. {
  832. dirCache = *dirCachePtr;
  833. }
  834. return dirCache;
  835. }
  836. void BfCodeGen::Cancel()
  837. {
  838. for (auto thread : mThreads)
  839. {
  840. thread->mShuttingDown = true;
  841. }
  842. mRequestEvent.Set(true);
  843. if (mIsUsingReleaseThunk)
  844. mCancelFunc();
  845. }
  846. void BfCodeGen::ClearResults()
  847. {
  848. mFailedRequests.Clear();
  849. mCodeGenFiles.Clear();
  850. }
  851. bool BfCodeGen::Finish()
  852. {
  853. BP_ZONE("BfCodeGen::Finish");
  854. while (mRequests.size() != 0)
  855. {
  856. auto request = mRequests[0];
  857. if (request->mResult.mType == BfCodeGenResult_NotDone)
  858. {
  859. bool hasRunningThreads = false;
  860. for (auto thread : mThreads)
  861. {
  862. if (thread->mRunning)
  863. hasRunningThreads = true;
  864. }
  865. if (!hasRunningThreads)
  866. {
  867. mPendingRequests.Clear();
  868. request->mResult.mType = BfCodeGenResult_Aborted;
  869. continue;
  870. }
  871. mDoneEvent.WaitFor(20);
  872. continue;
  873. }
  874. RequestComplete(request);
  875. delete request;
  876. mRequests.RemoveAt(0);
  877. return false;
  878. }
  879. if (mIsUsingReleaseThunk)
  880. {
  881. // Make the thunk release its threads (and more important, its LLVM contexts)
  882. mFinishFunc();
  883. }
  884. // We need to shut down these threads to remove their memory
  885. for (auto thread : mThreads)
  886. {
  887. thread->mShuttingDown = true;
  888. mOldThreads.push_back(thread);
  889. }
  890. mThreads.Clear();
  891. mRequestEvent.Set(true);
  892. ClearOldThreads(false);
  893. // for (auto request : mPendingRequests)
  894. // {
  895. // if (request->mExternResultPtr != NULL)
  896. // {
  897. // request->mExternResultPtr->mType = BfCodeGenResult_Aborted;
  898. // //delete request;
  899. // }
  900. // //request->mResult.mType = BfCodeGenResult_Aborted;
  901. // //delete request;
  902. // }
  903. // mPendingRequests.Clear();
  904. ///
  905. for (auto& cachePair : mDirectoryCache)
  906. {
  907. auto cacheDir = cachePair.mValue;
  908. cacheDir->Write();
  909. cacheDir->mVerified = false;
  910. }
  911. mRequestEvent.Reset();
  912. mRequestIdx = 0;
  913. return true;
  914. }
  915. //////////////////////////////////////////////////////////////////////////
  916. static BfCodeGen* gExternCodeGen = NULL;
  917. BF_EXPORT void BF_CALLTYPE Targets_Create();
  918. BF_EXPORT void BF_CALLTYPE Targets_Delete();
  919. static void GetExternCodeGen()
  920. {
  921. if (gExternCodeGen == NULL)
  922. {
  923. gExternCodeGen = new BfCodeGen();
  924. Targets_Create();
  925. }
  926. }
  927. BF_EXPORT int BF_CALLTYPE BfCodeGen_GetVersion()
  928. {
  929. return BF_CODEGEN_VERSION;
  930. }
  931. BF_EXPORT void BF_CALLTYPE BfCodeGen_ClearCache()
  932. {
  933. GetExternCodeGen();
  934. gExternCodeGen->ClearBuildCache();
  935. }
  936. BF_EXPORT void BF_CALLTYPE BfCodeGen_Finish()
  937. {
  938. if (gExternCodeGen != NULL)
  939. {
  940. gExternCodeGen->Finish();
  941. gExternCodeGen->ClearResults();
  942. }
  943. }
  944. BF_EXPORT void BF_CALLTYPE BfCodeGen_Kill()
  945. {
  946. delete gExternCodeGen;
  947. gExternCodeGen = NULL;
  948. Targets_Delete();
  949. }
  950. BF_EXPORT void BF_CALLTYPE BfCodeGen_Cancel()
  951. {
  952. if (gExternCodeGen != NULL)
  953. gExternCodeGen->Cancel();
  954. }
  955. BF_EXPORT void BF_CALLTYPE BfCodeGen_GenerateObj(const void* ptr, int size, const char* outFileName, BfCodeGenResult* resultPtr, const BfCodeGenOptions& options)
  956. {
  957. GetExternCodeGen();
  958. BfCodeGenRequest* codeGenRequest = new BfCodeGenRequest();
  959. codeGenRequest->mOptions = options;
  960. gExternCodeGen->DoWriteObjectFile(codeGenRequest, ptr, size, outFileName, resultPtr);
  961. }