diff --git a/include/NeuraDialect/NeuraOps.td b/include/NeuraDialect/NeuraOps.td index 0a067938..5d46955d 100644 --- a/include/NeuraDialect/NeuraOps.td +++ b/include/NeuraDialect/NeuraOps.td @@ -387,10 +387,11 @@ def Neura_LoopControllerOp : Op{ }]; let arguments = (ins - AnyType:$parent_valid, // Valid predicate from the parent loop + AnyType:$parentValid, // Valid predicate from the parent loop AnyType:$start, // Start index of the loop AnyType:$end, // End index of the loop - AnyType:$step // Step size for the loop + AnyType:$step, // Step size for the loop + StrAttr:$iterationType // Type of the loop iteration (e.g., "increment", "decrement") ); let results = (outs @@ -399,5 +400,5 @@ def Neura_LoopControllerOp : Op{ ); let assemblyFormat = - "$parent_valid `(` $start `,` $end `,` $step `)` attr-dict `:` type($parent_valid) `,` type($start) `,` type($end) `,` type($step) `->` type($index) `,` type($valid)"; + " `(``parent_valid` `=` $parentValid `,` `start` `=` $start `,` `end` `=` $end `,` `step` `=` $step`)` attr-dict `:` type($parentValid) `,` type($start) `,` type($end) `,` type($step) `->` type($index) `,` type($valid)"; } \ No newline at end of file diff --git a/lib/NeuraDialect/Transforms/FuseControlFlowPass.cpp b/lib/NeuraDialect/Transforms/FuseControlFlowPass.cpp index e3b45f94..955e0cae 100644 --- a/lib/NeuraDialect/Transforms/FuseControlFlowPass.cpp +++ b/lib/NeuraDialect/Transforms/FuseControlFlowPass.cpp @@ -1,10 +1,19 @@ #include "NeuraDialect/NeuraOps.h" +#include "NeuraDialect/NeuraTypes.h" #include "mlir/Dialect/Func/IR/FuncOps.h" +#include "mlir/IR/Attributes.h" #include "mlir/IR/Operation.h" #include "mlir/IR/PatternMatch.h" #include "mlir/Pass/Pass.h" #include "mlir/Transforms/GreedyPatternRewriteDriver.h" #include "llvm/ADT/MapVector.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/LogicalResult.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include using namespace mlir; @@ -16,43 +25,472 @@ namespace { class LoopInfo { public: // Key operations in a loop. - Value reserve_val; - Value phi_val; - Value index_val; + Value index_reserve_val; // Reserve values for index. + Value index_phi_val; Value condition_val; Value not_condition_val; // Loop iteration parameters. - Value start_val; - Value end_val; - Value step_val; + Value start_val; // Start value for the loop index. + Value end_val; // End value for the loop index. + Value step_val; // Step value for the loop index. // Backward edge information. - Operation *ctrl_mov = nullptr; // Initialized to nullptr. + Operation *index_ctrl_mov = nullptr; // Initialized to nullptr. + Operation *index_grant_op = + nullptr; // The grant_predicate operation for the index. // Used for replace and update operations. llvm::SetVector ops_to_remove; llvm::MapVector>> users_to_update; + // Parent loop when handling nested loops, if any. + LoopInfo *parent_loop = nullptr; + // Adds operations to remove. void addOpToRemove(Operation *op) { if (op) { - ops_to_remove.insert(op); + this->ops_to_remove.insert(op); } } // Checks if the loop info is complete. // There is no not_condition_val because it is derived from condition_val. bool isComplete() const { - return reserve_val && phi_val && index_val && condition_val && start_val && - end_val && step_val && ctrl_mov; + return index_reserve_val && index_phi_val && condition_val && start_val && + end_val && step_val && index_ctrl_mov; } // Records the users that use the loop index and (not-)condition values. void recordUsersToUpdate() { - // TODO: Implements the logic to record users of loop index and condition - // values. + recordUsersFor(this->index_phi_val); + recordUsersFor(this->index_reserve_val); + recordUsersFor(this->index_grant_op->getResult(0)); + recordUsersFor(this->condition_val); + if (this->not_condition_val) { + recordUsersFor(this->not_condition_val); + } + } + +private: + // Records users of a value. + void recordUsersFor(Value val) { + if (!val) { + return; + } + for (OpOperand &use : val.getUses()) { + Operation *user = use.getOwner(); + // Records the user that will not be removed. + if (!ops_to_remove.contains(user)) { + users_to_update[val].push_back({user, use.getOperandNumber()}); + } + } + } +}; + +// Finds the original parameter for start value. +Value findOriginalConstant(Value val, + llvm::SetVector &ops_to_remove) { + if (!val || !val.getDefiningOp()) + return val; + + Operation *def_op = val.getDefiningOp(); + + // If the value is already a constant, return it. + if (auto const_op = dyn_cast(def_op)) { + return val; + } + + // Handle grant operations and add them to the removal list. + if (auto grant_once_op = dyn_cast(def_op)) { + ops_to_remove.insert(def_op); + return findOriginalConstant(grant_once_op.getValue(), ops_to_remove); + } + + if (auto grant_always_op = dyn_cast(def_op)) { + ops_to_remove.insert(def_op); + return findOriginalConstant(grant_always_op.getValue(), ops_to_remove); + } + + // For grant_predicate, only track value inputs and ignore condition inputs. + if (auto grant_predicate_op = dyn_cast(def_op)) { + ops_to_remove.insert(def_op); + return findOriginalConstant(grant_predicate_op.getValue(), ops_to_remove); + } + + return val; +} + +// Identifies a simple loop. +// The pattern is: reserve -> phi -> icmp -> [not] -> grant_predicate -> +// ctrl_mov <- add +std::unique_ptr identifyLoop(Operation *index_reserve_op) { + if (!isa(index_reserve_op)) { + return nullptr; + } + + // Starts from the reserve operation. + auto loop = std::make_unique(); + loop->index_reserve_val = index_reserve_op->getResult(0); + loop->addOpToRemove(index_reserve_op); + + // Identifies the phi operation. + neura::PhiOp index_phi_op = nullptr; + for (Operation *user : loop->index_reserve_val.getUsers()) { + if (auto phi = dyn_cast(user)) { + index_phi_op = phi; + break; + } + } + + if (!index_phi_op) { + llvm::errs() + << "[CtrlFlowFuse] No index phi operation found for the loop.\n"; + return nullptr; // No phi operation found. + } + + loop->index_phi_val = index_phi_op.getResult(); + loop->addOpToRemove(index_phi_op); + + // Finds the start value for loop index. + Value initial_value = nullptr; + for (Value input : index_phi_op.getInputs()) { + if (input != loop->index_reserve_val) { + initial_value = input; + break; + } + } + + if (!initial_value) { + llvm::errs() + << "[CtrlFlowFuse] No initial value found for the loop index.\n"; + return nullptr; // No start value found. + } + + loop->start_val = findOriginalConstant(initial_value, loop->ops_to_remove); + + // Identifies the phi->icmp->[not]->grant_predicate pattern. + for (Operation *phi_user : index_phi_op->getUsers()) { + if (neura::ICmpOp icmp_op = dyn_cast(phi_user)) { + if (icmp_op.getCmpType() == "slt" && + icmp_op.getLhs() == loop->index_phi_val) { + loop->condition_val = icmp_op.getResult(); + loop->end_val = icmp_op.getRhs(); + loop->addOpToRemove(icmp_op); + + // Identifies the not operation if it exists. + for (Operation *cond_user : icmp_op->getUsers()) { + if (neura::NotOp not_op = dyn_cast(cond_user)) { + loop->not_condition_val = not_op.getResult(); + loop->addOpToRemove(not_op); + break; + } + } + + // Identifies the grant_predicate operation for the index_phi_val. + for (Operation *cond_user : icmp_op->getUsers()) { + if (neura::GrantPredicateOp grant_predicate_op = + dyn_cast(cond_user)) { + if (grant_predicate_op.getValue() == loop->index_phi_val && + grant_predicate_op.getPredicate() == loop->condition_val) { + loop->index_grant_op = grant_predicate_op; + loop->addOpToRemove(grant_predicate_op); + break; + } + } + } + break; + } else { + // TODO: Adds support for other compare types if needed. + if (icmp_op.getCmpType() != "slt") { + llvm::errs() << "[CtrlFlowFuse] Unsupported compare type: " + << icmp_op.getCmpType() << "\n"; + } else { + llvm::errs() << "[CtrlFlowFuse] Loop condition does not match " + "expected value.\n"; + } + return nullptr; // Unsupported compare type. + } + } + } + + if (!loop->condition_val || !loop->end_val || !loop->index_phi_val) { + llvm::errs() << "[CtrlFlowFuse] Incomplete loop information.\n"; + return nullptr; // Incomplete loop. + } + + // Identifies the ctrl_mov<-add pattern. + for (Operation *user : loop->index_reserve_val.getUsers()) { + if (neura::CtrlMovOp ctrl_mov_op = dyn_cast(user)) { + if (ctrl_mov_op.getTarget() == loop->index_reserve_val) { + loop->index_ctrl_mov = ctrl_mov_op; + loop->addOpToRemove(ctrl_mov_op); + + if (neura::AddOp add_op = + ctrl_mov_op.getValue().getDefiningOp()) { + loop->addOpToRemove(add_op); + Value granted_index = loop->index_grant_op->getResult(0); + if (add_op.getLhs() == granted_index) { + loop->step_val = + findOriginalConstant(add_op.getRhs(), loop->ops_to_remove); + } else if (add_op.getRhs() == granted_index) { + loop->step_val = + findOriginalConstant(add_op.getLhs(), loop->ops_to_remove); + } + } + break; + } + } + } + + if (!loop->index_ctrl_mov || !loop->step_val) { + llvm::errs() << "[CtrlFlowFuse] Incomplete loop information: ctrl_mov or " + "step value not found.\n"; + return nullptr; // Incomplete loop. + } + + if (loop->isComplete()) { + loop->recordUsersToUpdate(); + return loop; + } + + return nullptr; // Incomplete loop. +} + +Value createConstantPredicate(PatternRewriter &rewriter, Location loc, + bool value) { + auto predicated_type = rewriter.getType( + rewriter.getI1Type(), rewriter.getI1Type()); + return rewriter.create(loc, predicated_type, + rewriter.getBoolAttr(value), + rewriter.getBoolAttr(true)); +} + +Value ensureCorrectType(PatternRewriter &rewriter, Location loc, Value value, + Type target_type) { + if (!value || value.getType() == target_type) { + return value; + } + // If the types don't match, we need to cast the value. + // Predicate bit defaults to null. + // TODO: Handles cases where the cast is not trivial. + return rewriter.create( + loc, target_type, value, rewriter.getStringAttr("unknown cast"), nullptr); +} + +Operation *findDefiningOp(Value value) { + if (!value) { + return nullptr; + } + return value.getDefiningOp(); +} + +LogicalResult replaceWithLoopController(LoopInfo *loop_info, + PatternRewriter &rewriter) { + if (!loop_info || !loop_info->isComplete()) { + assert(false && "LoopInfo is incomplete or null."); + return failure(); + } + + Location loc = loop_info->index_reserve_val.getLoc(); + + Operation *start_def_op = findDefiningOp(loop_info->start_val); + Operation *end_def_op = findDefiningOp(loop_info->end_val); + Operation *step_def_op = findDefiningOp(loop_info->step_val); + + // Gets the insertion point for the new loop_controller operation. + Operation *insertion_point = nullptr; + + // Compares the defining operations to find the latest one. + auto updateLatestOp = [&](Operation *op1, Operation *op2) -> Operation * { + if (!op1) + return op2; + if (!op2) + return op1; + // Returns the later operation in the block. + return op2->isBeforeInBlock(op1) ? op1 : op2; + }; + + // Updates the insertion point based on the defining operations. + if (start_def_op) { + insertion_point = updateLatestOp(insertion_point, start_def_op); + } + if (end_def_op) { + insertion_point = updateLatestOp(insertion_point, end_def_op); + } + if (step_def_op) { + insertion_point = updateLatestOp(insertion_point, step_def_op); + } + + // Sets the insertion point after the latest defining operation. + if (insertion_point) { + rewriter.setInsertionPointAfter(insertion_point); + } else { + assert(false && "No valid insertion point found for loop_controller"); + return failure(); + } + + // Creates the parentValid signal for loop_controller. + auto true_val = createConstantPredicate(rewriter, loc, true); + + // Prepares the values and iter type for loop_controller. + auto index_type = loop_info->index_phi_val.getType(); + rewriter.setInsertionPointAfter(true_val.getDefiningOp()); + + Value start_val = + ensureCorrectType(rewriter, loc, loop_info->start_val, index_type); + Value end_val = + ensureCorrectType(rewriter, loc, loop_info->end_val, index_type); + Value step_val = + ensureCorrectType(rewriter, loc, loop_info->step_val, index_type); + StringAttr iter_type; + if (neura::ICmpOp icmp_op = + dyn_cast(loop_info->condition_val.getDefiningOp())) { + if (icmp_op.getCmpType() == "slt") { + iter_type = rewriter.getStringAttr("increment"); + } else { + assert(false && "Unsupported compare type"); + return failure(); // Unsupported compare type. + } + } + + // Creates the loop_controller operation. + auto loop_controller = rewriter.create( + loc, index_type, true_val.getType(), true_val, start_val, end_val, + step_val, iter_type); + + Value new_index = loop_controller.getIndex(); + Value new_valid = loop_controller.getValid(); + + // Creates the replacement map for the loop info. + DenseMap replacement_map; + + // Creates the map for loop_info values (index_phi_val, condition_val, etc.) + replacement_map[loop_info->index_phi_val] = new_index; + if (loop_info->index_grant_op) { + replacement_map[loop_info->index_grant_op->getResult(0)] = new_index; + } + replacement_map[loop_info->condition_val] = new_valid; + neura::NotOp new_not; + if (loop_info->not_condition_val) { + rewriter.setInsertionPointAfter(loop_controller); + new_not = rewriter.create( + loc, loop_info->not_condition_val.getType(), new_valid); + replacement_map[loop_info->not_condition_val] = new_not.getResult(); + } + + // Replaces the index_reserve_val with the new index. + if (!loop_info->users_to_update[loop_info->index_phi_val].empty()) { + for (auto &user_info : + loop_info->users_to_update[loop_info->index_phi_val]) { + Operation *user = user_info.first; + unsigned idx = user_info.second; + user->setOperand(idx, new_index); + } + } + + // Replaces the granted index value with the new index. + if (loop_info->index_grant_op && + !loop_info->users_to_update[loop_info->index_grant_op->getResult(0)] + .empty()) { + for (auto &user_info : + loop_info->users_to_update[loop_info->index_grant_op->getResult(0)]) { + Operation *user = user_info.first; + unsigned idx = user_info.second; + user->setOperand(idx, new_index); + } + } + + // Replaces the condition_val with the new_valid value. + if (!loop_info->users_to_update[loop_info->condition_val].empty()) { + for (auto &user_info : + loop_info->users_to_update[loop_info->condition_val]) { + Operation *user = user_info.first; + unsigned idx = user_info.second; + user->setOperand(idx, new_valid); + } + } + + // Handles not_condition_val if it exists + if (loop_info->not_condition_val && + !loop_info->users_to_update[loop_info->not_condition_val].empty()) { + // Replaces all uses of not_condition_val with the result of new_not + for (auto &user_info : + loop_info->users_to_update[loop_info->not_condition_val]) { + Operation *user = user_info.first; + unsigned idx = user_info.second; + user->setOperand(idx, new_not.getResult()); + } + } + + // Replaces the internal uses of the old loop info values. + for (Operation *op : loop_info->ops_to_remove) { + for (OpOperand &operand : op->getOpOperands()) { + Value old_val = operand.get(); + if (replacement_map.count(old_val)) { + operand.set(replacement_map[old_val]); + } + } + } + + // Tracks erased operations. + llvm::SmallPtrSet erased_ops; + bool made_progress = true; + while (made_progress) { + made_progress = false; + for (Operation *op : loop_info->ops_to_remove) { + if (!erased_ops.contains(op) && op->use_empty()) { + rewriter.eraseOp(op); + erased_ops.insert(op); + made_progress = true; + } + } + } + + // Checks if all operations were removed. + for (Operation *op : loop_info->ops_to_remove) { + if (!erased_ops.contains(op)) { + llvm::errs() << "Warning: Could not remove operation: " << *op << "\n"; + llvm::errs() << " Users: "; + for (Value result : op->getResults()) { + for (Operation *user : result.getUsers()) { + llvm::errs() << *user << " "; + } + } + llvm::errs() << "\n"; + } + } + + return success(); +} + +struct FuseLoopControlFlowPattern : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(func::FuncOp func_op, + PatternRewriter &rewriter) const override { + // Saves all the identified loops. + std::vector> identified_loops; + + // Step 1: Identify loops in the function. + func_op.walk([&](neura::ReserveOp reserveOp) { + if (auto loop = identifyLoop(reserveOp)) { + identified_loops.push_back(std::move(loop)); + } + }); + + if (identified_loops.empty()) { + return failure(); + } + + for (auto &loop_info : identified_loops) { + if (failed(replaceWithLoopController(loop_info.get(), rewriter))) { + return failure(); + } + } + + return success(); } }; @@ -68,7 +506,13 @@ struct FuseControlFlowPass void runOnOperation() override { ModuleOp module_op = getOperation(); - // TODO: Adds the logic to fuse determined control flow operations. + RewritePatternSet patterns(&getContext()); + + patterns.add(&getContext()); + + if (failed(applyPatternsGreedily(module_op, std::move(patterns)))) { + signalPassFailure(); + } } }; } // namespace diff --git a/test/controflow_fuse/simpleloop/simpleloop.mlir b/test/controflow_fuse/simpleloop/simpleloop.mlir index 41cdad3a..32565ed8 100644 --- a/test/controflow_fuse/simpleloop/simpleloop.mlir +++ b/test/controflow_fuse/simpleloop/simpleloop.mlir @@ -1,7 +1,58 @@ -// RUN: mlir-opt %s --lower-affine --convert-scf-to-cf --convert-cf-to-llvm -o %t-llvm.mlir -// RUN: mlir-neura-opt %t-llvm.mlir --assign-accelerator --lower-arith-to-neura --lower-memref-to-neura --lower-builtin-to-neura --lower-llvm-to-neura | FileCheck %s -// RUN: mlir-neura-opt %t-llvm.mlir --assign-accelerator --lower-arith-to-neura --lower-memref-to-neura --lower-builtin-to-neura --lower-llvm-to-neura --canonicalize-cast | FileCheck %s --check-prefix=CAST -// RUN: mlir-neura-opt %t-llvm.mlir --assign-accelerator --lower-arith-to-neura --lower-memref-to-neura --lower-builtin-to-neura --lower-llvm-to-neura --leverage-predicated-value --transform-ctrl-to-data-flow | FileCheck %s -check-prefix=CTRL2DATA +// RUN: mlir-opt %s \ +// RUN: --lower-affine \ +// RUN: --convert-scf-to-cf \ +// RUN: --convert-cf-to-llvm -o %t-llvm.mlir + +// RUN: mlir-neura-opt %t-llvm.mlir \ +// RUN: --assign-accelerator \ +// RUN: --lower-arith-to-neura \ +// RUN: --lower-memref-to-neura \ +// RUN: --lower-builtin-to-neura \ +// RUN: --lower-llvm-to-neura \ +// RUN: | FileCheck %s + +// RUN: mlir-neura-opt %t-llvm.mlir \ +// RUN: --assign-accelerator \ +// RUN: --lower-arith-to-neura \ +// RUN: --lower-memref-to-neura \ +// RUN: --lower-builtin-to-neura \ +// RUN: --lower-llvm-to-neura \ +// RUN: --canonicalize-cast | FileCheck %s --check-prefix=CAST + +// RUN: mlir-neura-opt %t-llvm.mlir \ +// RUN: --assign-accelerator \ +// RUN: --lower-arith-to-neura \ +// RUN: --lower-memref-to-neura \ +// RUN: --lower-builtin-to-neura \ +// RUN: --lower-llvm-to-neura \ +// RUN: --leverage-predicated-value \ +// RUN: --transform-ctrl-to-data-flow | FileCheck %s -check-prefix=CTRL2DATA + +// RUN: mlir-neura-opt %t-llvm.mlir \ +// RUN: --assign-accelerator \ +// RUN: --lower-arith-to-neura \ +// RUN: --lower-memref-to-neura \ +// RUN: --lower-builtin-to-neura \ +// RUN: --lower-llvm-to-neura \ +// RUN: --canonicalize-cast \ +// RUN: --canonicalize-live-in \ +// RUN: --leverage-predicated-value \ +// RUN: --transform-ctrl-to-data-flow \ +// RUN: --fuse-control-flow | FileCheck %s -check-prefix=CTRLFUSE + +// RUN: mlir-neura-opt %t-llvm.mlir \ +// RUN: --assign-accelerator \ +// RUN: --lower-arith-to-neura \ +// RUN: --lower-memref-to-neura \ +// RUN: --lower-builtin-to-neura \ +// RUN: --lower-llvm-to-neura \ +// RUN: --canonicalize-cast \ +// RUN: --canonicalize-live-in \ +// RUN: --leverage-predicated-value \ +// RUN: --transform-ctrl-to-data-flow \ +// RUN: --fuse-control-flow \ +// RUN: --insert-data-mov \ +// RUN: --map-to-accelerator="mapping-strategy=heuristic" | FileCheck %s -check-prefix=CTRLFUSE-MAPPING module attributes {} { func.func @_Z10simpleloopv() -> i32 attributes {llvm.linkage = #llvm.linkage} { @@ -81,3 +132,78 @@ module attributes {} { // CTRL2DATA-NEXT: neura.ctrl_mov %18 -> %9 : !neura.data !neura.data // CTRL2DATA-NEXT: "neura.return"(%10) : (!neura.data) -> () // CTRL2DATA-NEXT: } + + +// CTRLFUSE: func.func @_Z10simpleloopv() -> i32 attributes {accelerator = "neura", llvm.linkage = #llvm.linkage} { +// CTRLFUSE-NEXT: %0 = "neura.constant"() <{predicate = true, value = 1 : i64}> : () -> !neura.data +// CTRLFUSE-NEXT: %1 = "neura.grant_always"(%0) : (!neura.data) -> !neura.data +// CTRLFUSE-NEXT: %2 = "neura.constant"() <{predicate = true, value = 128 : i64}> : () -> !neura.data +// CTRLFUSE-NEXT: %3 = "neura.grant_always"(%2) : (!neura.data) -> !neura.data +// CTRLFUSE-NEXT: %4 = "neura.grant_once"(%2) : (!neura.data) -> !neura.data +// CTRLFUSE-NEXT: %5 = "neura.constant"() <{predicate = true, value = 0 : i32}> : () -> !neura.data +// CTRLFUSE-NEXT: %6 = "neura.grant_once"(%5) : (!neura.data) -> !neura.data +// CTRLFUSE-NEXT: %7 = "neura.constant"() <{predicate = true, value = 0 : i64}> : () -> !neura.data +// CTRLFUSE-NEXT: %8 = neura.reserve : !neura.data +// CTRLFUSE-NEXT: %9 = "neura.phi"(%8, %4) : (!neura.data, !neura.data) -> !neura.data +// CTRLFUSE-NEXT: %10 = "neura.constant"() <{predicate = true, value = true}> : () -> !neura.data +// CTRLFUSE-NEXT: %index, %valid = neura.loop_controller(parent_valid = %10, start = %7, end = %9, step = %0) {iterationType = "increment"} : !neura.data, !neura.data, !neura.data, !neura.data -> !neura.data, !neura.data +// CTRLFUSE-NEXT: %11 = "neura.not"(%valid) : (!neura.data) -> !neura.data +// CTRLFUSE-NEXT: %12 = neura.reserve : !neura.data +// CTRLFUSE-NEXT: %13 = "neura.phi"(%12, %6) : (!neura.data, !neura.data) -> !neura.data +// CTRLFUSE-NEXT: %14 = neura.grant_predicate %13, %valid : !neura.data, !neura.data -> !neura.data +// CTRLFUSE-NEXT: %15 = neura.grant_predicate %4, %valid : !neura.data, !neura.data -> !neura.data +// CTRLFUSE-NEXT: %16 = neura.grant_predicate %13, %11 : !neura.data, !neura.data -> !neura.data +// CTRLFUSE-NEXT: %17 = "neura.cast"(%index) <{cast_type = "i64_to_i32"}> : (!neura.data) -> !neura.data +// CTRLFUSE-NEXT: %18 = "neura.add"(%14, %17) : (!neura.data, !neura.data) -> !neura.data +// CTRLFUSE-NEXT: neura.ctrl_mov %18 -> %12 : !neura.data !neura.data +// CTRLFUSE-NEXT: neura.ctrl_mov %15 -> %8 : !neura.data !neura.data +// CTRLFUSE-NEXT: "neura.return"(%16) : (!neura.data) -> () +// CTRLFUSE-NEXT: } + + +// CTRLFUSE-MAPPING: func.func @_Z10simpleloopv() -> i32 attributes {CompiledII = 5 : i32, RecMII = 3 : i32, ResMII = 1 : i32, accelerator = "neura", llvm.linkage = #llvm.linkage} { +// CTRLFUSE-MAPPING-NEXT: %0 = "neura.constant"() <{predicate = true, value = 1 : i64}> {mapping_locs = [{id = 5 : i32, resource = "tile", time_step = 0 : i32, x = 1 : i32, y = 1 : i32}]} : () -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %1 = "neura.data_mov"(%0) {mapping_locs = []} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %2 = "neura.grant_always"(%1) {mapping_locs = [{id = 5 : i32, resource = "tile", time_step = 1 : i32, x = 1 : i32, y = 1 : i32}]} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %3 = "neura.constant"() <{predicate = true, value = 128 : i64}> {mapping_locs = [{id = 6 : i32, resource = "tile", time_step = 0 : i32, x = 1 : i32, y = 2 : i32}]} : () -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %4 = "neura.data_mov"(%3) {mapping_locs = []} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %5 = "neura.grant_always"(%4) {mapping_locs = [{id = 6 : i32, resource = "tile", time_step = 1 : i32, x = 1 : i32, y = 2 : i32}]} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %6 = "neura.data_mov"(%3) {mapping_locs = [{id = 18 : i32, resource = "link", time_step = 0 : i32}]} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %7 = "neura.grant_once"(%6) {mapping_locs = [{id = 10 : i32, resource = "tile", time_step = 1 : i32, x = 2 : i32, y = 2 : i32}]} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %8 = "neura.constant"() <{predicate = true, value = 0 : i32}> {mapping_locs = [{id = 9 : i32, resource = "tile", time_step = 0 : i32, x = 2 : i32, y = 1 : i32}]} : () -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %9 = "neura.data_mov"(%8) {mapping_locs = [{id = 28 : i32, resource = "link", time_step = 0 : i32}]} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %10 = "neura.grant_once"(%9) {mapping_locs = [{id = 13 : i32, resource = "tile", time_step = 1 : i32, x = 3 : i32, y = 1 : i32}]} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %11 = "neura.constant"() <{predicate = true, value = 0 : i64}> {mapping_locs = [{id = 10 : i32, resource = "tile", time_step = 0 : i32, x = 2 : i32, y = 2 : i32}]} : () -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %12 = neura.reserve : !neura.data +// CTRLFUSE-MAPPING-NEXT: %13 = "neura.data_mov"(%7) {mapping_locs = [{id = 33 : i32, resource = "link", time_step = 1 : i32}]} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %14 = "neura.phi"(%12, %13) {mapping_locs = [{id = 9 : i32, resource = "tile", time_step = 2 : i32, x = 2 : i32, y = 1 : i32}]} : (!neura.data, !neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %15 = "neura.constant"() <{predicate = true, value = true}> {mapping_locs = [{id = 14 : i32, resource = "tile", time_step = 0 : i32, x = 3 : i32, y = 2 : i32}]} : () -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %16 = "neura.data_mov"(%15) {mapping_locs = [{id = 43 : i32, resource = "link", time_step = 0 : i32}, {id = 43 : i32, resource = "link", time_step = 1 : i32}, {id = 43 : i32, resource = "link", time_step = 2 : i32}]} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %17 = "neura.data_mov"(%11) {mapping_locs = []} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %18 = "neura.data_mov"(%14) {mapping_locs = [{id = 30 : i32, resource = "link", time_step = 2 : i32}]} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %19 = "neura.data_mov"(%0) {mapping_locs = [{id = 16 : i32, resource = "link", time_step = 0 : i32}, {id = 18 : i32, resource = "link", time_step = 1 : i32}, {id = 18 : i32, resource = "link", time_step = 2 : i32}]} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %index, %valid = neura.loop_controller(parent_valid = %16, start = %17, end = %18, step = %19) {iterationType = "increment", mapping_locs = [{id = 10 : i32, resource = "tile", time_step = 3 : i32, x = 2 : i32, y = 2 : i32}]} : !neura.data, !neura.data, !neura.data, !neura.data -> !neura.data, !neura.data +// CTRLFUSE-MAPPING-NEXT: %20 = "neura.data_mov"(%valid) {mapping_locs = [{id = 31 : i32, resource = "link", time_step = 3 : i32}]} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %21 = "neura.not"(%20) {mapping_locs = [{id = 6 : i32, resource = "tile", time_step = 4 : i32, x = 1 : i32, y = 2 : i32}]} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %22 = neura.reserve : !neura.data +// CTRLFUSE-MAPPING-NEXT: %23 = "neura.data_mov"(%10) {mapping_locs = [{id = 42 : i32, resource = "link", time_step = 1 : i32}]} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %24 = "neura.phi"(%22, %23) {mapping_locs = [{id = 14 : i32, resource = "tile", time_step = 2 : i32, x = 3 : i32, y = 2 : i32}]} : (!neura.data, !neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %25 = "neura.data_mov"(%24) {mapping_locs = []} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %26 = "neura.data_mov"(%valid) {mapping_locs = [{id = 32 : i32, resource = "link", time_step = 3 : i32}]} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %27 = neura.grant_predicate %25, %26 {mapping_locs = [{id = 14 : i32, resource = "tile", time_step = 4 : i32, x = 3 : i32, y = 2 : i32}]} : !neura.data, !neura.data -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %28 = "neura.data_mov"(%7) {mapping_locs = [{id = 31 : i32, resource = "link", time_step = 1 : i32}, {id = 19 : i32, resource = "link", time_step = 2 : i32}, {id = 14 : i32, resource = "link", time_step = 3 : i32}]} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %29 = "neura.data_mov"(%valid) {mapping_locs = [{id = 33 : i32, resource = "link", time_step = 3 : i32}]} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %30 = neura.grant_predicate %28, %29 {mapping_locs = [{id = 9 : i32, resource = "tile", time_step = 4 : i32, x = 2 : i32, y = 1 : i32}]} : !neura.data, !neura.data -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %31 = "neura.data_mov"(%24) {mapping_locs = [{id = 45 : i32, resource = "link", time_step = 2 : i32}, {id = 46 : i32, resource = "link", time_step = 3 : i32}, {id = 35 : i32, resource = "link", time_step = 4 : i32}]} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %32 = "neura.data_mov"(%21) {mapping_locs = [{id = 20 : i32, resource = "link", time_step = 4 : i32}]} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %33 = neura.grant_predicate %31, %32 {mapping_locs = [{id = 7 : i32, resource = "tile", time_step = 5 : i32, x = 1 : i32, y = 3 : i32}]} : !neura.data, !neura.data -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %34 = "neura.data_mov"(%index) {mapping_locs = []} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %35 = "neura.cast"(%34) <{cast_type = "i64_to_i32"}> {mapping_locs = [{id = 10 : i32, resource = "tile", time_step = 4 : i32, x = 2 : i32, y = 2 : i32}]} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %36 = "neura.data_mov"(%27) {mapping_locs = [{id = 45 : i32, resource = "link", time_step = 4 : i32}, {id = 45 : i32, resource = "link", time_step = 5 : i32}]} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %37 = "neura.data_mov"(%35) {mapping_locs = [{id = 34 : i32, resource = "link", time_step = 4 : i32}, {id = 36 : i32, resource = "link", time_step = 5 : i32}]} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: %38 = "neura.add"(%36, %37) {mapping_locs = [{id = 15 : i32, resource = "tile", time_step = 6 : i32, x = 3 : i32, y = 3 : i32}]} : (!neura.data, !neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: neura.ctrl_mov %38 -> %22 {mapping_locs = [{id = 47 : i32, resource = "link", time_step = 6 : i32}]} : !neura.data !neura.data +// CTRLFUSE-MAPPING-NEXT: neura.ctrl_mov %30 -> %12 {mapping_locs = []} : !neura.data !neura.data +// CTRLFUSE-MAPPING-NEXT: %39 = "neura.data_mov"(%33) {mapping_locs = []} : (!neura.data) -> !neura.data +// CTRLFUSE-MAPPING-NEXT: "neura.return"(%39) {mapping_locs = [{id = 7 : i32, resource = "tile", time_step = 6 : i32, x = 1 : i32, y = 3 : i32}]} : (!neura.data) -> () +// CTRLFUSE-MAPPING-NEXT: } \ No newline at end of file