| 713 | } |
| 714 | |
| 715 | Status IrEmitter::HandleReduce(HloInstruction* instr) { |
| 716 | const HloReduceInstruction* reduce = Cast<HloReduceInstruction>(instr); |
| 717 | const Shape& out_shape = reduce->shape(); |
| 718 | bool returns_tuple = !out_shape.IsArray(); |
| 719 | int accumulators_count = 1; |
| 720 | if (returns_tuple) { |
| 721 | CHECK(out_shape.IsTuple()); |
| 722 | accumulators_count = out_shape.tuple_shapes_size(); |
| 723 | } |
| 724 | |
| 725 | auto arg = reduce->operand(0); |
| 726 | absl::Span<const int64> dimensions(reduce->dimensions()); |
| 727 | HloComputation* function = reduce->to_apply(); |
| 728 | return EmitTargetElementLoop( |
| 729 | *reduce, |
| 730 | [=](const llvm_ir::IrArray::Index& index) -> StatusOr<llvm::Value*> { |
| 731 | std::vector<llvm::Value*> accumulator_addrs; |
| 732 | std::vector<llvm::Type*> accumulator_types; |
| 733 | |
| 734 | // Initialize accumulators with initial values. |
| 735 | for (int i = 0; i < accumulators_count; i++) { |
| 736 | auto init_value = reduce->init_values()[i]; |
| 737 | const Shape& element_shape = |
| 738 | returns_tuple ? out_shape.tuple_shapes(i) : out_shape; |
| 739 | PrimitiveType accumulator_type = element_shape.element_type(); |
| 740 | llvm::Type* accumulator_llvm_type = |
| 741 | llvm_ir::PrimitiveTypeToIrType(accumulator_type, module_); |
| 742 | llvm::AllocaInst* accumulator_addr = Alloca(accumulator_llvm_type); |
| 743 | Store(Load(GetBasePointer(*init_value)), accumulator_addr); |
| 744 | accumulator_addrs.push_back(accumulator_addr); |
| 745 | accumulator_types.push_back(accumulator_llvm_type); |
| 746 | } |
| 747 | |
| 748 | // The enclosing loops go over all the target elements. Now we have to |
| 749 | // compute the actual target element. For this, we build a new loop nest |
| 750 | // to iterate over all the reduction dimensions in the argument. |
| 751 | // AddLoopsForShapeOnDimensions will return an Index where induction |
| 752 | // Value*s are placed for each dimension in dimensions, and all the rest |
| 753 | // are nullptrs. |
| 754 | llvm_ir::ForLoopNest loops(IrName(reduce, "inner"), &b_); |
| 755 | std::vector<llvm::Value*> input_multi_index = |
| 756 | loops.AddLoopsForShapeOnDimensions(arg->shape(), dimensions, |
| 757 | "reduction_dim"); |
| 758 | |
| 759 | SetToFirstInsertPoint(loops.GetInnerLoopBodyBasicBlock(), &b_); |
| 760 | |
| 761 | // Build a full index for the input argument, using reduced_dims_index |
| 762 | // as the base. In reduced_dims_index only the reduction dimensions are |
| 763 | // filled in. We fill in the rest of the dimensions with induction |
| 764 | // Value*s taken from 'index' which iterates over the target array. |
| 765 | // See the high-level description in the XLA documentation for details. |
| 766 | llvm_ir::IrArray::Index::const_iterator it = index.begin(); |
| 767 | |
| 768 | for (auto& i : input_multi_index) { |
| 769 | if (i == nullptr) { |
| 770 | i = *it++; |
| 771 | } |
| 772 | } |
nothing calls this directly
no test coverage detected