@@ -572,6 +572,70 @@ static bool removeSYCLKernelsConstRefArray(Module &M) {
572
572
return true ;
573
573
}
574
574
575
+ // Removes all device_global variables from the llvm.compiler.used global
576
+ // variable. A device_global with internal linkage will be in llvm.compiler.used
577
+ // to avoid the compiler wrongfully removing it during optimizations. However,
578
+ // as an effect the device_global variables will also be distributed across
579
+ // binaries, even if llvm.compiler.used has served its purpose. To avoid
580
+ // polluting other binaries with unused device_global variables, we remove them
581
+ // from llvm.compiler.used and erase them if they have no further uses.
582
+ static bool removeDeviceGlobalFromCompilerUsed (Module &M) {
583
+ GlobalVariable *GV = M.getGlobalVariable (" llvm.compiler.used" );
584
+ if (!GV)
585
+ return false ;
586
+
587
+ // Erase the old llvm.compiler.used. A new one will be created at the end if
588
+ // there are other values in it (other than device_global).
589
+ assert (GV->user_empty () && " Unexpected llvm.compiler.used users" );
590
+ Constant *Initializer = GV->getInitializer ();
591
+ const auto *VAT = cast<ArrayType>(GV->getValueType ());
592
+ GV->setInitializer (nullptr );
593
+ GV->eraseFromParent ();
594
+
595
+ // Destroy the initializer. Keep the operands so we keep the ones we need.
596
+ SmallVector<Constant *, 8 > IOperands;
597
+ for (auto It = Initializer->op_begin (); It != Initializer->op_end (); It++)
598
+ IOperands.push_back (cast<Constant>(*It));
599
+ assert (llvm::isSafeToDestroyConstant (Initializer) &&
600
+ " Cannot remove initializer of llvm.compiler.used global" );
601
+ Initializer->destroyConstant ();
602
+
603
+ // Iterate through all operands. If they are device_global then we drop them
604
+ // and erase them if they have no uses afterwards. All other values are kept.
605
+ SmallVector<Constant *, 8 > NewOperands;
606
+ for (auto It = IOperands.begin (); It != IOperands.end (); It++) {
607
+ Constant *Op = *It;
608
+ auto *DG = dyn_cast<GlobalVariable>(Op->stripPointerCasts ());
609
+
610
+ // If it is not a device_global we keep it.
611
+ if (!DG || !isDeviceGlobalVariable (*DG)) {
612
+ NewOperands.push_back (Op);
613
+ continue ;
614
+ }
615
+
616
+ // Destroy the device_global operand.
617
+ if (llvm::isSafeToDestroyConstant (Op))
618
+ Op->destroyConstant ();
619
+
620
+ // Remove device_global if it no longer has any uses.
621
+ if (!DG->isConstantUsed ())
622
+ DG->eraseFromParent ();
623
+ }
624
+
625
+ // If we have any operands left from the original llvm.compiler.used we create
626
+ // a new one with the new size.
627
+ if (!NewOperands.empty ()) {
628
+ ArrayType *ATy = ArrayType::get (VAT->getElementType (), NewOperands.size ());
629
+ GlobalVariable *NGV =
630
+ new GlobalVariable (M, ATy, false , GlobalValue::AppendingLinkage,
631
+ ConstantArray::get (ATy, NewOperands), " " );
632
+ NGV->setName (" llvm.compiler.used" );
633
+ NGV->setSection (" llvm.metadata" );
634
+ }
635
+
636
+ return true ;
637
+ }
638
+
575
639
SmallVector<module_split::ModuleDesc, 2 >
576
640
handleESIMD (module_split::ModuleDesc &&MDesc, bool &Modified,
577
641
bool &SplitOccurred) {
@@ -719,6 +783,13 @@ processInputModule(std::unique_ptr<Module> M) {
719
783
// actions.
720
784
Modified |= removeSYCLKernelsConstRefArray (*M.get ());
721
785
786
+ // There may be device_global variables kept alive in "llvm.compiler.used"
787
+ // to keep the optimizer from wrongfully removing them. llvm.compiler.used
788
+ // symbols are usually removed at backend lowering, but this is handled here
789
+ // for SPIR-V since SYCL compilation uses llvm-spirv, not the SPIR-V backend.
790
+ if (auto Triple = M->getTargetTriple ().find (" spir" ) != std::string::npos)
791
+ Modified |= removeDeviceGlobalFromCompilerUsed (*M.get ());
792
+
722
793
// Instrument each image scope device globals if the module has been
723
794
// instrumented by sanitizer pass.
724
795
if (isModuleUsingAsan (*M))
0 commit comments