Jiahan Lab Notebook #1731
Replies: 3 comments 3 replies
-
Awesome! Thank you for getting this started!
|
Beta Was this translation helpful? Give feedback.
-
GoalConcretize the implementation of lowering Progress
func.func @main(%arg0 : i32, %arg1 : i32) -> i32 {
%0 = arith.cmpi slt, %arg0, %arg1 : i32
%1 = scf.if %0 -> i32 {
%3 = arith.addi %arg0, %arg1 : i32
scf.yield %3 : i32
} else {
scf.yield %arg1 : i32
}
return %1 : i32
}
And my proposed desired, lowered IR is: module attributes {calyx.entrypoint = "main"} {
calyx.component @main(%in0: i32, %in1: i32, %clk: i1 {clk}, %reset: i1 {reset}, %go: i1 {go}) -> (%out0: i32, %done: i1 {done}) {
%std_slt_0.left, %std_slt_0.right, %std_slt_0.out = calyx.std_slt @std_slt_0 : i32, i32, i1
%std_add_0.left, %std_add_0.right, %std_add_0.out = calyx.std_add @std_add_0 : i32, i32, i32
%yield_reg.in, %yield_reg.write_en, %yield_reg.clk, %yield_reg.reset, %yield_reg.out, %yield_reg.done = calyx.register @yield_reg : i32, i1, i1, i1, i32, i1
%ret_reg.in, %ret_reg.write_en, %ret_reg.clk, %ret_reg.reset, %ret_reg.out, %ret_reg.done = calyx.register @ret_reg : i32, i1, i1, i1, i32, i1
calyx.wires {
calyx.assign %out = %ret_reg.out : i32
calyx.comb_group @bb0_0 {
calyx.assign %std_slt.left = %arg0 : i32
calyx.assign %std_slt.right = %arg1 : i32
}
calyx.comb_group @bb0_1 {
calyx.assign %std_add.left = %arg0 : i32
calyx.assign %std_add.right = %arg1 : i32
}
calyx.group @assign_then_block {
calyx.assign %yield_reg.in = %std_add.out : i32
calyx.assign %yield_reg.write_en = %true : i1
calyx.group_done %yield_reg.done : i1
}
calyx.group @assign_else_block {
calyx.assign %yield_reg.in = %arg1 : i32
calyx.assign %yield_reg.write_en = %true : i1
calyx.group_done %yield_reg.done : i1
}
calyx.group @ret_assign {
calyx.assign %ret_reg.in = %yield_reg.out : i32
calyx.assign %ret_reg.write_en = %true : i1
calyx.group_done %ret_reg.done : i1
}
}
calyx.control {
calyx.seq {
calyx.if %std_slt.out with @bb0_1 {
calyx.seq {
calyx.enable @assign_then_block
}
}
calyx.else {
calyx.enable @assign_else_block
}
}
}
} {toplevel}
}
} (Also proposed in the PR as well) BlockerFor my proposal, a few things I'm not confident about are:
module {
func.func @counter(%start : i32, %end : i32) -> i32 {
%c1 = arith.constant 1 : i32
// The 'while' loop.
%result = scf.while (%current = %start) : (i32) -> i32 {
// Before region: Computes the loop condition.
%cond = arith.cmpi slt, %current, %end : i32
scf.condition(%cond) %current : i32
} do {
^bb0(%loop_var: i32):
// Loop body: Increment the counter by 1.
%next = arith.addi %loop_var, %c1 : i32
scf.yield %next : i32
}
return %result : i32
}
} And the corresponding Calyx control is: calyx.control { // line 1
calyx.seq { // line 2
calyx.seq { // line 3
calyx.par { // line 4
calyx.enable @assign_while_0_init_0 // line 5
} // line 6
calyx.while %std_slt_0.out with @bb0_0 { // line 7
calyx.seq { // line 8
calyx.enable @assign_while_0_latch // line 9
} // line 10
} // line 11
calyx.enable @ret_assign_0 // line 12
}
}
}
} {toplevel} Area of confusion: I don't quite understand the rule of thumb of placing Design consideration/philosophy behind Calyx constructsI'm attempting to grasp the design considerations behind various Calyx constructs, such as
For SCF to Calyx lowering passI'm wondering is MLIR lowering pass eliminating dead code already? I found something interesting as, when I have: module {
func.func @counter(%start : i32, %end : i32) -> i32 {
%c1 = arith.constant 1 : i32
// The 'while' loop.
%result = scf.while (%current = %start) : (i32) -> i32 {
// Before region: Computes the loop condition.
%cond = arith.cmpi slt, %current, %end : i32
scf.condition(%cond) %current : i32
} do {
^bb0(%loop_var: i32):
// Loop body: Increment the counter by 1.
%next = arith.addi %loop_var, %c1 : i32
%another_next = arith.addi %next, %c1 : i32 // note here is another assignment
scf.yield %next : i32
}
return %result : i32
}
} The calyx.group @assign_while_0_latch {
calyx.assign %while_0_arg0_reg.in = %std_add_0.out : i32
calyx.assign %while_0_arg0_reg.write_en = %true : i1
calyx.assign %std_add_0.left = %while_0_arg0_reg.out : i32
calyx.assign %std_add_0.right = %c1_i32 : i32
calyx.group_done %while_0_arg0_reg.done : i1
} so it's essentially eliminating that line since do {
^bb0(%loop_var: i32):
// Loop body: Increment the counter by 1.
%next = arith.addi %loop_var, %c1 : i32
%another_next = arith.addi %next, %c1 : i32
scf.yield %another_next : i32
} the resulting IR becomes: calyx.group @assign_while_0_latch {
calyx.assign %while_0_arg0_reg.in = %std_add_1.out : i32
calyx.assign %while_0_arg0_reg.write_en = %true : i1
calyx.assign %std_add_1.left = %std_add_0.out : i32
calyx.assign %std_add_0.left = %while_0_arg0_reg.out : i32
calyx.assign %std_add_0.right = %c1_i32 : i32
calyx.assign %std_add_1.right = %c1_i32 : i32
calyx.group_done %while_0_arg0_reg.done : i1
} which takes that assignment into consideration. Also I want to apologize for this super late progress update, I was exploring different things and I was also blocked by some other work over the past weeks. I will update my lab notebook in time :) |
Beta Was this translation helpful? Give feedback.
-
Thanks @rachitnigam for your detailed explanation :)
Which means we don't need to worry about it yet during lowering MLIR to Calyx, and rather let Calyx itself to handle it after MLIR-to-Calyx?
These make sense! Thanks for the explanation! Are the groups used temporarily for grouping things so that we can (1) isolate computation and (2) identify code-simplification opportunities? Because when I was reading the paper, Figure 2d shows
Under this situation, |
Beta Was this translation helpful? Give feedback.
-
Hi @sampsyo @rachitnigam , just created my lab notebook and I will start the discussion here!
Goal
My current goal is to fix the issue, i.e. add support for
scf.if
during lowering by adding extra functionalities toyieldOp
.Progress
gdb
tools to track the call stack so now I understand the whole process of how themlir
file source got lowered to Calyx:runOnOperation
gets run, in which we sequentially apply lowering pattern to each operation, includingscf.yield
for our case;runPartialPattern
thus gets invoked, in which we apply patterns and fold greedily to the test code based on the configuration;funcOp
(the testing code) will walk its instructions using switch-cases that are based on operation types. And since we havescf::YieldOp
insidescf.if
, we will usebuildOp
, and then the overloading version foryieldOp
gets called here. However, it assumes that thisyieldOp
is either inside a for loop or a while loop. But it can actually be inside anifOp
as well, so the current code fails to take that into considerationBlocker
IfOp
, just like we have an if statement forForOp
and an if statement for WhileOp: ;IfOp
, something likeauto ifOp = dyn_cast<scf:IfOp>(yieldOp->getParentOp());
would be a good starting point?
2. Not a blocker, but a general question: why do we keep saying "partial lowering"? ""Partial" as to there are many HLS passes and MLIR.SCF -> Calyx is just "partial"?
3. Not a blocker, but just something I don't understand. What does
mean here? What's the benefit of using that queue?
Beta Was this translation helpful? Give feedback.
All reactions