[llvm] [SPIRV] Enable DCE in instruction selection and update tests (PR #168428)
Steven Perron via llvm-commits
llvm-commits at lists.llvm.org
Thu Nov 20 09:25:19 PST 2025
================
@@ -506,22 +509,195 @@ static bool isConstReg(MachineRegisterInfo *MRI, Register OpReg) {
return false;
}
+// TODO(168736): We should make this either a flag in tabelgen
+// or reduce our dependence on the global registery, so we can remove this
+// function. It can easily be missed when new intrinsics are added.
+static bool intrinsicHasSideEffects(Intrinsic::ID ID) {
+ switch (ID) {
+ // Intrinsics that do not have side effects.
+ // This is not an exhaustive list and may need to be updated.
+ case Intrinsic::spv_all:
+ case Intrinsic::spv_alloca:
+ case Intrinsic::spv_any:
+ case Intrinsic::spv_bitcast:
+ case Intrinsic::spv_const_composite:
+ case Intrinsic::spv_cross:
+ case Intrinsic::spv_degrees:
+ case Intrinsic::spv_distance:
+ case Intrinsic::spv_extractelt:
+ case Intrinsic::spv_extractv:
+ case Intrinsic::spv_faceforward:
+ case Intrinsic::spv_fdot:
+ case Intrinsic::spv_firstbitlow:
+ case Intrinsic::spv_firstbitshigh:
+ case Intrinsic::spv_firstbituhigh:
+ case Intrinsic::spv_frac:
+ case Intrinsic::spv_gep:
+ case Intrinsic::spv_global_offset:
+ case Intrinsic::spv_global_size:
+ case Intrinsic::spv_group_id:
+ case Intrinsic::spv_insertelt:
+ case Intrinsic::spv_insertv:
+ case Intrinsic::spv_isinf:
+ case Intrinsic::spv_isnan:
+ case Intrinsic::spv_lerp:
+ case Intrinsic::spv_length:
+ case Intrinsic::spv_normalize:
+ case Intrinsic::spv_num_subgroups:
+ case Intrinsic::spv_num_workgroups:
+ case Intrinsic::spv_ptrcast:
+ case Intrinsic::spv_radians:
+ case Intrinsic::spv_reflect:
+ case Intrinsic::spv_refract:
+ case Intrinsic::spv_resource_getpointer:
+ case Intrinsic::spv_resource_handlefrombinding:
+ case Intrinsic::spv_resource_handlefromimplicitbinding:
+ case Intrinsic::spv_resource_nonuniformindex:
+ case Intrinsic::spv_rsqrt:
+ case Intrinsic::spv_saturate:
+ case Intrinsic::spv_sdot:
+ case Intrinsic::spv_sign:
+ case Intrinsic::spv_smoothstep:
+ case Intrinsic::spv_step:
+ case Intrinsic::spv_subgroup_id:
+ case Intrinsic::spv_subgroup_local_invocation_id:
+ case Intrinsic::spv_subgroup_max_size:
+ case Intrinsic::spv_subgroup_size:
+ case Intrinsic::spv_thread_id:
+ case Intrinsic::spv_thread_id_in_group:
+ case Intrinsic::spv_udot:
+ case Intrinsic::spv_undef:
+ case Intrinsic::spv_value_md:
+ case Intrinsic::spv_workgroup_size:
+ return false;
+ default:
+ return true;
+ }
+}
+
+// TODO(168736): We should make this either a flag in tabelgen
+// or reduce our dependence on the global registery, so we can remove this
+// function. It can easily be missed when new intrinsics are added.
+static bool isOpcodeWithNoSideEffects(unsigned Opcode) {
+ switch (Opcode) {
+ case SPIRV::OpTypeVoid:
+ case SPIRV::OpTypeBool:
+ case SPIRV::OpTypeInt:
+ case SPIRV::OpTypeFloat:
+ case SPIRV::OpTypeVector:
+ case SPIRV::OpTypeMatrix:
+ case SPIRV::OpTypeImage:
+ case SPIRV::OpTypeSampler:
+ case SPIRV::OpTypeSampledImage:
+ case SPIRV::OpTypeArray:
+ case SPIRV::OpTypeRuntimeArray:
+ case SPIRV::OpTypeStruct:
+ case SPIRV::OpTypeOpaque:
+ case SPIRV::OpTypePointer:
+ case SPIRV::OpTypeFunction:
+ case SPIRV::OpTypeEvent:
+ case SPIRV::OpTypeDeviceEvent:
+ case SPIRV::OpTypeReserveId:
+ case SPIRV::OpTypeQueue:
+ case SPIRV::OpTypePipe:
+ case SPIRV::OpTypeForwardPointer:
+ case SPIRV::OpTypePipeStorage:
+ case SPIRV::OpTypeNamedBarrier:
+ case SPIRV::OpTypeAccelerationStructureNV:
+ case SPIRV::OpTypeCooperativeMatrixNV:
+ case SPIRV::OpTypeCooperativeMatrixKHR:
+ return true;
+ default:
+ return false;
+ }
+}
+
bool isDead(const MachineInstr &MI, const MachineRegisterInfo &MRI) {
+ // If there are no definitions, then assume there is some other
+ // side-effect that makes this instruction live.
+ if (MI.getNumDefs() == 0)
+ return false;
+
for (const auto &MO : MI.all_defs()) {
Register Reg = MO.getReg();
- if (Reg.isPhysical() || !MRI.use_nodbg_empty(Reg))
+ if (Reg.isPhysical()) {
+ LLVM_DEBUG(dbgs() << "Not dead: def of physical register " << Reg);
return false;
+ }
+ for (const auto &UseMI : MRI.use_nodbg_instructions(Reg)) {
+ if (UseMI.getOpcode() != SPIRV::OpName) {
+ LLVM_DEBUG(dbgs() << "Not dead: def " << MO << " has use in " << UseMI);
+ return false;
+ }
+ }
}
+
if (MI.getOpcode() == TargetOpcode::LOCAL_ESCAPE || MI.isFakeUse() ||
- MI.isLifetimeMarker())
+ MI.isLifetimeMarker()) {
+ LLVM_DEBUG(
+ dbgs()
+ << "Not dead: Opcode is LOCAL_ESCAPE, fake use, or lifetime marker.\n");
return false;
- if (MI.isPHI())
+ }
+ if (MI.isPHI()) {
+ LLVM_DEBUG(dbgs() << "Dead: Phi instruction with no uses.\n");
return true;
+ }
+
+ if (MI.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS ||
+ MI.getOpcode() == TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS) {
+ const auto &Intr = cast<GIntrinsic>(MI);
+ if (!intrinsicHasSideEffects(Intr.getIntrinsicID())) {
----------------
s-perron wrote:
I added a comment. I left the function signature the same. `isDead` is already looking at the opcode it add extra complexity to the other function, which is very focused.
https://github.com/llvm/llvm-project/pull/168428
More information about the llvm-commits
mailing list