@@ -645,186 +645,103 @@ absl::StatusOr<Expression*> ModuleBuilder::EmitAsInlineExpression(
645645 return NodeToExpression (node, inputs, file_, options_);
646646}
647647
648- absl::Status ModuleBuilder::EmitArrayCopyAndUpdateViaGenerate1D (
648+ absl::Status ModuleBuilder::EmitArrayCopyAndUpdateViaGenerate (
649649 std::string_view op_name, IndexableExpression* lhs,
650- IndexableExpression* rhs, Expression* update_value, IndexType index_type,
651- Type* xls_type) {
652- ArrayType* array_type = xls_type->AsArrayOrDie ();
653-
654- // Create names derived from the unique operation name to ensure the derived
655- // names are unique. We namespace via double underscore in an attempt to
656- // reserve the suffixes and avoid collisions.
657- std::string genvar_name =
658- SanitizeAndUniquifyName (absl::StrCat (op_name, " __index" ));
659- std::string generate_loop_name =
660- SanitizeAndUniquifyName (absl::StrCat (op_name, " __gen" ));
661-
662- XLS_ASSIGN_OR_RETURN (auto * genvar,
663- module_->AddGenvar (genvar_name, SourceInfo ()));
664-
665- GenerateLoop* generate_loop = module_->Add <GenerateLoop>(
666- SourceInfo (), genvar, file_->PlainLiteral (0 , SourceInfo ()),
667- file_->PlainLiteral (array_type->size (), SourceInfo ()),
668- generate_loop_name);
669-
670- // In the body there is a conditional where we choose to either assign the rhs
671- // index directly or the updated value.
672- Expression* index_is_match =
673- file_->Equals (index_type.expression , genvar, SourceInfo ());
674- Expression* rhs_at_index = file_->Index (rhs, genvar, SourceInfo ());
675- generate_loop->AddBodyNode (file_->Make <ContinuousAssignment>(
676- SourceInfo (), file_->Make <Index>(SourceInfo (), lhs, genvar),
677- file_->Ternary (index_is_match, update_value, rhs_at_index,
678- SourceInfo ())));
679- return absl::OkStatus ();
680- }
681-
682- // Emits a copy and update of an array as a sequence of assignments.
683- // Specifically, 'rhs' is copied to 'lhs' with the element at the indices
684- // 'indices' replaced with 'update_value'. Examples of emitted verilog:
685- //
686- // // lhs: bits[32][3] = array_update(rhs, value, indices=[1]):
687- // assign lhs[0] = rhs[0];
688- // assign lhs[1] = value;
689- // assign lhs[2] = rhs[1];
690- //
691- // // lhs: bits[32][3] = array_update(rhs, value, indices=[x]):
692- // assign lhs[0] = x == 0 ? value : rhs[0];
693- // assign lhs[1] = x == 1 ? value : rhs[1];
694- // assign lhs[2] = x == 2 ? value : rhs[2];
695- //
696- // // lhs: bits[32][3][2] = array_update(rhs, value, indices=[x, 1]):
697- // assign lhs[0][0] = rhs[0][0];
698- // assign lhs[0][1] = x == 0 ? value : rhs[0][1];
699- // assign lhs[0][2] = rhs[0][2];
700- // assign lhs[1][0] = rhs[1][0];
701- // assign lhs[1][1] = x == 1 ? value : rhs[0][1];
702- // assign lhs[1][2] = rhs[0][2];
703- //
704- // // lhs: bits[32][3][2] = array_update(rhs, value, indices=[x, y]):
705- // assign lhs[0][0] = (x == 0 && y == 0) ? value : rhs[0][0];
706- // assign lhs[0][1] = (x == 0 && y == 1) ? value : rhs[0][1];
707- // assign lhs[0][2] = (x == 0 && y == 2) ? value : rhs[0][2];
708- // assign lhs[1][0] = (x == 1 && y == 0) ? value : rhs[1][0];
709- // assign lhs[1][1] = (x == 1 && y == 1) ? value : rhs[1][1];
710- // assign lhs[1][2] = (x == 1 && y == 2) ? value : rhs[1][2];
711- //
712- // // lhs: bits[32][3][2] = array_update(rhs, value, indices=[x]):
713- // assign lhs[0][0] = x == 0 ? value[0] : rhs[0][0];
714- // assign lhs[0][1] = x == 0 ? value[1] : rhs[0][1];
715- // assign lhs[0][2] = x == 0 ? value[2] : rhs[0][2];
716- // assign lhs[1][0] = x == 1 ? value[0] : rhs[1][0];
717- // assign lhs[1][1] = x == 1 ? value[1] : rhs[1][1];
718- // assign lhs[1][2] = x == 1 ? value[2] : rhs[1][2];
719- //
720- // EmitArrayCopyAndUpdate recursively constructs the assignments. 'index_match'
721- // is the index match expression (or explicit true/false value) used in ternary
722- // expression to select element(s) from the update value or the rhs.
723- absl::Status ModuleBuilder::EmitArrayCopyAndUpdate (
724- IndexableExpression* lhs, IndexableExpression* rhs,
725- Expression* update_value,
726- absl::Span<const ModuleBuilder::IndexType> indices, IndexMatch index_match,
727- Type* xls_type) {
728- auto is_statically_true = [](const IndexMatch& im) {
729- return std::holds_alternative<bool >(im) && std::get<bool >(im);
730- };
731- auto is_statically_false = [](const IndexMatch& im) {
732- return std::holds_alternative<bool >(im) && !std::get<bool >(im);
733- };
734- auto combine_index_matches = [&](const IndexMatch& a,
735- const IndexMatch& b) -> IndexMatch {
736- if (is_statically_false (a) || is_statically_false (b)) {
737- return false ;
650+ IndexableExpression* rhs, Expression* update_value,
651+ absl::Span<const ModuleBuilder::IndexType> indices, Type* xls_type) {
652+ std::vector<GenerateLoopIterationSpec> iteration_specs;
653+ // Genvars for the index space of `indices`.
654+ std::vector<LogicRef*> index_genvars;
655+ // Genvars for the index space of the updated value. This will be non-empty
656+ // only if the update value is an array.
657+ std::vector<LogicRef*> update_genvars;
658+
659+ // Create the iteration space for `indices`.
660+ Type* iter_type = xls_type;
661+ for (int64_t dim_index = 0 ; dim_index < indices.size (); ++dim_index) {
662+ ArrayType* current_array = iter_type->AsArrayOrDie ();
663+ int64_t dim_size = current_array->size ();
664+
665+ std::string genvar_name =
666+ SanitizeAndUniquifyName (absl::StrCat (op_name, " __index_" , dim_index));
667+ XLS_ASSIGN_OR_RETURN (LogicRef * genvar,
668+ module_->AddGenvar (genvar_name, SourceInfo ()));
669+ index_genvars.push_back (genvar);
670+ iteration_specs.push_back (GenerateLoopIterationSpec{
671+ genvar, file_->PlainLiteral (0 , SourceInfo ()),
672+ file_->PlainLiteral (dim_size, SourceInfo ()),
673+ std::optional<std::string>(SanitizeAndUniquifyName (
674+ absl::StrCat (op_name, " __gen_" , dim_index)))});
675+
676+ iter_type = current_array->element_type ();
677+ }
678+
679+ // Create the iteration space for the update value.
680+ Type* update_leaf_type = iter_type;
681+ int64_t update_dim = 0 ;
682+ while (update_leaf_type->IsArray ()) {
683+ ArrayType* current_array = update_leaf_type->AsArrayOrDie ();
684+ int64_t dim_size = current_array->size ();
685+
686+ std::string genvar_name =
687+ SanitizeAndUniquifyName (absl::StrCat (op_name, " __update_" , update_dim));
688+ XLS_ASSIGN_OR_RETURN (LogicRef * genvar,
689+ module_->AddGenvar (genvar_name, SourceInfo ()));
690+ update_genvars.push_back (genvar);
691+ iteration_specs.push_back (GenerateLoopIterationSpec{
692+ genvar, file_->PlainLiteral (0 , SourceInfo ()),
693+ file_->PlainLiteral (dim_size, SourceInfo ()),
694+ std::optional<std::string>(SanitizeAndUniquifyName (
695+ absl::StrCat (op_name, " __gen_up_" , update_dim)))});
696+
697+ update_leaf_type = current_array->element_type ();
698+ ++update_dim;
699+ }
700+
701+ GenerateLoop* generate_loop =
702+ module_->Add <GenerateLoop>(SourceInfo (), iteration_specs);
703+
704+ // Build the index match condition.
705+ Expression* indices_match = nullptr ;
706+ for (int64_t i = 0 ; i < indices.size (); ++i) {
707+ Expression* eq =
708+ file_->Equals (indices[i].expression , index_genvars[i], SourceInfo ());
709+ indices_match = (indices_match == nullptr )
710+ ? eq
711+ : file_->LogicalAnd (indices_match, eq, SourceInfo ());
712+ }
713+
714+ // Build lhs and rhs indexed by all loop genvars (index + update dims).
715+ IndexableExpression* lhs_indexed = lhs;
716+ IndexableExpression* rhs_indexed = rhs;
717+ for (LogicRef* v : index_genvars) {
718+ lhs_indexed = file_->Index (lhs_indexed, v, SourceInfo ());
719+ rhs_indexed = file_->Index (rhs_indexed, v, SourceInfo ());
720+ }
721+ for (LogicRef* v : update_genvars) {
722+ lhs_indexed = file_->Index (lhs_indexed, v, SourceInfo ());
723+ rhs_indexed = file_->Index (rhs_indexed, v, SourceInfo ());
724+ }
725+
726+ // Build update_value indexed by its own loop genvars.
727+ Expression* update_indexed = update_value;
728+ if (!update_genvars.empty ()) {
729+ IndexableExpression* update_idx =
730+ update_value->AsIndexableExpressionOrDie ();
731+ for (LogicRef* v : update_genvars) {
732+ update_idx = file_->Index (update_idx, v, SourceInfo ());
738733 }
739- if (is_statically_true (a)) {
740- return b;
741- }
742- if (is_statically_true (b)) {
743- return a;
744- }
745- return file_->LogicalAnd (std::get<Expression*>(a), std::get<Expression*>(b),
746- SourceInfo ());
747- };
748-
749- if (indices.empty ()) {
750- if (is_statically_true (index_match)) {
751- // Indices definitely *do* match the subarray/element being replaced with
752- // update value. Assign from update value exclusively. E.g.:
753- // assign lhs[i][j] = update_value[j]
754- return AddAssignment (
755- /* xls_type=*/ xls_type,
756- /* lhs=*/ lhs,
757- /* rhs=*/ update_value,
758- /* add_assignment=*/
759- [&](Expression* lhs, Expression* rhs) {
760- assignment_section ()->Add <ContinuousAssignment>(SourceInfo (), lhs,
761- rhs);
762- });
763- }
764- if (is_statically_false (index_match)) {
765- // Indices definitely do *NOT* match the subarray/element being replaced
766- // with update value. Assign from rhs exclusively. E.g.:
767- // assign lhs[i][j] = rhs[j]
768- return AddAssignment (
769- /* xls_type=*/ xls_type,
770- /* lhs=*/ lhs,
771- /* rhs=*/ rhs,
772- /* add_assignment=*/
773- [&](Expression* lhs, Expression* rhs) {
774- assignment_section ()->Add <ContinuousAssignment>(SourceInfo (), lhs,
775- rhs);
776- });
777- }
778- // Indices may or may not match the subarray/element being replaced with
779- // update value. Use a ternary expression to pick from rhs or update
780- // value. E.g:
781- // assign lhs[i][j] = (i == idx) ? update_value[j] : rhs[j]
782- auto gen_ternary = [&](absl::Span<Expression* const > inputs) {
783- return file_->Ternary (std::get<Expression*>(index_match), inputs[0 ],
784- inputs[1 ], SourceInfo ());
785- };
786-
787- // Emit a continuous assignment with a ternary select. The ternary
788- // operation supports array types in SystemVerilog so sv_array_expr is
789- // true.
790- return AddAssignmentToGeneratedExpression (
791- xls_type, lhs, /* inputs=*/ {update_value, rhs}, gen_ternary,
792- /* add_assignment=*/
793- [&](Expression* lhs, Expression* rhs) {
794- assignment_section ()->Add <ContinuousAssignment>(SourceInfo (), lhs,
795- rhs);
796- },
797- /* sv_array_expr=*/ true );
798- }
799-
800- // Iterate through array elements and recurse.
801- ArrayType* array_type = xls_type->AsArrayOrDie ();
802- IndexType index_type = indices.front ();
803- int64_t index_bit_count = index_type.xls_type ->bit_count ();
804- for (int64_t i = 0 ; i < array_type->size (); ++i) {
805- // Compute the current index match expression for this index element.
806- IndexMatch current_index_match;
807- if (index_type.expression ->IsLiteral ()) {
808- // Index element is a literal. The condition is statically known (true or
809- // false).
810- current_index_match = index_type.expression ->IsLiteralWithValue (i);
811- } else if (Bits::MinBitCountUnsigned (i) > index_bit_count) {
812- // Index is not wide enough to hold 'i'.
813- current_index_match = false ;
814- } else {
815- // Index element is a not literal. The condition is not statically known.
816- current_index_match =
817- file_->Equals (index_type.expression ,
818- file_->Literal (UBits (i, index_bit_count), SourceInfo ()),
819- SourceInfo ());
820- }
821- XLS_RETURN_IF_ERROR (EmitArrayCopyAndUpdate (
822- file_->Index (lhs, i, SourceInfo ()), file_->Index (rhs, i, SourceInfo ()),
823- update_value, indices.subspan (1 ),
824- combine_index_matches (current_index_match, index_match),
825- array_type->element_type ()));
734+ update_indexed = update_idx;
826735 }
827736
737+ // Emit the assignment inside the generate loop body.
738+ Expression* assignment_rhs =
739+ indices_match == nullptr ? update_indexed
740+ : file_->Ternary (indices_match, update_indexed,
741+ rhs_indexed, SourceInfo ());
742+ generate_loop->AddStatement (file_->Make <ContinuousAssignment>(
743+ SourceInfo (), lhs_indexed, assignment_rhs));
744+
828745 return absl::OkStatus ();
829746}
830747
@@ -965,26 +882,13 @@ absl::StatusOr<LogicRef*> ModuleBuilder::EmitAsAssignment(
965882 IndexType{inputs[i], node->operand (i)->GetType ()->AsBitsOrDie ()});
966883 }
967884
968- // We use a generate loop in the simple case where the array is 1D and
969- // the element type is bits.
970- if (index_types.size () == 1 &&
971- node->GetType ()->AsArrayOrDie ()->element_type ()->IsBits ()) {
972- XLS_RETURN_IF_ERROR (EmitArrayCopyAndUpdateViaGenerate1D (
973- /* op_name=*/ node->GetName (),
974- /* lhs=*/ ref,
975- /* rhs=*/ inputs[0 ]->AsIndexableExpressionOrDie (),
976- /* update_value=*/ inputs[1 ],
977- /* index_type=*/ index_types[0 ],
978- /* xls_type=*/ array_type));
979- } else {
980- XLS_RETURN_IF_ERROR (EmitArrayCopyAndUpdate (
981- /* lhs=*/ ref,
982- /* rhs=*/ inputs[0 ]->AsIndexableExpressionOrDie (),
983- /* update_value=*/ inputs[1 ],
984- /* indices=*/ index_types,
985- /* index_match=*/ true ,
986- /* xls_type=*/ array_type));
987- }
885+ XLS_RETURN_IF_ERROR (EmitArrayCopyAndUpdateViaGenerate (
886+ /* op_name=*/ node->GetName (),
887+ /* lhs=*/ ref,
888+ /* rhs=*/ inputs[0 ]->AsIndexableExpressionOrDie (),
889+ /* update_value=*/ inputs[1 ],
890+ /* indices=*/ index_types,
891+ /* xls_type=*/ array_type));
988892 break ;
989893 }
990894 case Op::kArrayConcat : {
0 commit comments