profile
viewpoint

apple/swift 54003

The Swift Programming Language

apple/swift-evolution 11567

This maintains proposals for changes and user-visible enhancements to the Swift Programming Language.

apple/swift-package-manager 8012

The Package Manager for the Swift Programming Language

llvm/llvm-project 6431

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies. Note: the repository does not accept github pull requests at this moment. Please submit your patches at http://reviews.llvm.org.

apple/swift-corelibs-foundation 4039

The Foundation Project, providing core utilities, internationalization, and OS independence

apple/swift-corelibs-libdispatch 1899

The libdispatch Project, (a.k.a. Grand Central Dispatch), for concurrency on multicore hardware

apple/swift-corelibs-xctest 862

The XCTest Project, A Swift core library for providing unit test support

apple/swift-llbuild 799

A low-level build system, used by Xcode and the Swift Package Manager

pull request commentllvm/circt

[FIRRTL, SV] Lower FIRRTL Verification Ops to SystemVerilog

cool, thanks @seldridge !

seldridge

comment created time in 10 hours

PullRequestReviewEvent

pull request commentllvm/circt

[NFC] Cleanup whitespace in FIRRTL ODS

Thx, feel free to directly commit patches you think are obvious :-)

seldridge

comment created time in 16 hours

Pull request review commentllvm/circt

[FIRRTL, SV] Lower FIRRTL Verification Ops to SystemVerilog

 def FatalOp : SVOp<"fatal"> {    let assemblyFormat = [{ attr-dict }]; }++//===----------------------------------------------------------------------===//+// Verification Statements+//===----------------------------------------------------------------------===//++def AssertOp : SVOp<"assert"> {+  let summary = "immediate assertion statement";+  let description = [{+    Assert that a specific condition is always true.+  }];++  let arguments = (ins AnyType:$predicate);+  let results = (outs);++  let assemblyFormat = [{ attr-dict `(` $predicate `)` `:` type($predicate) }];+}++def AssumeOp : SVOp<"assume"> {+  let summary = "assume property statement";+  let description = [{+    Assume that a specific property is always true.+  }];++  let arguments = (ins AnyType:$property);+  let results = (outs);++  let assemblyFormat = [{ attr-dict `(` $property `)` `:` type($property) }];+}+

However, if you want to utilize the complex case from FIRRTL lowering, then that would be a client and we should totally do it :-)

seldridge

comment created time in 21 hours

PullRequestReviewEvent

Pull request review commentllvm/circt

[FIRRTL, SV] Lower FIRRTL Verification Ops to SystemVerilog

 def FatalOp : SVOp<"fatal"> {    let assemblyFormat = [{ attr-dict }]; }++//===----------------------------------------------------------------------===//+// Verification Statements+//===----------------------------------------------------------------------===//++def AssertOp : SVOp<"assert"> {+  let summary = "immediate assertion statement";+  let description = [{+    Assert that a specific condition is always true.+  }];++  let arguments = (ins AnyType:$predicate);+  let results = (outs);++  let assemblyFormat = [{ attr-dict `(` $predicate `)` `:` type($predicate) }];+}++def AssumeOp : SVOp<"assume"> {+  let summary = "assume property statement";+  let description = [{+    Assume that a specific property is always true.+  }];++  let arguments = (ins AnyType:$property);+  let results = (outs);++  let assemblyFormat = [{ attr-dict `(` $property `)` `:` type($property) }];+}+

Does SystemVerilog do something with the name? If not, I'd recommend that we don't proactively add complexity in case it is helpful.

I also think it would be worth handling the basic case (which you have here) separate from the complicated case (which should have two regions like the if stmt does). We can add the complex case when there is a client for it.

seldridge

comment created time in 21 hours

PullRequestReviewEvent

Pull request review commentllvm/circt

[FIRRTL, SV] Lower FIRRTL Verification Ops to SystemVerilog

 def FatalOp : SVOp<"fatal"> {    let assemblyFormat = [{ attr-dict }]; }++//===----------------------------------------------------------------------===//+// Verification Statements+//===----------------------------------------------------------------------===//++def AssertOp : SVOp<"assert"> {+  let summary = "immediate assertion statement";+  let description = [{+    Assert that a specific condition is always true.+  }];++  let arguments = (ins AnyType:$predicate);+  let results = (outs);++  let assemblyFormat = [{ attr-dict `(` $predicate `)` `:` type($predicate) }];+}++def AssumeOp : SVOp<"assume"> {+  let summary = "assume property statement";+  let description = [{+    Assume that a specific property is always true.+  }];++  let arguments = (ins AnyType:$property);+  let results = (outs);++  let assemblyFormat = [{ attr-dict `(` $property `)` `:` type($property) }];+}+

that is wacky :-)

seldridge

comment created time in a day

Pull request review commentllvm/circt

[FIRRTL, SV] Lower FIRRTL Verification Ops to SystemVerilog

 def FatalOp : SVOp<"fatal"> {    let assemblyFormat = [{ attr-dict }]; }++//===----------------------------------------------------------------------===//+// Verification Statements+//===----------------------------------------------------------------------===//++def AssertOp : SVOp<"assert"> {+  let summary = "immediate assertion statement";+  let description = [{+    Assert that a specific condition is always true.+  }];++  let arguments = (ins AnyType:$predicate);+  let results = (outs);++  let assemblyFormat = [{ attr-dict `(` $predicate `)` `:` type($predicate) }];

It's a bit subjective, but I'd drop the ('s from around the operand list in the asm syntax when it isn't "call like". Similarly in the assume/cover op as well. I'm not sure how consistent we are here, but sv.if and others don't put parens around their arguments.

seldridge

comment created time in a day

PullRequestReviewEvent
PullRequestReviewEvent

push eventllvm/circt

Chris Lattner

commit sha cdedc2f2868867c8da0c6b6a7705284375a338d3

Fix some comments, NFC.

view details

push time in a day

pull request commentllvm/circt

Adding Verilator for linting anything which produces Verilog

Right, we shouldn't run it on all the testcases, that breaks the spirit of unit testing. We should instead run a corpus of stuff against verilators lint rules, distill failures down to small examples, and encode those as tests that we capture in the testsuite.

teqdruid

comment created time in a day

issue commentllvm/circt

[FIRRTL] Parser fails on negative integer parameters

If using an APInt (like MLIR attributes are) the typical convention is to store them as signless types, sign/zero extended to their native width. In this case it should be all ones in either case.

LLVM also has an APSInt type that tracks sign internally, so you can just use a divide operator and it knows which one to do. I don't think that MLIR attributes expose that though.

seldridge

comment created time in 3 days

PullRequestReviewEvent

issue commentllvm/circt

[FIRRTL] Parse dshlw

+1, makes sense to me!

seldridge

comment created time in 3 days

push eventllvm/circt

Chris Lattner

commit sha 32b8f9b56ab4866253a43e5b5f413b5089eae37b

[LowerToRTL] Update LowerToRTLModule to work with the new rtl.module design. This introduces temporary unnamed wires to make the rewrite trivial.

view details

push time in 5 days

push eventllvm/circt

Chris Lattner

commit sha b085a1ddb0c24b23c40ddaffe44c368491cdfb33

[RTL] Add a RTLModulePortInfo::isOutput() helper method, NFC. This just tidies up some predicates.

view details

push time in 5 days

pull request commentllvm/circt

Adding Verilator for linting anything which produces Verilog

We should not require verilator, but it is ok to have verilator as an optional tool that is enabled at cmake time. I would recommend making the presence of verilator be a "lit" feature, so we can write specific tests with "REQUIRES: verilator".

teqdruid

comment created time in 5 days

Pull request review commentllvm/circt

[FIRRTL] Add InstanceOp Verification

 static ParseResult parseFExtModuleOp(OpAsmParser &parser,   return parseFModuleOp(parser, result, /*isExtModule:*/ true); } -static LogicalResult verifyFModuleOp(FModuleOp module) {+static LogicalResult verifyFModuleOp(FModuleOp &module) {   // The parent op must be a circuit op.-  auto *parentOp = module.getParentOp();-  if (!parentOp || !isa<CircuitOp>(parentOp)) {-    module.emitOpError("should be embedded into a firrtl.circuit");+  auto parentOp = dyn_cast<CircuitOp>(module.getParentOp());

I think this is ok, but random FYI: "dyn_cast" does not allow a null operand. Use "dyn_cast_or_null" if you need that. That said, in this case, I think the verify hook only gets called when things are in a container, so this should be ok.

seldridge

comment created time in 6 days

PullRequestReviewEvent
PullRequestReviewEvent

pull request commentllvm/circt

[FIRRTL] Add ExtModule Verification

nice work!

seldridge

comment created time in 6 days

Pull request review commentllvm/circt

[FIRRTL] Disallow Recursive Instantiation

 static ParseResult parseFExtModuleOp(OpAsmParser &parser,   return parseFModuleOp(parser, result, /*isExtModule:*/ true); } -static LogicalResult verifyFModuleOp(FModuleOp module) {+static LogicalResult verifyFModuleOp(FModuleOp &module) {   // The parent op must be a circuit op.-  auto *parentOp = module.getParentOp();-  if (!parentOp || !isa<CircuitOp>(parentOp)) {+  auto parentOp = dyn_cast<CircuitOp>(module.getParentOp());+  if (!parentOp) {     module.emitOpError("should be embedded into a firrtl.circuit");     return failure();   } +  // Disallow recursive instantiation.+  for (auto &op : *module.getBodyBlock()) {+    auto inst = dyn_cast<InstanceOp>(op);

Actually, even better: instance.getParentOfType<CircuitOp>(); should work.

seldridge

comment created time in 6 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[FIRRTL] Disallow Recursive Instantiation

 static ParseResult parseFExtModuleOp(OpAsmParser &parser,   return parseFModuleOp(parser, result, /*isExtModule:*/ true); } -static LogicalResult verifyFModuleOp(FModuleOp module) {+static LogicalResult verifyFModuleOp(FModuleOp &module) {   // The parent op must be a circuit op.-  auto *parentOp = module.getParentOp();-  if (!parentOp || !isa<CircuitOp>(parentOp)) {+  auto parentOp = dyn_cast<CircuitOp>(module.getParentOp());+  if (!parentOp) {     module.emitOpError("should be embedded into a firrtl.circuit");     return failure();   } +  // Disallow recursive instantiation.+  for (auto &op : *module.getBodyBlock()) {+    auto inst = dyn_cast<InstanceOp>(op);

I think something like this should work (it is constant time): dyn_cast<FModuleOp>(instance.getParentOp());

seldridge

comment created time in 6 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[FIRRTL] Add ExtModule Verification

 static LogicalResult verifyCircuitOp(CircuitOp &circuit) {     return failure();   } +  // Store a mapping of defname strings to the first external module+  // that defines it.+  llvm::StringMap<FExtModuleOp> defnameMap;++  // Verify external modules.+  for (auto &op : *circuit.getBody()) {+    if (auto extModule = dyn_cast<FExtModuleOp>(op)) {+      if (auto defname = extModule.defnameAttr()) {+        // Check that this extmodule's defname does not conflict with+        // the symbol name of any module.+        auto collidingModule = circuit.lookupSymbol(defname.getValue());+        if (isa_and_nonnull<FModuleOp>(collidingModule)) {+          auto diag =+              op.emitOpError()+              << "attribute 'defname' with value " << defname+              << " conflicts with the name of another module in the circuit";+          diag.attachNote(collidingModule->getLoc())+              << "previous module declared here";+          return failure();+        }++        // Find an optional extmodule with a defname collision. Update+        // the defnameMap if this is the first extmodule with that+        // defname or if the current extmodule takes no parameters and+        // the collision does. The latter condition improves later+        // extmodule verification as checking against a parameterless+        // module is stricter.+        auto collidingExtModuleOpt =+            Optional<FExtModuleOp>(llvm::NoneType::None);+        {+          auto value = defnameMap.find(defname.getValue());+          if (value != defnameMap.end()) {+            collidingExtModuleOpt = Optional<FExtModuleOp>(value->getValue());+            if (value->getValue().parameters() && !extModule.parameters())+              defnameMap.insert_or_assign(defname.getValue(), extModule);+          } else {+            defnameMap.insert_or_assign(defname.getValue(), extModule);+          }+        }++        // Go to next extmodule if no extmodule with the same defname+        // was found.+        if (!collidingExtModuleOpt)+          continue;++        auto collidingExtModule = collidingExtModuleOpt.getValue();++        // Check that the number of ports is exactly the same.+        auto ports = new SmallVector<std::pair<StringAttr, FIRRTLType>, 8>;+        auto collidingPorts =+            new SmallVector<std::pair<StringAttr, FIRRTLType>, 8>;

I think code review is fine. No worries about not magically knowing everything, we are all learning different things here :-)

seldridge

comment created time in 6 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[FIRRTL] Add ExtModule Verification

 static LogicalResult verifyCircuitOp(CircuitOp &circuit) {     return failure();   } +  // Store a mapping of defname to either the first external module+  // that defines it or, preferentially, the first external module+  // that defines it and has no parameters.+  llvm::DenseMap<Attribute, FExtModuleOp> defnameMap;++  // Verify external modules.+  for (auto &op : *circuit.getBody()) {+    auto extModule = dyn_cast<FExtModuleOp>(op);+    if (!extModule)+      continue;++    auto defname = extModule.defnameAttr();+    if (!defname)+      continue;++    // Check that this extmodule's defname does not conflict with+    // the symbol name of any module.+    auto collidingModule = circuit.lookupSymbol(defname.getValue());+    if (isa_and_nonnull<FModuleOp>(collidingModule)) {+      auto diag =+          op.emitOpError()+          << "attribute 'defname' with value " << defname+          << " conflicts with the name of another module in the circuit";+      diag.attachNote(collidingModule->getLoc())+          << "previous module declared here";+      return failure();+    }++    // Find an optional extmodule with a defname collision. Update+    // the defnameMap if this is the first extmodule with that+    // defname or if the current extmodule takes no parameters and+    // the collision does. The latter condition improves later+    // extmodule verification as checking against a parameterless+    // module is stricter.+    FExtModuleOp collidingExtModule;+    {+      auto &value = defnameMap[defname];+      if (value) {+        collidingExtModule = value;+        if (value.parameters() && !extModule.parameters())+          value = extModule;+      } else {+        value = extModule;+      }+    }++    // Go to next extmodule if no extmodule with the same defname+    // was found.+    if (!collidingExtModule)+      continue;++    // Check that the number of ports is exactly the same.+    SmallVector<std::pair<StringAttr, FIRRTLType>, 8> ports;+    SmallVector<std::pair<StringAttr, FIRRTLType>, 8> collidingPorts;+    extModule.getPortInfo(ports);+    collidingExtModule.getPortInfo(collidingPorts);+    if (ports.size() != collidingPorts.size()) {+      auto diag = op.emitOpError()+                  << "with 'defname' attribute " << defname << " has "+                  << ports.size()+                  << " ports which is different from a previously defined "+                     "extmodule with the same 'defname' which has "+                  << collidingPorts.size() << " ports";+      diag.attachNote(collidingExtModule.getLoc())+          << "previous extmodule definition occurred here";+      return failure();+    }++    // Check that ports match for name and type. Since parameters+    // *might* affect widths, ignore widths if either module has+    // parameters. Note that this allows for misdetections, but+    // has zero false positives.+    for (auto p : llvm::zip(ports, collidingPorts)) {+      StringAttr aName, bName;+      FIRRTLType aType, bType;+      std::forward_as_tuple(std::tie(aName, aType), std::tie(bName, bType)) = p;

Whoa, I've never seen forward_as_tuple before :-). I tend to use lower abstraction code for clarity, but this is fine.

seldridge

comment created time in 6 days

Pull request review commentllvm/circt

[FIRRTL] Add ExtModule Verification

 static LogicalResult verifyCircuitOp(CircuitOp &circuit) {     return failure();   } +  // Store a mapping of defname to either the first external module+  // that defines it or, preferentially, the first external module+  // that defines it and has no parameters.+  llvm::DenseMap<Attribute, FExtModuleOp> defnameMap;++  // Verify external modules.+  for (auto &op : *circuit.getBody()) {+    auto extModule = dyn_cast<FExtModuleOp>(op);+    if (!extModule)+      continue;++    auto defname = extModule.defnameAttr();+    if (!defname)+      continue;++    // Check that this extmodule's defname does not conflict with+    // the symbol name of any module.+    auto collidingModule = circuit.lookupSymbol(defname.getValue());+    if (isa_and_nonnull<FModuleOp>(collidingModule)) {+      auto diag =+          op.emitOpError()+          << "attribute 'defname' with value " << defname+          << " conflicts with the name of another module in the circuit";+      diag.attachNote(collidingModule->getLoc())+          << "previous module declared here";+      return failure();+    }++    // Find an optional extmodule with a defname collision. Update+    // the defnameMap if this is the first extmodule with that+    // defname or if the current extmodule takes no parameters and+    // the collision does. The latter condition improves later+    // extmodule verification as checking against a parameterless+    // module is stricter.+    FExtModuleOp collidingExtModule;+    {+      auto &value = defnameMap[defname];+      if (value) {+        collidingExtModule = value;+        if (value.parameters() && !extModule.parameters())+          value = extModule;+      } else {+        value = extModule;+      }+    }++    // Go to next extmodule if no extmodule with the same defname+    // was found.+    if (!collidingExtModule)+      continue;

would it make sense to pull this into the 'if' above? Something like: if (!value) { value = extModule; continue }. This eliminates the "else" by making it be the fall through.

Just my personal style, but I wouldn't worry about adding the extra scope around the "value" reference. Once you do that, you can move the definition of collidingExtModule lower.

seldridge

comment created time in 6 days

PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commentllvm/circt

[FIRRTL] Disallow Recursive Instantiation

 static ParseResult parseFExtModuleOp(OpAsmParser &parser,   return parseFModuleOp(parser, result, /*isExtModule:*/ true); } -static LogicalResult verifyFModuleOp(FModuleOp module) {+static LogicalResult verifyFModuleOp(FModuleOp &module) {   // The parent op must be a circuit op.-  auto *parentOp = module.getParentOp();-  if (!parentOp || !isa<CircuitOp>(parentOp)) {+  auto parentOp = dyn_cast<CircuitOp>(module.getParentOp());+  if (!parentOp) {     module.emitOpError("should be embedded into a firrtl.circuit");     return failure();   } +  // Disallow recursive instantiation.+  for (auto &op : *module.getBodyBlock()) {+    auto inst = dyn_cast<InstanceOp>(op);

Instead of doing the scan in the verification of the FModuleOp, would it make sense to do this check in the InstanceOp itself? The check there would just be "check to see if the module being referenced is the same as the module I'm in".

seldridge

comment created time in 6 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[FIRRTL] Add ExtModule Verification

 static LogicalResult verifyCircuitOp(CircuitOp &circuit) {     return failure();   } +  // Check that no extmodule defname conflicts with another module+  // name in the circuit.+  for (auto &op : *circuit.getBody()) {+    if (auto extModule = dyn_cast<FExtModuleOp>(op)) {+      if (auto defname = extModule.defnameAttr()) {+        auto collidingModule = circuit.lookupSymbol(defname.getValue());+        if (collidingModule && isa<FModuleOp>(collidingModule)) {+          op.emitOpError(+                "attribute 'defname' with value '" + defname.getValue() ++                "' conflicts with the name of another module in the circuit")+                  .attachNote(collidingModule->getLoc())+              << "previous module declared here";

nice!

seldridge

comment created time in 7 days

Pull request review commentllvm/circt

[FIRRTL] Add ExtModule Verification

 static LogicalResult verifyCircuitOp(CircuitOp &circuit) {     return failure();   } +  // Store a mapping of defname strings to the first external module+  // that defines it.+  llvm::StringMap<FExtModuleOp> defnameMap;

Thank you for using efficient algorithms, however you can do one better here: instead of indexing with the string itself, you can use an llvm::DenseMap<Attribute, FExtModuleOp>. mlir::Attribute is a uniqued thing a pointer in size, so this will be very efficient.

seldridge

comment created time in 7 days

Pull request review commentllvm/circt

[FIRRTL] Add ExtModule Verification

 static LogicalResult verifyCircuitOp(CircuitOp &circuit) {     return failure();   } +  // Store a mapping of defname strings to the first external module+  // that defines it.+  llvm::StringMap<FExtModuleOp> defnameMap;++  // Verify external modules.+  for (auto &op : *circuit.getBody()) {+    if (auto extModule = dyn_cast<FExtModuleOp>(op)) {+      if (auto defname = extModule.defnameAttr()) {+        // Check that this extmodule's defname does not conflict with+        // the symbol name of any module.+        auto collidingModule = circuit.lookupSymbol(defname.getValue());+        if (isa_and_nonnull<FModuleOp>(collidingModule)) {+          auto diag =+              op.emitOpError()+              << "attribute 'defname' with value " << defname+              << " conflicts with the name of another module in the circuit";+          diag.attachNote(collidingModule->getLoc())+              << "previous module declared here";+          return failure();+        }++        // Find an optional extmodule with a defname collision. Update+        // the defnameMap if this is the first extmodule with that+        // defname or if the current extmodule takes no parameters and+        // the collision does. The latter condition improves later+        // extmodule verification as checking against a parameterless+        // module is stricter.+        auto collidingExtModuleOpt =+            Optional<FExtModuleOp>(llvm::NoneType::None);+        {+          auto value = defnameMap.find(defname.getValue());+          if (value != defnameMap.end()) {+            collidingExtModuleOpt = Optional<FExtModuleOp>(value->getValue());+            if (value->getValue().parameters() && !extModule.parameters())+              defnameMap.insert_or_assign(defname.getValue(), extModule);+          } else {+            defnameMap.insert_or_assign(defname.getValue(), extModule);+          }+        }++        // Go to next extmodule if no extmodule with the same defname+        // was found.+        if (!collidingExtModuleOpt)+          continue;++        auto collidingExtModule = collidingExtModuleOpt.getValue();++        // Check that the number of ports is exactly the same.+        auto ports = new SmallVector<std::pair<StringAttr, FIRRTLType>, 8>;+        auto collidingPorts =+            new SmallVector<std::pair<StringAttr, FIRRTLType>, 8>;+        extModule.getPortInfo(*ports);+        collidingExtModule.getPortInfo(*collidingPorts);+        if (ports->size() != collidingPorts->size()) {+          auto diag = op.emitOpError()+                      << "with 'defname' attribute " << defname << " has "+                      << ports->size()+                      << " ports which is different from a previously defined "+                         "extmodule with the same 'defname' which has "+                      << collidingPorts->size() << " ports";+          diag.attachNote(collidingExtModule.getLoc())+              << "previous extmodule definition occurred here";+          return failure();+        }++        // Check that ports match for name and type. Since parameters+        // *might* affect widths, ignore widths if either module has+        // parameters. Note that this allows for misdetections, but+        // has zero false positives.+        for (auto p : llvm::zip(*ports, *collidingPorts)) {+          auto a_name = std::get<0>(std::get<0>(p));+          auto b_name = std::get<0>(std::get<1>(p));+          FIRRTLType a_type, b_type;+          if (extModule.parameters() || collidingExtModule.parameters()) {+            a_type = std::get<1>(std::get<0>(p)).getWidthlessType();+            b_type = std::get<1>(std::get<1>(p)).getWidthlessType();+          } else {+            a_type = std::get<1>(std::get<0>(p));+            b_type = std::get<1>(std::get<1>(p));+          }

Random suggestion to reduce the redundancy, but I'd structure this like this:

  auto a_type = std::get<1>(std::get<0>(p));
  auto b_type = ...
  if (stuff) {
    a_type = a_type.getWidthlessType();
    b_type also
  }
seldridge

comment created time in 7 days

Pull request review commentllvm/circt

[FIRRTL] Add ExtModule Verification

 static LogicalResult verifyCircuitOp(CircuitOp &circuit) {     return failure();   } +  // Store a mapping of defname strings to the first external module+  // that defines it.+  llvm::StringMap<FExtModuleOp> defnameMap;++  // Verify external modules.+  for (auto &op : *circuit.getBody()) {+    if (auto extModule = dyn_cast<FExtModuleOp>(op)) {+      if (auto defname = extModule.defnameAttr()) {+        // Check that this extmodule's defname does not conflict with+        // the symbol name of any module.+        auto collidingModule = circuit.lookupSymbol(defname.getValue());+        if (isa_and_nonnull<FModuleOp>(collidingModule)) {+          auto diag =+              op.emitOpError()+              << "attribute 'defname' with value " << defname+              << " conflicts with the name of another module in the circuit";+          diag.attachNote(collidingModule->getLoc())+              << "previous module declared here";+          return failure();+        }++        // Find an optional extmodule with a defname collision. Update+        // the defnameMap if this is the first extmodule with that+        // defname or if the current extmodule takes no parameters and+        // the collision does. The latter condition improves later+        // extmodule verification as checking against a parameterless+        // module is stricter.+        auto collidingExtModuleOpt =+            Optional<FExtModuleOp>(llvm::NoneType::None);+        {+          auto value = defnameMap.find(defname.getValue());+          if (value != defnameMap.end()) {+            collidingExtModuleOpt = Optional<FExtModuleOp>(value->getValue());+            if (value->getValue().parameters() && !extModule.parameters())+              defnameMap.insert_or_assign(defname.getValue(), extModule);+          } else {+            defnameMap.insert_or_assign(defname.getValue(), extModule);+          }+        }++        // Go to next extmodule if no extmodule with the same defname+        // was found.+        if (!collidingExtModuleOpt)+          continue;++        auto collidingExtModule = collidingExtModuleOpt.getValue();++        // Check that the number of ports is exactly the same.+        auto ports = new SmallVector<std::pair<StringAttr, FIRRTLType>, 8>;+        auto collidingPorts =+            new SmallVector<std::pair<StringAttr, FIRRTLType>, 8>;

You generally don't want to heap allocate SmallVectors, and this is leaking them because they don't get freed. Instead, declare them like this:

SmallVector<std::pair<StringAttr, FIRRTLType>, 8> ports;

and then drop the stars down below

seldridge

comment created time in 7 days

Pull request review commentllvm/circt

[FIRRTL] Add ExtModule Verification

 static LogicalResult verifyCircuitOp(CircuitOp &circuit) {     return failure();   } +  // Store a mapping of defname strings to the first external module+  // that defines it.+  llvm::StringMap<FExtModuleOp> defnameMap;++  // Verify external modules.+  for (auto &op : *circuit.getBody()) {+    if (auto extModule = dyn_cast<FExtModuleOp>(op)) {+      if (auto defname = extModule.defnameAttr()) {+        // Check that this extmodule's defname does not conflict with+        // the symbol name of any module.+        auto collidingModule = circuit.lookupSymbol(defname.getValue());+        if (isa_and_nonnull<FModuleOp>(collidingModule)) {+          auto diag =+              op.emitOpError()+              << "attribute 'defname' with value " << defname+              << " conflicts with the name of another module in the circuit";+          diag.attachNote(collidingModule->getLoc())+              << "previous module declared here";+          return failure();+        }++        // Find an optional extmodule with a defname collision. Update+        // the defnameMap if this is the first extmodule with that+        // defname or if the current extmodule takes no parameters and+        // the collision does. The latter condition improves later+        // extmodule verification as checking against a parameterless+        // module is stricter.+        auto collidingExtModuleOpt =+            Optional<FExtModuleOp>(llvm::NoneType::None);

ops are nullable, so I'd just declare this as FExtModuleOp instead of using Optional explicitly.

seldridge

comment created time in 7 days

Pull request review commentllvm/circt

[FIRRTL] Add ExtModule Verification

 static LogicalResult verifyCircuitOp(CircuitOp &circuit) {     return failure();   } +  // Store a mapping of defname strings to the first external module+  // that defines it.+  llvm::StringMap<FExtModuleOp> defnameMap;++  // Verify external modules.+  for (auto &op : *circuit.getBody()) {+    if (auto extModule = dyn_cast<FExtModuleOp>(op)) {+      if (auto defname = extModule.defnameAttr()) {+        // Check that this extmodule's defname does not conflict with+        // the symbol name of any module.+        auto collidingModule = circuit.lookupSymbol(defname.getValue());+        if (isa_and_nonnull<FModuleOp>(collidingModule)) {+          auto diag =+              op.emitOpError()+              << "attribute 'defname' with value " << defname+              << " conflicts with the name of another module in the circuit";+          diag.attachNote(collidingModule->getLoc())+              << "previous module declared here";+          return failure();+        }++        // Find an optional extmodule with a defname collision. Update+        // the defnameMap if this is the first extmodule with that+        // defname or if the current extmodule takes no parameters and+        // the collision does. The latter condition improves later+        // extmodule verification as checking against a parameterless+        // module is stricter.+        auto collidingExtModuleOpt =+            Optional<FExtModuleOp>(llvm::NoneType::None);+        {+          auto value = defnameMap.find(defname.getValue());+          if (value != defnameMap.end()) {+            collidingExtModuleOpt = Optional<FExtModuleOp>(value->getValue());+            if (value->getValue().parameters() && !extModule.parameters())+              defnameMap.insert_or_assign(defname.getValue(), extModule);+          } else {+            defnameMap.insert_or_assign(defname.getValue(), extModule);+          }+        }

Again, because of nullability, you can simplify this a bit. I'd recommend a pattern like:

auto &entry = defnameMap[defname.getValue()];
if ("set or overwrite")
   entry = extModule;
seldridge

comment created time in 7 days

Pull request review commentllvm/circt

[FIRRTL] Add ExtModule Verification

 static LogicalResult verifyCircuitOp(CircuitOp &circuit) {     return failure();   } +  // Store a mapping of defname strings to the first external module+  // that defines it.+  llvm::StringMap<FExtModuleOp> defnameMap;++  // Verify external modules.+  for (auto &op : *circuit.getBody()) {+    if (auto extModule = dyn_cast<FExtModuleOp>(op)) {+      if (auto defname = extModule.defnameAttr()) {

I'd recommend utilizing continue a bit more here to reduce nesting, e.g.:

    auto extModule = dyn_cast<FExtModuleOp>(op);
    if (!extModule) continue;
seldridge

comment created time in 7 days

PullRequestReviewEvent
PullRequestReviewEvent

push eventllvm/circt

George Lyon

commit sha 5d9e944cabed295679c4a1b20706f1cd033495cd

Make checking for 'end' more robust (#176) ; CHECK-NEXT: end is unfortunately quite fragile, as I noticed when trying to run the tests after compiling with some absolute paths that had the trigram "end" in them. The comment after the preceding lines prints the original source location, and the simple ; CHECK-NEXT: end would fail because it would locate the match in the comment.

view details

push time in 7 days

PR merged llvm/circt

Make checking for 'end' in tests more robust

; CHECK-NEXT: end is unfortunately quite fragile, as I noticed when trying to run the tests after compiling with some absolute paths that had the trigram "end" in them. The comment after the preceding lines prints the original source location, and the simple ; CHECK-NEXT: end would fail because it would locate the match in the comment.

My absolute-path setup is a temporary hack, but this issue would also arise if the relative path happened to have the trigram "end" in it.

+4 -4

1 comment

2 changed files

GeorgeLyon

pr closed time in 7 days

pull request commentllvm/circt

Make checking for 'end' in tests more robust

Nice catch, thank you!

GeorgeLyon

comment created time in 7 days

issue commentllvm/circt

[RTL] Improve the asm syntax of RTLModuleOp

Yeah, great minds think alike, it would be very nice to improve here.

teqdruid

comment created time in 8 days

pull request commentllvm/circt

[RTL] RTL module results as outputs

woo hoo! :-)

teqdruid

comment created time in 8 days

Pull request review commentllvm/circt

[RTL] RTL module results as outputs

 #include "mlir/IR/OpImplementation.h" #include "mlir/IR/RegionKindInterface.h" #include "mlir/IR/SymbolTable.h"+#include "mlir/Interfaces/ControlFlowInterfaces.h" #include "mlir/Interfaces/SideEffectInterfaces.h"  namespace circt { namespace rtl { -// This holds the name, type, direction of a module's ports+/// A RTL module ports direction.+enum PortDirection {+  INPUT = 1,+  OUTPUT = 2,+  INOUT = 3,+};++/// This holds the name, type, direction of a module's ports struct RTLModulePortInfo {   StringAttr name;+  PortDirection direction;   Type type;-  StringAttr direction;+  size_t argNum; // Either the argument index or the result index depending on

I'd keep the default ctor (it is used when filling it in element wise) but just init it with something that is obvious in a debugger if it gets forgotten.

teqdruid

comment created time in 8 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[RTL] RTL module results as outputs

 void ModuleEmitter::emitStatement(rtl::ConnectOp op) {   emitLocationInfoAndNewLine(ops); } +void ModuleEmitter::emitStatement(rtl::OutputOp op) {

I mean in the c++ code.

teqdruid

comment created time in 8 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[RTL] More Module cases

 void ModuleEmitter::emitStatement(rtl::RTLInstanceOp op) {    auto opArgs = op.inputs(); -  auto moduleIR = op.getParentOfType<firrtl::CircuitOp>();-  auto referencedModule =-      cast<rtl::RTLModuleOp>(moduleIR.lookupSymbol(defName));-  assert(referencedModule && "invalid rtl.instance op");+  SmallVector<rtl::RTLModulePortInfo, 8> portInfo;+  if (auto moduleIR = op.getParentOfType<firrtl::CircuitOp>()) {+    auto moduleOp = moduleIR.lookupSymbol(defName);+    if (auto m = cast<rtl::RTLModuleOp>(moduleOp)) {+      m.getRTLPortInfo(portInfo);+    } else if (auto m = cast<rtl::RTLExternModuleOp>(moduleOp)) {+      m.getRTLPortInfo(portInfo);+    } else {+      assert(0 && "invalid rtl.instance op");+    }+  } else if (auto moduleIR = op.getParentOfType<mlir::ModuleOp>()) {

Oh wait, I got it - this looks right. rtl.module shouldn't be required to be in a firrtl.circuit. In fact, maybe they shouldn't be allowed to be. :-)

darthscsi

comment created time in 8 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[RTL] More Module cases

 void ModuleEmitter::emitStatement(rtl::RTLInstanceOp op) {    auto opArgs = op.inputs(); -  auto moduleIR = op.getParentOfType<firrtl::CircuitOp>();-  auto referencedModule =-      cast<rtl::RTLModuleOp>(moduleIR.lookupSymbol(defName));-  assert(referencedModule && "invalid rtl.instance op");+  SmallVector<rtl::RTLModulePortInfo, 8> portInfo;+  if (auto moduleIR = op.getParentOfType<firrtl::CircuitOp>()) {+    auto moduleOp = moduleIR.lookupSymbol(defName);+    if (auto m = cast<rtl::RTLModuleOp>(moduleOp)) {+      m.getRTLPortInfo(portInfo);+    } else if (auto m = cast<rtl::RTLExternModuleOp>(moduleOp)) {+      m.getRTLPortInfo(portInfo);+    } else {+      assert(0 && "invalid rtl.instance op");+    }+  } else if (auto moduleIR = op.getParentOfType<mlir::ModuleOp>()) {

Actually, I don't think we want to support free standing modules. All firrtl.module declaration should be in a circuit - we should add verifier check for that.

darthscsi

comment created time in 8 days

PullRequestReviewEvent
PullRequestReviewEvent

pull request commentllvm/circt

[README] Add link to the dialect diagram

I'd recommend checking in the diagram source and a rendered version of it both into circt/docs. Even better if there is a shell script that regenerates the output in circt/utils :-)

fabianschuiki

comment created time in 8 days

Pull request review commentllvm/circt

[RTL] RTL module results as outputs

 void ModuleEmitter::emitStatement(rtl::ConnectOp op) {   emitLocationInfoAndNewLine(ops); } +void ModuleEmitter::emitStatement(rtl::OutputOp op) {+  SmallPtrSet<Operation *, 8> ops;+  ops.insert(op);++  SmallVector<rtl::RTLModulePortInfo, 8> ports;+  rtl::RTLModuleOp parent = op.getParentOfType<rtl::RTLModuleOp>();+  parent.getRTLPortInfo(ports);+  size_t operandIndex = 0;+  for (rtl::RTLModulePortInfo port : ports) {+    if (port.direction != rtl::PortDirection::OUTPUT)+      continue;+    indent() << "assign " << port.name.getValue() << " = ";+    emitExpression(op.getOperand(operandIndex), ops);+    os << ';';+    emitLocationInfoAndNewLine(ops);+    ++operandIndex;+  }

Very nice.

teqdruid

comment created time in 8 days

Pull request review commentllvm/circt

[RTL] RTL module results as outputs

 void ModuleEmitter::emitStatement(rtl::ConnectOp op) {   emitLocationInfoAndNewLine(ops); } +void ModuleEmitter::emitStatement(rtl::OutputOp op) {

Plz add a comment saying something like "for output op we emit the assign statements at the end of the module that set all the output wires"

teqdruid

comment created time in 8 days

Pull request review commentllvm/circt

[RTL] RTL module results as outputs

 StringAttr rtl::getRTLNameAttr(ArrayRef<NamedAttribute> attrs) {   return StringAttr(); } -StringAttr rtl::getRTLDirectionAttr(ArrayRef<NamedAttribute> attrs) {+static bool isRTLInoutArg(ArrayRef<NamedAttribute> attrs) {

nit, I'd name this containsRTLInOutAttr or containsInOutAttr

teqdruid

comment created time in 8 days

Pull request review commentllvm/circt

[RTL] RTL module results as outputs

 void ModuleEmitter::emitRTLModule(rtl::RTLModuleOp module) {   SmallVector<rtl::RTLModulePortInfo, 8> portInfo;   module.getRTLPortInfo(portInfo); -  size_t nextPort = 0;-  for (auto &port : portInfo)-    addName(module.getArgument(nextPort++), port.name);+  for (auto &port : portInfo) {+    StringRef name = port.name.getValue();+    if (name.empty()) {+      llvm::errs() << "Found port without a name. Port names are required for "

This should emitOpError instead of printing to errs. Please add a testcase too :)

teqdruid

comment created time in 8 days

Pull request review commentllvm/circt

[RTL] RTL module results as outputs

-// RUN: circt-opt -pass-pipeline='lower-firrtl-to-rtl-module' %s -verify-diagnostics  | FileCheck %s+// RUN: true

Thx, I'll fix this up after you land this patch.

teqdruid

comment created time in 8 days

Pull request review commentllvm/circt

[RTL] RTL module results as outputs

 void ModuleEmitter::collectNamesEmitDecls(Block &block) {     }   } -  if (!declsToEmit.empty())+  if (!declsToEmit.empty() || rtlInstanceDeclaredWires)     os << '\n'; } +bool ModuleEmitter::collectNamesEmitWires(rtl::RTLInstanceOp &op) {+  for (size_t i = 0, e = op.getNumResults(); i < e; ++i) {+    auto result = op.getResult(i);+    StringRef wireName = addName(result, op.getResultName(i));++    Type resultType = result.getType();+    if (auto intType = resultType.dyn_cast<IntegerType>()) {+      if (intType.getWidth() == 1) {+        indent() << "wire " << wireName << ";\n";+      } else {+        indent() << "wire [" << intType.getWidth() - 1 << ":0] " << wireName+                 << ";\n";+      }+    } else {+      indent() << "// Type '" << resultType

This should emitOpError as well so the client of the compiler knows the pass failed and the output had a problem.

teqdruid

comment created time in 8 days

Pull request review commentllvm/circt

[RTL] RTL module results as outputs

 def RTLInstanceOp : RTLOp<"instance", []> {   let arguments = (ins StrAttr:$instanceName,                        FlatSymbolRefAttr:$moduleName,                        Variadic<AnyType>:$inputs);-  let results = (outs);+  let results = (outs Variadic<AnyType>);++  let extraClassDeclaration = [{   +    StringAttr getResultName(size_t i);+  }];      let assemblyFormat = [{-     $instanceName $moduleName `(` $inputs `)` attr-dict `:` type($inputs)+    $instanceName $moduleName `(` $inputs `)` custom<ResultNames>( attr-dict ) `:` functional-type($inputs, results)

Plz fit to 80 columns.

teqdruid

comment created time in 8 days

Pull request review commentllvm/circt

[RTL] RTL module results as outputs

 #include "mlir/IR/OpImplementation.h" #include "mlir/IR/RegionKindInterface.h" #include "mlir/IR/SymbolTable.h"+#include "mlir/Interfaces/ControlFlowInterfaces.h" #include "mlir/Interfaces/SideEffectInterfaces.h"  namespace circt { namespace rtl { -// This holds the name, type, direction of a module's ports+/// A RTL module ports direction.+enum PortDirection {+  INPUT = 1,+  OUTPUT = 2,+  INOUT = 3,+};++/// This holds the name, type, direction of a module's ports struct RTLModulePortInfo {   StringAttr name;+  PortDirection direction;   Type type;-  StringAttr direction;+  size_t argNum; // Either the argument index or the result index depending on

Does it work to declare this as size_t argNum = ~0U; to reduce the chance of an uninitialized variable when working with RTLModulePortInfo?

teqdruid

comment created time in 8 days

Pull request review commentllvm/circt

[RTL] RTL module results as outputs

 static LogicalResult verifyRTLInstanceOp(RTLInstanceOp op) {   auto referencedModule =       mlir::SymbolTable::lookupSymbolIn(moduleIR, op.moduleName());   if (referencedModule == nullptr) {-    op.emitError(-        llvm::formatv("Cannot find module definition '{0}'", op.moduleName()));+    op.emitError("Cannot find module definition '") << op.moduleName() << "'.";     return failure();   }   if (!isa<rtl::RTLModuleOp>(referencedModule) &&       !isa<rtl::RTLExternModuleOp>(referencedModule)) {-    op.emitError(-        llvm::formatv("Symbol resolved to '{0}', not a RTL[Ext]ModuleOp",-                      referencedModule->getName()));+    op.emitError("Symbol resolved to '")+        << referencedModule->getName() << "' which is not a RTL[Ext]ModuleOp.";     return failure();   }   return success(); } +StringAttr RTLInstanceOp::getResultName(size_t idx) {+  if (auto nameAttrList = getAttrOfType<ArrayAttr>("name"))+    if (idx < nameAttrList.size())+      return nameAttrList[idx].dyn_cast<StringAttr>();+  return StringAttr();+}++/// Intercept the `attr-dict` parsing to inject the result names which _may_ be+/// missing.+ParseResult parseResultNames(OpAsmParser &p, NamedAttrList &attrDict) {+  MLIRContext *ctxt = p.getBuilder().getContext();+  if (p.parseOptionalAttrDict(attrDict))+    return failure();++  // Assemble the result names from the asm.+  SmallVector<Attribute, 8> names;+  for (size_t i = 0, e = p.getNumResults(); i < e; ++i) {+    StringAttr resultName = StringAttr::get(p.getResultName(i).first, ctxt);+    names.push_back(resultName);

low prio suggestion, I'd inline the call to ::get into the push_back.

teqdruid

comment created time in 8 days

PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commentllvm/circt

[RTL] RTL module results as outputs

 void ModuleEmitter::emitRTLModule(rtl::RTLModuleOp module) {   SmallVector<rtl::RTLModulePortInfo, 8> portInfo;   module.getRTLPortInfo(portInfo); -  size_t nextPort = 0;-  for (auto &port : portInfo)-    addName(module.getArgument(nextPort++), port.name);+  for (auto &port : portInfo) {+    StringRef name = port.name.getValue();+    if (name.empty()) {+      llvm::errs() << "Found port without a name. Port names are required for "+                      "Verilog synthesis.\n";+      name = "<<NO-NAME-FOUND>>";+    }+    if (port.direction == rtl::PortDirection::OUTPUT)+      usedNames.insert(name);

Yep!

teqdruid

comment created time in 8 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[RTL] Add canonicalization pass for shift left.

 void MulOp::getCanonicalizationPatterns(OwningRewritePatternList &results,        APInt value, value2; +      // mul(x, c) -> mul( shl(x, log2(c)) ), where c is a power of two.+      if (size == 2 && matchPattern(inputs.back(), m_RConstant(value)) &&+          value.isPowerOf2()) {+        auto shift =+            rewriter.create<ConstantOp>(op.getLoc(), value.exactLogBase2(),+                                        op.getType().cast<IntegerType>());+        auto shlOp = rewriter.create<rtl::ShlOp>(op.getLoc(), inputs[0], shift);++        std::array<Value, 1> newOperands = {shlOp};+        rewriter.replaceOpWithNewOp<MulOp>(op, op.getType(), newOperands);

In this case, I'd recommend seeing if you can pass this as ArrayRef(shlOp) instead of the intermediate std::array.

cgyurgyik

comment created time in 8 days

PullRequestReviewEvent
PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commentllvm/circt

[RTL] Add canonicalization pass for shift left.

 void AddOp::getCanonicalizationPatterns(OwningRewritePatternList &results,       }        auto mulOp = inputs[size - 1].getDefiningOp<rtl::MulOp>();-      APInt multiplier;-       // add(..., x, mul(x, c)) -> add(..., mul(x, c + 1))       if (mulOp && mulOp.inputs().size() == 2 &&           mulOp.inputs()[0] == inputs[size - 2] &&-          matchPattern(mulOp.inputs()[1], m_RConstant(multiplier))) {--        APInt one(/*numBits=*/multiplier.getBitWidth(), 1, /*isSigned=*/false);+          matchPattern(mulOp.inputs()[1], m_RConstant(value))) { -        auto rhs = rewriter.create<ConstantOp>(op.getLoc(), multiplier + one);+        APInt one(/*numBits=*/value.getBitWidth(), 1, /*isSigned=*/false);+        auto rhs = rewriter.create<ConstantOp>(op.getLoc(), value + one);         std::array<Value, 2> factors = {mulOp.inputs()[0], rhs};

std::array is good, thanks! You can also use a C array and wrap with an ArrayRef(factors) when you pass it, but std::array is better in this case. Thanks!

cgyurgyik

comment created time in 8 days

Pull request review commentllvm/circt

[RTL] Add canonicalization pass for shift left.

 void AddOp::getCanonicalizationPatterns(OwningRewritePatternList &results,         return success();       } +      // add(..., x, x) -> add(..., shl(x, 1))+      if (inputs[size - 1] == inputs[size - 2]) {+        SmallVector<Value, 4> newOperands(inputs.drop_back(/*n=*/2));++        auto one = rewriter.create<ConstantOp>(+            op.getLoc(), 1, op.getType().cast<IntegerType>());+        auto shiftLeftOp =+            rewriter.create<rtl::ShlOp>(op.getLoc(), inputs.back(), one);++        newOperands.push_back(shiftLeftOp);+        rewriter.replaceOpWithNewOp<AddOp>(op, op.getType(), newOperands);+        return success();+      }++      auto shlOp = inputs[size - 1].getDefiningOp<rtl::ShlOp>();+      APInt shift;++      // add(..., x, shl(x, c)) -> add(..., mul(x, c * 2 + 1))+      if (shlOp && shlOp.lhs() == inputs[size - 2] &&+          matchPattern(shlOp.rhs(), m_RConstant(shift))) {++        auto numBits =+            shlOp.getOperand(1).getType().cast<IntegerType>().getWidth();+        APInt one(numBits, 1, /*isSigned=*/false);+        APInt two(numBits, 2, /*isSigned=*/false);++        auto multiplier = rewriter.create<ConstantOp>(+            op.getLoc(), (shift * two + one).getLimitedValue(),+            op.getType().cast<IntegerType>());++        SmallVector<Value, 2> mulInputs;+        mulInputs.push_back(shlOp.lhs());+        mulInputs.push_back(multiplier);+        auto mulOp = rewriter.create<rtl::MulOp>(op.getLoc(), mulInputs);++        SmallVector<Value, 4> newOperands(inputs.drop_back(/*n=*/2));+        newOperands.push_back(mulOp);+        rewriter.replaceOpWithNewOp<AddOp>(op, op.getType(), newOperands);+        return success();+      }++      auto mulOp = inputs[size - 1].getDefiningOp<rtl::MulOp>();+      APInt multiplier;++      // add(..., x, mul(x, c) -> add(..., shl(x, s)), where (c + 1) is a

sounds great!

cgyurgyik

comment created time in 8 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[RTL] Add canonicalization pass for shift left.

 void MulOp::getCanonicalizationPatterns(OwningRewritePatternList &results,        APInt value, value2; +      // mul(x, c) -> mul(shl(x, n)), where c is a power of two, & n = log2(c)+      if (size == 2 && matchPattern(inputs.back(), m_RConstant(value)) &&+          value.isPowerOf2()) {+        auto shift =+            rewriter.create<ConstantOp>(op.getLoc(), value.exactLogBase2(),

Ok oh, I thought exactLogBase2 returned an APInt. Plz disregard!

cgyurgyik

comment created time in 8 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[RTL] Add canonicalization pass for shift left.

 void AddOp::getCanonicalizationPatterns(OwningRewritePatternList &results,         return success();       } +      // add(..., x, x) -> add(..., mul(x, 2))+      if (inputs[size - 1] == inputs[size - 2]) {+        SmallVector<Value, 2> mulInputs;++        auto two = rewriter.create<ConstantOp>(+            op.getLoc(), 2, op.getType().cast<IntegerType>());+        mulInputs.push_back(inputs.back());+        mulInputs.push_back(two);++        auto mulOp = rewriter.create<rtl::MulOp>(op.getLoc(), mulInputs);

Close, but the third should be: add(x, mul(x, s)) -> mul(x, s + 1). The case when s+1 is a power of two can be handled by recurisve simplifications. We want to handle things like add(x, mul(x, 13)) -> mul(x, 14).

cgyurgyik

comment created time in 8 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[FIRRTL] Add basic defname verification

 static LogicalResult verifyCircuitOp(CircuitOp &circuit) {     return failure();   } +  // Check that no extmodule defname conflicts with another module+  // name in the circuit.+  for (auto &op : *circuit.getBody()) {+    if (auto extModule = dyn_cast<FExtModuleOp>(op)) {+      if (auto defname = extModule.defnameAttr()) {+        auto collidingModule = circuit.lookupSymbol(defname.getValue());+        if (collidingModule && isa<FModuleOp>(collidingModule)) {+          op.emitOpError(+                "attribute 'defname' with value '" + defname.getValue() ++                "' conflicts with the name of another module in the circuit")+                  .attachNote(collidingModule->getLoc())+              << "previous module declared here";

I'm happy for you to make the call here - it is ok to land this as an incremental step, or hold it to iterate on. Whatever you prefer

seldridge

comment created time in 8 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[RTL] RTL module results as outputs

 class ModuleEmitter : public VerilogEmitterBase {   void emitOperation(Operation *op);    void collectNamesEmitDecls(Block &block);-  void addName(Value value, StringRef name);-  void addName(Value value, StringAttr nameAttr) {-    addName(value, nameAttr ? nameAttr.getValue() : "");+  // Use as key in 'nameTable'. This is an xor field either `Value` can be valid+  // or `size_t`. If the former, it's any value in the module. If the former, we+  // need to return the signal name for the result index specified by this+  // number.+  using ValueOrResult = std::tuple<Value, size_t>;

i'd recommend that you do refactor the conflict renaming stuff out of ModuleEmitter::addName(Value value, StringRef name) into a helper function that takes a StringRef for the name and then returns a StringRef for the potentially renamified name (pointing into the usedNames set, so it doesn't dangle). This can be done as a standalone patch (I can do this if it is helpful).

Once you have that, you can then have addName use it, and you can implement a getOutputName(idx, nameStrIfpresent) which uses the same helper routine to avoid generating conflicting names.

teqdruid

comment created time in 8 days

PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commentllvm/circt

[FIRRTL] Add basic defname verification

 firrtl.circuit "Foo" {     %a = firrtl.reginit %clk, %reset, %zero {name = "a"} : (!firrtl.clock, !firrtl.uint<1>, !firrtl.uint<1>) -> !firrtl.flip<uint<1>>   } }++// -----++firrtl.circuit "Foo" {++  // expected-error @+2 {{'firrtl.extmodule' op attribute 'defname' with value 'Bar' conflicts with the name of another module in the circuit}}+  // expected-note @+2 {{previous module declared here}}

Sure nit but I'd move the note line to being just above the @Bar line and use @+1 instead of +2.

seldridge

comment created time in 8 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[FIRRTL] Add basic defname verification

 static LogicalResult verifyCircuitOp(CircuitOp &circuit) {     return failure();   } +  // Check that no extmodule defname conflicts with another module+  // name in the circuit.+  for (auto &op : *circuit.getBody()) {+    if (auto extModule = dyn_cast<FExtModuleOp>(op)) {+      if (auto defname = extModule.defnameAttr()) {+        auto collidingModule = circuit.lookupSymbol(defname.getValue());+        if (collidingModule && isa<FModuleOp>(collidingModule)) {+          op.emitOpError(+                "attribute 'defname' with value '" + defname.getValue() ++                "' conflicts with the name of another module in the circuit")+                  .attachNote(collidingModule->getLoc())+              << "previous module declared here";

It's late, so I forget the semantics here, but is this ok?

extmodule @foo defname = "xyz" ...
extmodule @bar  defname = "xyz" ...

or is that a conflict? If that is a conflict, then I think you should keep the symbol names in a map that is populated during a scan. Dump both the module names into the set as well as the defname names.

seldridge

comment created time in 8 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[RTL] RTL module results as outputs

 class ModuleEmitter : public VerilogEmitterBase {   void emitOperation(Operation *op);    void collectNamesEmitDecls(Block &block);-  void addName(Value value, StringRef name);-  void addName(Value value, StringAttr nameAttr) {-    addName(value, nameAttr ? nameAttr.getValue() : "");+  // Use as key in 'nameTable'. This is an xor field either `Value` can be valid+  // or `size_t`. If the former, it's any value in the module. If the former, we+  // need to return the signal name for the result index specified by this+  // number.+  using ValueOrResult = std::tuple<Value, size_t>;

Yes, I'm suggesting that you change ModuleEmitter::emitStatement(rtl::OutputOp op) to not call getResultName. Just push the complexity for this one thing into it, instead of making it core to the whole transformation.

teqdruid

comment created time in 8 days

PullRequestReviewEvent

Pull request review commentllvm/circt

[RTL] Add canonicalization pass for shift left.

 void AddOp::getCanonicalizationPatterns(OwningRewritePatternList &results,         return success();       } +      // add(..., x, x) -> add(..., mul(x, 2))+      if (inputs[size - 1] == inputs[size - 2]) {+        SmallVector<Value, 2> mulInputs;++        auto two = rewriter.create<ConstantOp>(+            op.getLoc(), 2, op.getType().cast<IntegerType>());+        mulInputs.push_back(inputs.back());+        mulInputs.push_back(two);++        auto mulOp = rewriter.create<rtl::MulOp>(op.getLoc(), mulInputs);

Ok, sorry for the bad advice, but this is one step too far. It is true that this will converge to the right solution (given that x*2 will turn into a shift) but there is no reason to ever intentionally generate non-canonical form. Please change this to generate x<<1 directly, thanks :-)

The reason to rely on recursive simplifications is if it provides a simplification to the compiler code itself.

cgyurgyik

comment created time in 9 days

Pull request review commentllvm/circt

[RTL] Add canonicalization pass for shift left.

 void AddOp::getCanonicalizationPatterns(OwningRewritePatternList &results,         return success();       } +      // add(..., x, x) -> add(..., shl(x, 1))+      if (inputs[size - 1] == inputs[size - 2]) {

Ok, fair enough, we can keep it to this forum.

cgyurgyik

comment created time in 9 days

Pull request review commentllvm/circt

[RTL] Add canonicalization pass for shift left.

 void MulOp::getCanonicalizationPatterns(OwningRewritePatternList &results,        APInt value, value2; +      // mul(x, c) -> mul(shl(x, n)), where c is a power of two, & n = log2(c)+      if (size == 2 && matchPattern(inputs.back(), m_RConstant(value)) &&+          value.isPowerOf2()) {+        auto shift =+            rewriter.create<ConstantOp>(op.getLoc(), value.exactLogBase2(),

Does it work if you drop passing the integer type explicitly? I think rtl::ConstantOp has a ctor that takes an APInt alone.

cgyurgyik

comment created time in 9 days

Pull request review commentllvm/circt

[RTL] Add canonicalization pass for shift left.

 void AddOp::getCanonicalizationPatterns(OwningRewritePatternList &results,         return success();       } +      // add(..., x, x) -> add(..., mul(x, 2))+      if (inputs[size - 1] == inputs[size - 2]) {+        SmallVector<Value, 2> mulInputs;++        auto two = rewriter.create<ConstantOp>(+            op.getLoc(), 2, op.getType().cast<IntegerType>());+        mulInputs.push_back(inputs.back());+        mulInputs.push_back(two);++        auto mulOp = rewriter.create<rtl::MulOp>(op.getLoc(), mulInputs);++        SmallVector<Value, 4> newOperands(inputs.drop_back(/*n=*/2));+        newOperands.push_back(mulOp);+        rewriter.replaceOpWithNewOp<AddOp>(op, op.getType(), newOperands);+        return success();+      }++      auto mulOp = inputs[size - 1].getDefiningOp<rtl::MulOp>();+      APInt multiplier;++      // add(..., x, mul(x, c)) -> add(..., mul(x, c + 1))+      if (mulOp && mulOp.inputs().size() == 2 &&+          mulOp.inputs()[0] == inputs[size - 2] &&+          matchPattern(mulOp.inputs()[1], m_RConstant(multiplier))) {++        SmallVector<Value, 2> mulInputs(mulOp.inputs().drop_back());++        auto numBits =+            mulOp.getOperand(1).getType().cast<IntegerType>().getWidth();+        APInt one(numBits, 1, /*isSigned=*/false);++        auto rhs = rewriter.create<ConstantOp>(+            op.getLoc(), (multiplier + one).getLimitedValue(),

getLimitedValue() will truncate to 64-bits, which will be incorrect for (unlikely) very large shift amounts. Please use this instead, which is also simpler:

auto rhs = rewriter.create<ConstantOp>(op.getLoc(), multiplier + one);

cgyurgyik

comment created time in 9 days

Pull request review commentllvm/circt

[RTL] Add canonicalization pass for shift left.

 void AddOp::getCanonicalizationPatterns(OwningRewritePatternList &results,         return success();       } +      // add(..., x, x) -> add(..., mul(x, 2))+      if (inputs[size - 1] == inputs[size - 2]) {+        SmallVector<Value, 2> mulInputs;++        auto two = rewriter.create<ConstantOp>(+            op.getLoc(), 2, op.getType().cast<IntegerType>());+        mulInputs.push_back(inputs.back());+        mulInputs.push_back(two);++        auto mulOp = rewriter.create<rtl::MulOp>(op.getLoc(), mulInputs);++        SmallVector<Value, 4> newOperands(inputs.drop_back(/*n=*/2));+        newOperands.push_back(mulOp);+        rewriter.replaceOpWithNewOp<AddOp>(op, op.getType(), newOperands);+        return success();+      }++      auto mulOp = inputs[size - 1].getDefiningOp<rtl::MulOp>();+      APInt multiplier;++      // add(..., x, mul(x, c)) -> add(..., mul(x, c + 1))+      if (mulOp && mulOp.inputs().size() == 2 &&+          mulOp.inputs()[0] == inputs[size - 2] &&+          matchPattern(mulOp.inputs()[1], m_RConstant(multiplier))) {++        SmallVector<Value, 2> mulInputs(mulOp.inputs().drop_back());++        auto numBits =+            mulOp.getOperand(1).getType().cast<IntegerType>().getWidth();+        APInt one(numBits, 1, /*isSigned=*/false);

instead of getting the width from the type, I'd just get it from multiplier.getWidth() since you have it handy and they are guaranteed to be the same.

cgyurgyik

comment created time in 9 days

Pull request review commentllvm/circt

[RTL] Add canonicalization pass for shift left.

 void AddOp::getCanonicalizationPatterns(OwningRewritePatternList &results,         return success();       } +      // add(..., x, x) -> add(..., mul(x, 2))+      if (inputs[size - 1] == inputs[size - 2]) {+        SmallVector<Value, 2> mulInputs;++        auto two = rewriter.create<ConstantOp>(+            op.getLoc(), 2, op.getType().cast<IntegerType>());+        mulInputs.push_back(inputs.back());+        mulInputs.push_back(two);++        auto mulOp = rewriter.create<rtl::MulOp>(op.getLoc(), mulInputs);++        SmallVector<Value, 4> newOperands(inputs.drop_back(/*n=*/2));+        newOperands.push_back(mulOp);+        rewriter.replaceOpWithNewOp<AddOp>(op, op.getType(), newOperands);+        return success();+      }++      auto mulOp = inputs[size - 1].getDefiningOp<rtl::MulOp>();+      APInt multiplier;++      // add(..., x, mul(x, c)) -> add(..., mul(x, c + 1))+      if (mulOp && mulOp.inputs().size() == 2 &&+          mulOp.inputs()[0] == inputs[size - 2] &&+          matchPattern(mulOp.inputs()[1], m_RConstant(multiplier))) {++        SmallVector<Value, 2> mulInputs(mulOp.inputs().drop_back());

mulInputs is exactly 2 entries in size when it is completed. I'd sink this down below and just use a static array, ala:

Value mulInputs[2] = {mulOp.inputs()[0], rhs };
cgyurgyik

comment created time in 9 days

more