Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[HWToBTOR2] Deduce resets from (Fir)RegOps #6918

Merged
merged 5 commits into from Apr 18, 2024

Conversation

TaoBi22
Copy link
Contributor

@TaoBi22 TaoBi22 commented Apr 13, 2024

Currently, the HWToBTOR2 lowering assumes all regs will only be reset by one hw.module argument called %reset, and produces incorrect BTOR2 if this isn't the case. This PR changes the pass so that register constructs in the output are instead reset by the value corresponding to the reset operand (if present) of the corresponding MLIR op.

@TaoBi22
Copy link
Contributor Author

TaoBi22 commented Apr 13, 2024

@dobios doesn't look like I can request a review from you since you're not in the LLVM org but if you could make sure this looks ok it would be much appreciated!

Comment on lines +534 to +544
size_t resetLID = noLID;
if (BlockArgument barg = dyn_cast<BlockArgument>(reset)) {
// Extract the block argument index and use that to get the line number
size_t argIdx = barg.getArgNumber();

// Check that the extracted argument is in range before using it
resetLID = inputLIDs[argIdx];

} else {
resetLID = getOpLID(reset);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think getOpLID(Value value) already covers block argument case?

Suggested change
size_t resetLID = noLID;
if (BlockArgument barg = dyn_cast<BlockArgument>(reset)) {
// Extract the block argument index and use that to get the line number
size_t argIdx = barg.getArgNumber();
// Check that the extracted argument is in range before using it
resetLID = inputLIDs[argIdx];
} else {
resetLID = getOpLID(reset);
}
size_t resetLID = getOpLID(reset);

Copy link
Contributor Author

@TaoBi22 TaoBi22 Apr 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does, but here it needs to be explicitly checked - AFAICT, this is because of some undesired behaviour in the pass currently where the SetOpLID call above inserts nullptr into the LID map if the register input is a blockarg, and getOpLID gets confused. This is pretty counterintuitive and probably worth fixing, so once this lands I'll probably do another PR to neaten this up (by explicitly incrementing the LID instead, and probably adding an assert that nullptr isn't the SetOpLID input) - in the meantime it should work fine with the explicit check.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is more that SetOpLID is never called on block arguments, so getOpLID will return null if called on a block argument, which is why I have the explicit check there. What you're doing here seems to be doing exactly what my check does in getOpLID so I don't think that it's useful (as @uenoku mentioned). I still need to test this out to make sure it works as expected. I really don't know why I didn't just get the reset like this in the first place lol

Copy link
Contributor Author

@TaoBi22 TaoBi22 Apr 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very possible I've missed something, but I think setOpLID is called on a blockarg if the reg input is a blockarg in this function? I also thought that getOpLID would work on its own originally and tried that first, but it didn't seem to behave as it should so I added the explicit check - if I just use getOpLID, then:

module {
  hw.module @MultipleResets(in %clock : !seq.clock, in %reset0 : i1, in %reset1 : i1, in %in : i32) {
    %c0_i32 = hw.constant 0 : i32
    %reg0 = seq.compreg  %in, %clock reset %reset0, %c0_i32 : i32  
    %reg1 = seq.compreg  %in, %clock reset %reset1, %c0_i32 : i32  
    %0 = comb.and bin %reset0, %reset1 : i1
    %reg2 = seq.compreg  %in, %clock reset %0, %c0_i32 : i32  
    hw.output
  }
}

from the test added in this PR gets lowered to

1 sort bitvec 1
2 input 1 reset0
3 input 1 reset1
4 sort bitvec 32
5 input 4 in
6 state 4 reg0
7 state 4 reg1
8 state 4 reg2
9 constd 4 0
10 and 1 2 3
11 ite 4 2 9 5
12 next 4 6 11
13 ite 4 11 9 5
14 next 4 7 13
15 ite 4 10 9 5
16 next 4 8 15

which is incorrect, as the line 13 ite 4 11 9 5 takes 11 (the output of the previous reset ite) as the reset signal rather than 3, which is the desired reset signal. I think this is because if next is a blockarg, setOpLID is called on its defining op (nullptr) and so getOpLID finds the 'defining op' of all blockargs in the LID map and so doesn't realise they're blockargs (as getOpLID only checks if it's been given a blockarg once it fails to find the operation in opLIDMap) (but I may well have missed something so please correct me if this doesn't sound like the right explanation!).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah damn I see what you mean, it that case it might be worth doing it manually like you did. I'll go over this pass and clean things up a bit to make the behavior a bit more consistent.

Copy link
Contributor Author

@TaoBi22 TaoBi22 Apr 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be great, thanks - I have some free cycles at the moment if I can be of any help with that! Are you happy to approve this PR in the meantime?

Copy link
Contributor

@dobios dobios left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing this! I've been meaning to do it for while but got lost in other work. Looks good to me, I'm not quite sure the explicit block argument check is necessary though given that getOpLID does exactly that.

Comment on lines +534 to +544
size_t resetLID = noLID;
if (BlockArgument barg = dyn_cast<BlockArgument>(reset)) {
// Extract the block argument index and use that to get the line number
size_t argIdx = barg.getArgNumber();

// Check that the extracted argument is in range before using it
resetLID = inputLIDs[argIdx];

} else {
resetLID = getOpLID(reset);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is more that SetOpLID is never called on block arguments, so getOpLID will return null if called on a block argument, which is why I have the explicit check there. What you're doing here seems to be doing exactly what my check does in getOpLID so I don't think that it's useful (as @uenoku mentioned). I still need to test this out to make sure it works as expected. I really don't know why I didn't just get the reset like this in the first place lol

Copy link
Contributor

@dobios dobios left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be good enough for now imo, I have a few changes to this pass on my fork which will hopefully clean things up and make this cleaner to fix in the future. Thanks for the fix!

@TaoBi22 TaoBi22 merged commit 21b9395 into llvm:main Apr 18, 2024
4 checks passed
cepheus69 pushed a commit to cepheus69/circt that referenced this pull request Apr 22, 2024
* Fetch reset value from (Fir)RegOp instead of hardcoded signal name

* Add block arg check on reset

* Add test for multiple resets &non-block-arg resets

* Fix test module name

* Add EOF newline to test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants