5858#include " llvm/Transforms/Scalar/DCE.h"
5959#include " llvm/Transforms/Scalar/EarlyCSE.h"
6060#include " llvm/Transforms/Scalar/SROA.h"
61+ #include " llvm/Transforms/Utils/Cloning.h"
6162#include " llvm/Transforms/Utils/GlobalStatus.h"
6263
6364#include < algorithm>
@@ -295,18 +296,41 @@ void saveModuleIR(Module &M, StringRef OutFilename) {
295296 MPM.run (M, MAM);
296297}
297298
298- std::string saveModuleIR (Module &M, int I, StringRef Suff) {
299- DUMP_ENTRY_POINTS (M, EmitOnlyKernelsAsEntryPoints, " saving IR" );
299+ std::unique_ptr<Module> makeDummyImageIR (const Module &M) {
300+ auto MCopy = CloneModule (M);
301+ for (Function &F : MCopy->functions ()) {
302+ if (!F.hasFnAttribute (" indirectly-callable" ))
303+ continue ;
304+
305+ F.erase (F.begin (), F.end ());
306+ BasicBlock *newBB = BasicBlock::Create (F.getContext (), " entry" , &F);
307+ IRBuilder<> builder (newBB);
308+ if (F.getReturnType ()->isVoidTy ())
309+ builder.CreateRetVoid ();
310+ else
311+ builder.CreateRet (UndefValue::get (F.getReturnType ()));
312+ }
313+ return MCopy;
314+ }
315+
316+ std::string saveModuleIR (module_split::ModuleDesc &MD, int I, StringRef Suff) {
317+ std::unique_ptr<Module> Storage;
318+ Module *M = &MD.getModule ();
319+ if (MD.isDummyImage ()) {
320+ Storage = makeDummyImageIR (MD.getModule ());
321+ M = Storage.get ();
322+ }
323+
324+ DUMP_ENTRY_POINTS (*M, EmitOnlyKernelsAsEntryPoints, " saving IR" );
300325 StringRef FileExt = (OutputAssembly) ? " .ll" : " .bc" ;
301326 std::string OutFilename = makeResultFileName (FileExt, I, Suff);
302- saveModuleIR (M, OutFilename);
327+ saveModuleIR (* M, OutFilename);
303328 return OutFilename;
304329}
305330
306331std::string saveModuleProperties (module_split::ModuleDesc &MD,
307332 const GlobalBinImageProps &GlobProps, int I,
308- StringRef Suff, StringRef Target = " " ,
309- bool IsDummy = false ) {
333+ StringRef Suff, StringRef Target = " " ) {
310334 auto PropSet =
311335 computeModuleProperties (MD.getModule (), MD.entries (), GlobProps);
312336
@@ -318,9 +342,8 @@ std::string saveModuleProperties(module_split::ModuleDesc &MD,
318342 NewSuff += Target;
319343 }
320344
321- if (IsDummy) {
322- PropSet.add (PropSetRegTy::SYCL_VIRTUAL_FUNCTIONS, " dummy" , 1 );
323- }
345+ if (MD.isDummyImage ())
346+ PropSet.add (PropSetRegTy::SYCL_VIRTUAL_FUNCTIONS, " dummy-image" , 1 );
324347
325348 std::error_code EC;
326349 std::string SCFile = makeResultFileName (" .prop" , I, NewSuff);
@@ -420,8 +443,7 @@ void addTableRow(util::SimpleTable &Table,
420443// IR component saving is skipped, and this file name is recorded as such in
421444// the result.
422445void saveModule (std::vector<std::unique_ptr<util::SimpleTable>> &OutTables,
423- module_split::ModuleDesc &MD, int I, StringRef IRFilename,
424- bool IsDummy = false ) {
446+ module_split::ModuleDesc &MD, int I, StringRef IRFilename) {
425447 IrPropSymFilenameTriple BaseTriple;
426448 StringRef Suffix = getModuleSuffix (MD);
427449 MD.saveSplitInformationAsMetadata ();
@@ -430,7 +452,7 @@ void saveModule(std::vector<std::unique_ptr<util::SimpleTable>> &OutTables,
430452 BaseTriple.Ir = IRFilename.str ();
431453 } else {
432454 MD.cleanup ();
433- BaseTriple.Ir = saveModuleIR (MD. getModule () , I, Suffix);
455+ BaseTriple.Ir = saveModuleIR (MD, I, Suffix);
434456 }
435457 if (DoSymGen) {
436458 // save the names of the entry points - the symbol table
@@ -445,8 +467,8 @@ void saveModule(std::vector<std::unique_ptr<util::SimpleTable>> &OutTables,
445467 GlobalBinImageProps Props = {EmitKernelParamInfo, EmitProgramMetadata,
446468 EmitExportedSymbols, EmitImportedSymbols,
447469 DeviceGlobals};
448- CopyTriple.Prop = saveModuleProperties (MD, Props, I, Suffix,
449- OutputFile.Target , IsDummy );
470+ CopyTriple.Prop =
471+ saveModuleProperties (MD, Props, I, Suffix, OutputFile.Target );
450472 }
451473 addTableRow (*Table, CopyTriple);
452474 }
@@ -746,36 +768,53 @@ bool isTargetCompatibleWithModule(const std::string &Target,
746768 return true ;
747769}
748770
749- std::optional<module_split::ModuleDesc>
750- makeDummy (module_split::ModuleDesc &MD) {
771+ bool hasVirtualFunctionsAndOptionalKernelFeatures (const Module &M) {
751772 bool hasVirtualFunctions = false ;
752773 bool hasOptionalKernelFeatures = false ;
753- for (Function &F : MD. getModule () .functions ()) {
774+ for (const Function &F : M .functions ()) {
754775 if (F.hasFnAttribute (" indirectly-callable" ))
755776 hasVirtualFunctions = true ;
756777 if (F.getMetadata (" sycl_used_aspects" ))
757778 hasOptionalKernelFeatures = true ;
758779 if (hasVirtualFunctions && hasOptionalKernelFeatures)
759780 break ;
760781 }
761- if (!hasVirtualFunctions || !hasOptionalKernelFeatures)
762- return {};
763-
764- auto MDCopy = MD.clone ();
765-
766- for (Function &F : MDCopy.getModule ().functions ()) {
767- if (!F.hasFnAttribute (" indirectly-callable" ))
768- continue ;
769-
770- F.erase (F.begin (), F.end ());
771- BasicBlock *newBB = BasicBlock::Create (F.getContext (), " entry" , &F);
772- IRBuilder<> builder (newBB);
773- builder.CreateRetVoid ();
774- }
775-
776- return MDCopy;
782+ return hasVirtualFunctions && hasOptionalKernelFeatures;
777783}
778784
785+ // std::optional<module_split::ModuleDesc>
786+ // makeDummy(module_split::ModuleDesc &MD) {
787+ // bool hasVirtualFunctions = false;
788+ // bool hasOptionalKernelFeatures = false;
789+ // for (Function &F : M.functions()) {
790+ // if (F.hasFnAttribute("indirectly-callable"))
791+ // hasVirtualFunctions = true;
792+ // if (F.getMetadata("sycl_used_aspects"))
793+ // hasOptionalKernelFeatures = true;
794+ // if (hasVirtualFunctions && hasOptionalKernelFeatures)
795+ // break;
796+ // }
797+ // if (!hasVirtualFunctions || !hasOptionalKernelFeatures)
798+ // return {};
799+
800+ // auto MDCopy = MD.clone();
801+
802+ // for (Function &F : MDCopy.getModule().functions()) {
803+ // if (!F.hasFnAttribute("indirectly-callable"))
804+ // continue;
805+
806+ // F.erase(F.begin(), F.end());
807+ // BasicBlock *newBB = BasicBlock::Create(F.getContext(), "entry", &F);
808+ // IRBuilder<> builder(newBB);
809+ // if (F.getReturnType()->isVoidTy())
810+ // builder.CreateRetVoid();
811+ // else
812+ // builder.CreateRet(UndefValue::get(F.getReturnType()));
813+ // }
814+
815+ // return MDCopy;
816+ // }
817+
779818std::vector<std::unique_ptr<util::SimpleTable>>
780819processInputModule (std::unique_ptr<Module> M) {
781820 // Construct the resulting table which will accumulate all the outputs.
@@ -924,11 +963,16 @@ processInputModule(std::unique_ptr<Module> M) {
924963 ++ID;
925964 }
926965
966+ // For kernels with virtual functions and optional kernel features, generate
967+ // a dummy image to avoid link errors. A dummy image for a set of virtual
968+ // functions is a module with the same set of virtual functions, but with
969+ // those function bodies replaced with just a return.
927970 bool dummyEmitted = false ;
928971 for (module_split::ModuleDesc &IrMD : MMs) {
929- if (auto Dummy = makeDummy (IrMD)) {
930- saveModule (Tables, *Dummy, ID, OutIRFileName, /* IsDummy*/ true );
931- dummyEmitted = true ;
972+ if ((dummyEmitted = hasVirtualFunctionsAndOptionalKernelFeatures (
973+ IrMD.getModule ()))) {
974+ auto DummyImage = IrMD.makeDummy ();
975+ saveModule (Tables, DummyImage, ID, OutIRFileName);
932976 }
933977 }
934978 if (dummyEmitted)
0 commit comments