Skip to content
This repository has been archived by the owner on Aug 20, 2024. It is now read-only.

Fix memory annotation deduplication #2286

Merged
merged 21 commits into from
Jul 14, 2021

Conversation

jared-barocsi
Copy link
Contributor

Contributor Checklist

  • Did you add Scaladoc to every public function/method?
  • Did you update the FIRRTL spec to include every new feature/behavior?
  • Did you add at least one test demonstrating the PR?
  • Did you delete any extraneous printlns/debugging code?
  • Did you specify the type of improvement?
  • Did you state the API impact?
  • Did you specify the code generation impact?
  • Did you request a desired merge strategy?
  • Did you add text to be included in the Release Notes for this change?

Type of Improvement

  • Bug fix
  • Backend code generation

API Impact

No changes to any API

Backend Code Generation Impact

MemoryInitAnnotations and its subclasses are 're-localized' after module deduplication, which will result in the correct Verilog being emitted for memories with these annotations.

Desired Merge Strategy

Squash and merge

Release Notes

Reviewer Checklist (only modified by reviewer)

  • Did you add the appropriate labels?
  • Did you mark the proper milestone (1.2.x, 1.3.0, 1.4.0) ?
  • Did you review?
  • Did you check whether all relevant Contributor checkboxes have been checked?
  • Did you mark as Please Merge?

Copy link
Member

@seldridge seldridge left a comment

Choose a reason for hiding this comment

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

Awesome. Thanks for tackling this, @jared-barocsi!

I have some thoughts about the dependencies for this transform included inline.

Comment on lines 117 to 121
override def prerequisites = firrtl.stage.Forms.LowForm

override def optionalPrerequisites = Nil

override def optionalPrerequisiteOf = firrtl.stage.Forms.BackendEmitters
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 this actually has no prerequisites and no optionalPrerequisiteOf.

@@ -84,7 +84,8 @@ object Forms {
Dependency(passes.PadWidths),
Dependency(passes.memlib.VerilogMemDelays),
Dependency(passes.SplitExpressions),
Dependency[firrtl.transforms.LegalizeAndReductionsTransform]
Dependency[firrtl.transforms.LegalizeAndReductionsTransform],
Dependency[firrtl.transforms.DedupAnnotationsTransform]
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 this should actually be part of ChirrtlForm and EliminateTargetPaths invalidates it. Also, any transform which implements the ResolvedAnnotationPaths trait would now invalidate annotation deduplication.

Does that make sense to you?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that does make sense. I noticed that the actually deduplication passes themselves were in HighForm, but I was previously pointed to VerilogMinimumOptimized being where I would put this pass. Clearly that approach ends up breaking some tests, along with LowFormMinimumOptimized, so I'll try and get some more clarification on where it should belong.

Copy link
Member

@seldridge seldridge Jun 30, 2021

Choose a reason for hiding this comment

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

Also, any change to Forms.scala will cause the LoweringCompilersSpec to break. That is a super-legacy thing that is hard-coding the transform order before the Dependency API was added. It then compares the new order, computed from the Dependency API, applies any "patches" to that order, and then checks that it matches. That test always needs to be updated if anything in Forms.scala changes or if dependencies change.

(It's expected that that will break with the change you made here! That will need a patch.)

Copy link
Contributor Author

@jared-barocsi jared-barocsi Jun 30, 2021

Choose a reason for hiding this comment

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

It seems that adding DedupAnnotationsTransform to Deduped, which is then appended to HighForm, does not require a change to LoweringCompilersSpec

Copy link
Contributor

@jackkoenig jackkoenig left a comment

Choose a reason for hiding this comment

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

This is a good start, thanks for the working test!

I want to take this in a slightly different direction, my suggestion comment ended up getting really long so it might be helpful to meet to discuss it.

src/main/scala/firrtl/transforms/DedupAnnotations.scala Outdated Show resolved Hide resolved
src/main/scala/firrtl/transforms/DedupAnnotations.scala Outdated Show resolved Hide resolved
Copy link
Contributor

@jackkoenig jackkoenig left a comment

Choose a reason for hiding this comment

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

This is really great, excellent work @jared-barocsi!

I left some comments inline and 1 more thing I'd like to see is the Verilog Emitter to error if the annotations were unable to be deduped. I think that logic would live here:

private[firrtl] class EmissionOptions(annotations: AnnotationSeq) {

src/main/scala/firrtl/Utils.scala Outdated Show resolved Hide resolved
* Returning None signifies this annotation will not deduplicate.
* @return
*/
private[firrtl] def dedup: Option[((ReferenceTarget, Any), Annotation, ReferenceTarget)] = None
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
private[firrtl] def dedup: Option[((ReferenceTarget, Any), Annotation, ReferenceTarget)] = None
private[firrtl] def dedup: Option[(Any, Annotation, ReferenceTarget)] = None

Since (ReferenceTarget, Any) is itself an Any, I think the key should just be Any.

Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be private to firrtl? I may be wrong but it seems to me to be pretty important to annotation writers.

It would be useful to have a generic mixin that automagically defines this, by grabbing all the parameters of the case class and filtering out the target.

Copy link
Contributor

@jackkoenig jackkoenig Jul 9, 2021

Choose a reason for hiding this comment

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

I totally agree this could be important, I don't want it to be public yet. I mainly want to get this fix in and released as 1.4.4 while we discuss what the user-facing API should be.

src/main/scala/firrtl/transforms/DedupAnnotations.scala Outdated Show resolved Hide resolved
@jared-barocsi jared-barocsi marked this pull request as ready for review July 13, 2021 18:59
Copy link
Contributor

@jackkoenig jackkoenig left a comment

Choose a reason for hiding this comment

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

LGTM! Great work!

@jackkoenig jackkoenig added this to the 1.4.x milestone Jul 14, 2021
@jackkoenig jackkoenig merged commit 4081d9f into chipsalliance:master Jul 14, 2021
mergify bot pushed a commit that referenced this pull request Jul 14, 2021
* Add transform to deduplicate memory annotations
* Add annotation deduplication to Dedup stage
* ResolveAnnotationPaths and EliminateTargetPaths now invalidate the dedup annotations transform
* Verilog emitter now throws exception when memory annotations fail to dedup

Co-authored-by: Jack Koenig <koenig@sifive.com>
(cherry picked from commit 4081d9f)
@mergify mergify bot added the Backported This PR has been backported to marked stable branch label Jul 14, 2021
@kammoh
Copy link
Contributor

kammoh commented Jul 16, 2021

The problem I reported here doesn't seem to be resolved.

AttributeAnnotation disappears when more than one instance of the module Ram1R1W exist:
https://scastie.scala-lang.org/epf6LnZkRNiXxq0vYbez4Q

@jackkoenig Do you think it could be a different issue?

@jared-barocsi
Copy link
Contributor Author

@kammoh, AttributeAnnotation needs a deduplication implementation of its own. I can follow this up with a secondary PR to resolve the issue.

@seldridge
Copy link
Member

Would you be able to provide a default implementation for all SingleTargetAnnotation (and possibly MultiTargetAnnotation)? That should catch most of this stuff.

@jared-barocsi
Copy link
Contributor Author

jared-barocsi commented Jul 19, 2021

Would you be able to provide a default implementation for all SingleTargetAnnotation (and possibly MultiTargetAnnotation)? That should catch most of this stuff.

I don't think this is possible because the tuple ('dedup key') that's used to sort all of the annotations into deduplicatable groups contains the annotation's value, and that is different across implementations of SingleTargetAnnotation (for example, MemoryFileInlineAnnotation has a filename, MemoryArrayInitAnnotation has an array values, AttributeAnnotation has the description string).

MultiTargetAnnotation requires an additional patch to the transform so that it can correctly check each target against the circuit's InstanceGraph.

@seldridge
Copy link
Member

🤔 I think what you may need here is to define "annotation value equality". Given both the existing duplicate method (explaining how to copy the annotation with a new target) and a way to compare two annotations, ignoring their targets, you may be able to avoid the value issue. For SingleTargetAnnotation, I think "annotation value equality" could be derived as equality of all product members that are not the target. For other annotations, a user may need to define this.

I think you could then group all annotations that are "annotation value equal" and, for their targets, deduplicate those and generate new annotations. I.e., produce fewer annotations or annotations with simpler targets.

I'm just spitballing here and trying to reuse as much work as possible. 😄

What do you think?

I admit MultiTargetAnnotation is a pain, but is fortunately much rarer. No need for a solution there.

@davidbiancolin
Copy link
Contributor

+1 for @seldridge's idea, but I think Jack was concerned about the API leaking out before a longer-term annotation deduplication strategy is worked out. At very least, it could be done for SingleTargetAnnotations and kept package private for now.

I'd also like see inline attributes fixed in the next 1.4.x release.

mergify bot added a commit that referenced this pull request Jul 20, 2021
* Fix memory annotation deduplication (#2286)

* Add transform to deduplicate memory annotations
* Add annotation deduplication to Dedup stage
* ResolveAnnotationPaths and EliminateTargetPaths now invalidate the dedup annotations transform
* Verilog emitter now throws exception when memory annotations fail to dedup

Co-authored-by: Jack Koenig <koenig@sifive.com>
(cherry picked from commit 4081d9f)

* Fix binary compatibility issues with memory anno dedup transform (#2295)

* Remove unused exception in dedup transform
* Fold Annotation.dedup() into dedup transform for binary compatibility
* ResolvedAnnotationPaths no longer invalidates any transforms
* Use Annotation.copy() instead of constructor in dedup logic

Co-authored-by: Jared Barocsi <82000041+jared-barocsi@users.noreply.github.com>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Backported This PR has been backported to marked stable branch
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants