@@ -94,6 +94,8 @@ class SPIRVInstructionSelector : public InstructionSelector {
9494
9595private:
9696 void resetVRegsType (MachineFunction &MF);
97+ void removeDeadInstruction (MachineInstr &MI) const ;
98+ void removeOpNamesForDeadMI (MachineInstr &MI) const ;
9799
98100 // tblgen-erated 'select' implementation, used as the initial selector for
99101 // the patterns that don't require complex C++.
@@ -510,22 +512,202 @@ static bool isConstReg(MachineRegisterInfo *MRI, Register OpReg) {
510512 return false ;
511513}
512514
515+ // TODO(168736): We should make this either a flag in tabelgen
516+ // or reduce our dependence on the global registry, so we can remove this
517+ // function. It can easily be missed when new intrinsics are added.
518+
519+ // Most SPIR-V instrinsics are considered to have side-effects in their tablegen
520+ // definition because they are referenced in the global registry. This is a list
521+ // of intrinsics that have no side effects other than their references in the
522+ // global registry.
523+ static bool intrinsicHasSideEffects (Intrinsic::ID ID) {
524+ switch (ID) {
525+ // This is not an exhaustive list and may need to be updated.
526+ case Intrinsic::spv_all:
527+ case Intrinsic::spv_alloca:
528+ case Intrinsic::spv_any:
529+ case Intrinsic::spv_bitcast:
530+ case Intrinsic::spv_const_composite:
531+ case Intrinsic::spv_cross:
532+ case Intrinsic::spv_degrees:
533+ case Intrinsic::spv_distance:
534+ case Intrinsic::spv_extractelt:
535+ case Intrinsic::spv_extractv:
536+ case Intrinsic::spv_faceforward:
537+ case Intrinsic::spv_fdot:
538+ case Intrinsic::spv_firstbitlow:
539+ case Intrinsic::spv_firstbitshigh:
540+ case Intrinsic::spv_firstbituhigh:
541+ case Intrinsic::spv_frac:
542+ case Intrinsic::spv_gep:
543+ case Intrinsic::spv_global_offset:
544+ case Intrinsic::spv_global_size:
545+ case Intrinsic::spv_group_id:
546+ case Intrinsic::spv_insertelt:
547+ case Intrinsic::spv_insertv:
548+ case Intrinsic::spv_isinf:
549+ case Intrinsic::spv_isnan:
550+ case Intrinsic::spv_lerp:
551+ case Intrinsic::spv_length:
552+ case Intrinsic::spv_normalize:
553+ case Intrinsic::spv_num_subgroups:
554+ case Intrinsic::spv_num_workgroups:
555+ case Intrinsic::spv_ptrcast:
556+ case Intrinsic::spv_radians:
557+ case Intrinsic::spv_reflect:
558+ case Intrinsic::spv_refract:
559+ case Intrinsic::spv_resource_getpointer:
560+ case Intrinsic::spv_resource_handlefrombinding:
561+ case Intrinsic::spv_resource_handlefromimplicitbinding:
562+ case Intrinsic::spv_resource_nonuniformindex:
563+ case Intrinsic::spv_rsqrt:
564+ case Intrinsic::spv_saturate:
565+ case Intrinsic::spv_sdot:
566+ case Intrinsic::spv_sign:
567+ case Intrinsic::spv_smoothstep:
568+ case Intrinsic::spv_step:
569+ case Intrinsic::spv_subgroup_id:
570+ case Intrinsic::spv_subgroup_local_invocation_id:
571+ case Intrinsic::spv_subgroup_max_size:
572+ case Intrinsic::spv_subgroup_size:
573+ case Intrinsic::spv_thread_id:
574+ case Intrinsic::spv_thread_id_in_group:
575+ case Intrinsic::spv_udot:
576+ case Intrinsic::spv_undef:
577+ case Intrinsic::spv_value_md:
578+ case Intrinsic::spv_workgroup_size:
579+ return false ;
580+ default :
581+ return true ;
582+ }
583+ }
584+
585+ // TODO(168736): We should make this either a flag in tabelgen
586+ // or reduce our dependence on the global registry, so we can remove this
587+ // function. It can easily be missed when new intrinsics are added.
588+ static bool isOpcodeWithNoSideEffects (unsigned Opcode) {
589+ switch (Opcode) {
590+ case SPIRV::OpTypeVoid:
591+ case SPIRV::OpTypeBool:
592+ case SPIRV::OpTypeInt:
593+ case SPIRV::OpTypeFloat:
594+ case SPIRV::OpTypeVector:
595+ case SPIRV::OpTypeMatrix:
596+ case SPIRV::OpTypeImage:
597+ case SPIRV::OpTypeSampler:
598+ case SPIRV::OpTypeSampledImage:
599+ case SPIRV::OpTypeArray:
600+ case SPIRV::OpTypeRuntimeArray:
601+ case SPIRV::OpTypeStruct:
602+ case SPIRV::OpTypeOpaque:
603+ case SPIRV::OpTypePointer:
604+ case SPIRV::OpTypeFunction:
605+ case SPIRV::OpTypeEvent:
606+ case SPIRV::OpTypeDeviceEvent:
607+ case SPIRV::OpTypeReserveId:
608+ case SPIRV::OpTypeQueue:
609+ case SPIRV::OpTypePipe:
610+ case SPIRV::OpTypeForwardPointer:
611+ case SPIRV::OpTypePipeStorage:
612+ case SPIRV::OpTypeNamedBarrier:
613+ case SPIRV::OpTypeAccelerationStructureNV:
614+ case SPIRV::OpTypeCooperativeMatrixNV:
615+ case SPIRV::OpTypeCooperativeMatrixKHR:
616+ return true ;
617+ default :
618+ return false ;
619+ }
620+ }
621+
513622bool isDead (const MachineInstr &MI, const MachineRegisterInfo &MRI) {
623+ // If there are no definitions, then assume there is some other
624+ // side-effect that makes this instruction live.
625+ if (MI.getNumDefs () == 0 )
626+ return false ;
627+
514628 for (const auto &MO : MI.all_defs ()) {
515629 Register Reg = MO.getReg ();
516- if (Reg.isPhysical () || !MRI.use_nodbg_empty (Reg))
630+ if (Reg.isPhysical ()) {
631+ LLVM_DEBUG (dbgs () << " Not dead: def of physical register " << Reg);
517632 return false ;
633+ }
634+ for (const auto &UseMI : MRI.use_nodbg_instructions (Reg)) {
635+ if (UseMI.getOpcode () != SPIRV::OpName) {
636+ LLVM_DEBUG (dbgs () << " Not dead: def " << MO << " has use in " << UseMI);
637+ return false ;
638+ }
639+ }
518640 }
641+
519642 if (MI.getOpcode () == TargetOpcode::LOCAL_ESCAPE || MI.isFakeUse () ||
520- MI.isLifetimeMarker ())
643+ MI.isLifetimeMarker ()) {
644+ LLVM_DEBUG (
645+ dbgs ()
646+ << " Not dead: Opcode is LOCAL_ESCAPE, fake use, or lifetime marker.\n " );
521647 return false ;
522- if (MI.isPHI ())
648+ }
649+ if (MI.isPHI ()) {
650+ LLVM_DEBUG (dbgs () << " Dead: Phi instruction with no uses.\n " );
523651 return true ;
652+ }
653+
654+ // It is possible that the only side effect is that the instruction is
655+ // referenced in the global registry. If that is the only side effect, the
656+ // intrinsic is dead.
657+ if (MI.getOpcode () == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS ||
658+ MI.getOpcode () == TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS) {
659+ const auto &Intr = cast<GIntrinsic>(MI);
660+ if (!intrinsicHasSideEffects (Intr.getIntrinsicID ())) {
661+ LLVM_DEBUG (dbgs () << " Dead: Intrinsic with no real side effects.\n " );
662+ return true ;
663+ }
664+ }
665+
524666 if (MI.mayStore () || MI.isCall () ||
525667 (MI.mayLoad () && MI.hasOrderedMemoryRef ()) || MI.isPosition () ||
526- MI.isDebugInstr () || MI.isTerminator () || MI.isJumpTableDebugInfo ())
668+ MI.isDebugInstr () || MI.isTerminator () || MI.isJumpTableDebugInfo ()) {
669+ LLVM_DEBUG (dbgs () << " Not dead: instruction has side effects.\n " );
527670 return false ;
528- return true ;
671+ }
672+
673+ if (isPreISelGenericOpcode (MI.getOpcode ())) {
674+ // TODO: Is there a generic way to check if the opcode has side effects?
675+ LLVM_DEBUG (dbgs () << " Dead: Generic opcode with no uses.\n " );
676+ return true ;
677+ }
678+
679+ if (isOpcodeWithNoSideEffects (MI.getOpcode ())) {
680+ LLVM_DEBUG (dbgs () << " Dead: known opcode with no side effects\n " );
681+ return true ;
682+ }
683+
684+ return false ;
685+ }
686+
687+ void SPIRVInstructionSelector::removeOpNamesForDeadMI (MachineInstr &MI) const {
688+ // Delete the OpName that uses the result if there is one.
689+ for (const auto &MO : MI.all_defs ()) {
690+ Register Reg = MO.getReg ();
691+ if (Reg.isPhysical ())
692+ continue ;
693+ SmallVector<MachineInstr *, 4 > UselessOpNames;
694+ for (MachineInstr &UseMI : MRI->use_nodbg_instructions (Reg)) {
695+ assert (UseMI.getOpcode () == SPIRV::OpName &&
696+ " There is still a use of the dead function." );
697+ UselessOpNames.push_back (&UseMI);
698+ }
699+ for (MachineInstr *OpNameMI : UselessOpNames) {
700+ GR.invalidateMachineInstr (OpNameMI);
701+ OpNameMI->eraseFromParent ();
702+ }
703+ }
704+ }
705+
706+ void SPIRVInstructionSelector::removeDeadInstruction (MachineInstr &MI) const {
707+ salvageDebugInfo (*MRI, MI);
708+ GR.invalidateMachineInstr (&MI);
709+ removeOpNamesForDeadMI (MI);
710+ MI.eraseFromParent ();
529711}
530712
531713bool SPIRVInstructionSelector::select (MachineInstr &I) {
@@ -534,6 +716,13 @@ bool SPIRVInstructionSelector::select(MachineInstr &I) {
534716 assert (I.getParent () && " Instruction should be in a basic block!" );
535717 assert (I.getParent ()->getParent () && " Instruction should be in a function!" );
536718
719+ LLVM_DEBUG (dbgs () << " Checking if instruction is dead: " << I;);
720+ if (isDead (I, *MRI)) {
721+ LLVM_DEBUG (dbgs () << " Instruction is dead.\n " );
722+ removeDeadInstruction (I);
723+ return true ;
724+ }
725+
537726 Register Opcode = I.getOpcode ();
538727 // If it's not a GMIR instruction, we've selected it already.
539728 if (!isPreISelGenericOpcode (Opcode)) {
@@ -585,9 +774,7 @@ bool SPIRVInstructionSelector::select(MachineInstr &I) {
585774 // if the instruction has been already made dead by folding it away
586775 // erase it
587776 LLVM_DEBUG (dbgs () << " Instruction is folded and dead.\n " );
588- salvageDebugInfo (*MRI, I);
589- GR.invalidateMachineInstr (&I);
590- I.eraseFromParent ();
777+ removeDeadInstruction (I);
591778 return true ;
592779 }
593780
0 commit comments