| 185 | } |
| 186 | |
| 187 | bool IsLoopFusible(const HloInstruction& instr) { |
| 188 | // Don't fuse get-tuple-element on GPU: We can, but it's slower than not |
| 189 | // fusing. We never generate kernels for unfused GTEs. Instead, if an |
| 190 | // unfused GTE is an input to a kernel (including a fusion kernel), we |
| 191 | // compute the address of the GTE at the top of the kernel. Often we know the |
| 192 | // address of the GTE result statically, so we can do this without chasing any |
| 193 | // pointers. |
| 194 | return instr.IsFusible() && |
| 195 | ((instr.IsElementwise() && instr.operand_count() > 0) || |
| 196 | instr.opcode() == HloOpcode::kBitcast || |
| 197 | instr.opcode() == HloOpcode::kBroadcast || |
| 198 | instr.opcode() == HloOpcode::kConcatenate || |
| 199 | instr.opcode() == HloOpcode::kDynamicSlice || |
| 200 | instr.opcode() == HloOpcode::kDynamicUpdateSlice || |
| 201 | (instr.opcode() == HloOpcode::kFusion && |
| 202 | instr.fusion_kind() == HloInstruction::FusionKind::kLoop) || |
| 203 | instr.opcode() == HloOpcode::kGather || |
| 204 | instr.opcode() == HloOpcode::kIota || |
| 205 | instr.opcode() == HloOpcode::kPad || |
| 206 | (instr.opcode() == HloOpcode::kReduce && |
| 207 | !IsReductionFromOrToContiguousDimensions(instr) && |
| 208 | !instr.shape().IsTuple()) || // TODO(b/129089333): Don't fuse |
| 209 | // variadic reductions. |
| 210 | instr.opcode() == HloOpcode::kReduceWindow || |
| 211 | instr.opcode() == HloOpcode::kReshape || |
| 212 | instr.opcode() == HloOpcode::kReverse || |
| 213 | instr.opcode() == HloOpcode::kSlice || |
| 214 | instr.opcode() == HloOpcode::kConstant || |
| 215 | instr.opcode() == HloOpcode::kTranspose); |
| 216 | } |
| 217 | |
| 218 | bool IsFusible(const HloInstruction& instr) { |
| 219 | return IsInputFusible(instr) || IsLoopFusible(instr); |
no test coverage detected