profile
viewpoint

google/iree 466

👻

River707/decomp-editor 3

Collection of tools for the Poke-Emerald disassembly project

River707/llvm-review-monitor 3

Browser Extension - Get notifications for LLVM code review

joker-eph/mlir 0

"Multi-Level Intermediate Representation" Compiler Infrastructure

River707/iree 0

👻

River707/llvm-project 0

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.

pull request commentllvm/circt

[RTL] Add 'and' idompotent case for canonicalization.

No, I don't think it will generally reassociate duplicates to the end, but we can make it reassociate duplicates to be next to each other. That would be a straightforward canonicalization to add to the generic code I think. @River707 do you have any concerns with that?

No concerns, seems fine.

cgyurgyik

comment created time in 14 days

push eventRiver707/decomp-editor

River

commit sha 5dc5363fa45d0332b5cbf65709dcf67cb467b19a

Simplify the handling of palette tags by always generating palettes This revision changes palette generation to always generate a palette for all pics, regardless of if an event object uses the "generate from image" option. This greatly simplifies the handling of palette tags, and also makes them easier to reason about in-game.

view details

push time in 22 days

push eventRiver707/decomp-editor

River

commit sha 5dc5363fa45d0332b5cbf65709dcf67cb467b19a

Simplify the handling of palette tags by always generating palettes This revision changes palette generation to always generate a palette for all pics, regardless of if an event object uses the "generate from image" option. This greatly simplifies the handling of palette tags, and also makes them easier to reason about in-game.

view details

River

commit sha 1fe2426cd54dd3bfd4811e4a0ed9dd118175439c

Add a new tool for editing adventure data

view details

push time in 22 days

push eventllvm/llvm-project

River Riddle

commit sha 431bb8b31825ca0e855a92f72a8a33cf7c6c30b6

[mlir][ODS] Use c++ types for integer attributes of fixed width when possible. Unsigned and Signless attributes use uintN_t and signed attributes use intN_t, where N is the fixed width. The 1-bit variants use bool. Differential Revision: https://reviews.llvm.org/D86739

view details

push time in a month

push eventllvm/llvm-project

River Riddle

commit sha 88c6e25e4f0630bd9204cb02787fcb67e097a43a

[mlir][OpFormatGen] Add support for specifiy "custom" directives. This revision adds support for custom directives to the declarative assembly format. This allows for users to use C++ for printing and parsing subsections of an otherwise declaratively specified format. The custom directive is structured as follows: ``` custom-directive ::= `custom` `<` UserDirective `>` `(` Params `)` ``` `user-directive` is used as a suffix when this directive is used during printing and parsing. When parsing, `parseUserDirective` will be invoked. When printing, `printUserDirective` will be invoked. The first parameter to these methods must be a reference to either the OpAsmParser, or OpAsmPrinter. The type of rest of the parameters is dependent on the `Params` specified in the assembly format. Differential Revision: https://reviews.llvm.org/D84719

view details

River Riddle

commit sha 24b88920fed750c864e30add6a0fefe58f1a9f54

[mlir][ODS] Add new SymbolNameAttr and add support for in assemblyFormat Symbol names are a special form of StringAttr that get treated specially in certain areas, such as formatting. This revision adds a special derived attr for them in ODS and adds support in the assemblyFormat for formatting them properly. Differential Revision: https://reviews.llvm.org/D86759

view details

River Riddle

commit sha eaeadce9bd11d50cecfdf9e97ac471acd38136ee

[mlir][OpFormatGen] Add initial support for regions in the custom op assembly format This adds some initial support for regions and does not support formatting the specific arguments of a region. For now this can be achieved by using a custom directive that formats the arguments and then parses the region. Differential Revision: https://reviews.llvm.org/D86760

view details

River Riddle

commit sha 2481846a303dc6b99d4eebe761e2e3ffccead448

[mlir][PDL] Move the formats for PatternOp and RewriteOp to the declarative form. This is possible now that the declarative assembly form supports regions. Differential Revision: https://reviews.llvm.org/D86830

view details

push time in a month

CommitCommentEvent

Pull request review commentllvm/circt

[SV] Add SV Dialect operations to represent interfaces

 static void printAlwaysAtPosEdgeOp(OpAsmPrinter &p, AlwaysAtPosEdgeOp op) { }  //===----------------------------------------------------------------------===//+// Structure operations+//===----------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// InterfaceOp++static ParseResult parseInterfaceOp(OpAsmParser &parser,+                                    OperationState &result) {+  StringAttr nameAttr;+  if (parser.parseSymbolName(nameAttr, ::mlir::SymbolTable::getSymbolAttrName(),+                             result.attributes))+    return failure();++  Region *body = result.addRegion();+  if (parser.parseRegion(*body, llvm::None, llvm::None))+    return failure();++  InterfaceOp::ensureTerminator(*body, parser.getBuilder(), result.location);++  return success();+}++static void printInterfaceOp(OpAsmPrinter &p, InterfaceOp op) {+  p << op.getOperationName() << ' ';+  p.printSymbolName(op.getName());+  p.printRegion(op.body(),+                /*printEntryBlockArgs=*/false,+                /*printBlockTerminators=*/false);+}++//===----------------------------------------------------------------------===//+// InterfaceSignalOp++static ParseResult parseInterfaceSignalOp(OpAsmParser &parser,+                                          OperationState &result) {+  StringAttr nameAttr;+  TypeAttr type;+  if (parser.parseSymbolName(nameAttr, ::mlir::SymbolTable::getSymbolAttrName(),+                             result.attributes) ||+      parser.parseColon() ||+      parser.parseAttribute(type, "type", result.attributes))+    return failure();++  return success();+}++static void printInterfaceSignalOp(OpAsmPrinter &p, InterfaceSignalOp op) {+  p << op.getOperationName() << ' ';+  p.printSymbolName(op.getName());+  p << " : ";+  p.printAttribute(op.getAttr("type"));+}++//===----------------------------------------------------------------------===//+// InterfaceModPortOp++static ParseResult parseInterfaceModPortOp(OpAsmParser &parser,+                                           OperationState &result) {+  StringAttr nameAttr;+  if (parser.parseSymbolName(nameAttr, ::mlir::SymbolTable::getSymbolAttrName(),+                             result.attributes) ||+      parser.parseLParen())+    return failure();++  auto context = result.getContext();+  SmallVector<Attribute, 8> ports;+  while (true) {+    NamedAttrList tmpAttrs;+    StringAttr directionAttr;+    StringAttr signalAttr;+    if (parser.parseSymbolName(signalAttr, "signal", tmpAttrs) ||+        parser.parseColon() ||+        parser.parseAttribute(directionAttr, "direction", tmpAttrs))+      break;++    auto directionEnum =+        symbolizeModPortDirectionAttr(directionAttr.getValue());++    if (!directionEnum.hasValue()) {+      return failure();+    }++    auto signalRef = FlatSymbolRefAttr::get(signalAttr.getValue(), context);++    ports.push_back(ModPortStructAttr::get(directionAttr, signalRef, context));++    if (!parser.parseOptionalRParen())+      break;++    parser.parseOptionalComma();+  }++  result.addAttribute("ports", ArrayAttr::get(ports, context));++  return success();+}++static void printInterfaceModPortOp(OpAsmPrinter &p, InterfaceModPortOp op) {+  p << op.getOperationName() << ' ';+  p.printSymbolName(op.getName());+  p << " (";++  auto ports = op.ports();+  for (size_t i = 0, e = ports.size(); i != e; ++i) {+    auto port = ports[i].dyn_cast<ModPortStructAttr>();++    p.printSymbolName(port.signal().getRootReference());+    p << " : ";+    p << port.direction();++    if (i < e - 1) {+      p << ", ";+    }+  }++  p << ')';+}++static LogicalResult verifyInterfaceModPortOp(InterfaceModPortOp op) {+  for (auto port : op.ports()) {+    if (!port.dyn_cast<ModPortStructAttr>()) {+      op.emitOpError("should have an array of ModPortStructAttr attributes");

nit: You can return this directly. Diagnostics implicitly convert to failure()

mikeurbach

comment created time in a month

PullRequestReviewEvent

Pull request review commentllvm/circt

[SV] Add SV Dialect operations to represent interfaces

 static void printAlwaysAtPosEdgeOp(OpAsmPrinter &p, AlwaysAtPosEdgeOp op) { }  //===----------------------------------------------------------------------===//+// Structure operations+//===----------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// InterfaceOp++static ParseResult parseInterfaceOp(OpAsmParser &parser,+                                    OperationState &result) {+  StringAttr nameAttr;+  if (parser.parseSymbolName(nameAttr, ::mlir::SymbolTable::getSymbolAttrName(),+                             result.attributes))+    return failure();++  Region *body = result.addRegion();+  if (parser.parseRegion(*body, llvm::None, llvm::None))+    return failure();++  InterfaceOp::ensureTerminator(*body, parser.getBuilder(), result.location);++  return success();+}++static void printInterfaceOp(OpAsmPrinter &p, InterfaceOp op) {+  p << op.getOperationName() << ' ';+  p.printSymbolName(op.getName());+  p.printRegion(op.body(),+                /*printEntryBlockArgs=*/false,+                /*printBlockTerminators=*/false);+}++//===----------------------------------------------------------------------===//+// InterfaceSignalOp++static ParseResult parseInterfaceSignalOp(OpAsmParser &parser,+                                          OperationState &result) {+  StringAttr nameAttr;+  TypeAttr type;+  if (parser.parseSymbolName(nameAttr, ::mlir::SymbolTable::getSymbolAttrName(),+                             result.attributes) ||+      parser.parseColon() ||+      parser.parseAttribute(type, "type", result.attributes))+    return failure();++  return success();+}++static void printInterfaceSignalOp(OpAsmPrinter &p, InterfaceSignalOp op) {

Hmmm, I sent out a few things for review that could probably help here:

  • D84719 adds support for "custom" directives, which allows for injecting custom parsing for specific parts of an otherwise declarative format. (e.g. the handling for "ports")
  • D86759 adds support for formatting symbol names properly in the declarative format
  • D86760 adds some support for regions (those without custom arg handling), and if you need more complex support you could use a custom directive.
mikeurbach

comment created time in a month

PullRequestReviewEvent
CommitCommentEvent
CommitCommentEvent

push eventllvm/llvm-project

River Riddle

commit sha d289a97f91443177b605926668512479c2cee37b

[mlir][PDL] Add a PDL Interpreter Dialect The PDL Interpreter dialect provides a lower level abstraction compared to the PDL dialect, and is targeted towards low level optimization and interpreter code generation. The dialect operations encapsulates low-level pattern match and rewrite "primitives", such as navigating the IR (Operation::getOperand), creating new operations (OpBuilder::create), etc. Many of the operations within this dialect also fuse branching control flow with some form of a predicate comparison operation. This type of fusion reduces the amount of work that an interpreter must do when executing. An example of this representation is shown below: ```mlir // The following high level PDL pattern: pdl.pattern : benefit(1) { %resultType = pdl.type %inputOperand = pdl.input %root, %results = pdl.operation "foo.op"(%inputOperand) -> %resultType pdl.rewrite %root { pdl.replace %root with (%inputOperand) } } // May be represented in the interpreter dialect as follows: module { func @matcher(%arg0: !pdl.operation) { pdl_interp.check_operation_name of %arg0 is "foo.op" -> ^bb2, ^bb1 ^bb1: pdl_interp.return ^bb2: pdl_interp.check_operand_count of %arg0 is 1 -> ^bb3, ^bb1 ^bb3: pdl_interp.check_result_count of %arg0 is 1 -> ^bb4, ^bb1 ^bb4: %0 = pdl_interp.get_operand 0 of %arg0 pdl_interp.is_not_null %0 : !pdl.value -> ^bb5, ^bb1 ^bb5: %1 = pdl_interp.get_result 0 of %arg0 pdl_interp.is_not_null %1 : !pdl.value -> ^bb6, ^bb1 ^bb6: pdl_interp.record_match @rewriters::@rewriter(%0, %arg0 : !pdl.value, !pdl.operation) : benefit(1), loc([%arg0]), root("foo.op") -> ^bb1 } module @rewriters { func @rewriter(%arg0: !pdl.value, %arg1: !pdl.operation) { pdl_interp.replace %arg1 with(%arg0) pdl_interp.return } } } ``` Differential Revision: https://reviews.llvm.org/D84579

view details

push time in a month

push eventllvm/llvm-project

River Riddle

commit sha 474f7639e3494d9605f4444b087f48e710fbb0d4

[mlir] Fix bug in block merging when the types of the operands differ The merging algorithm was previously not checking for type equivalence. Fixes PR47314 Differential Revision: https://reviews.llvm.org/D86594

view details

push time in a month

create barnchRiver707/decomp-editor

branch : ages-tools

created branch time in a month

created tagRiver707/decomp-editor

tag0.3.0

Collection of tools for the Poke-Emerald disassembly project

created time in a month

push eventRiver707/decomp-editor

River

commit sha 9388fecc04eb153caf51218e374a9d7da25b3e2a

Use JSON and Inja templates for Trainer data This will allow for more easily handling custom user data, and removes a lot of the hackyness related to interfacing with C directly.

view details

push time in a month

push eventllvm/llvm-project

River Riddle

commit sha 3fb3927bd333cded1f51025161a4ee7e7ca722c1

[mlir] Add a new "Pattern Descriptor Language" (PDL) dialect. PDL presents a high level abstraction for the rewrite pattern infrastructure available in MLIR. This abstraction allows for representing patterns transforming MLIR, as MLIR. This allows for applying all of the benefits that the general MLIR infrastructure provides, to the infrastructure itself. This means that pattern matching can be more easily verified for correctness, targeted by frontends, and optimized. PDL abstracts over various different aspects of patterns and core MLIR data structures. Patterns are specified via a `pdl.pattern` operation. These operations contain a region body for the "matcher" code, and terminate with a `pdl.rewrite` that either dispatches to an external rewriter or contains a region for the rewrite specified via `pdl`. The types of values in `pdl` are handle types to MLIR C++ types, with `!pdl.attribute`, `!pdl.operation`, and `!pdl.type` directly mapping to `mlir::Attribute`, `mlir::Operation*`, and `mlir::Value` respectively. An example pattern is shown below: ```mlir // pdl.pattern contains metadata similarly to a `RewritePattern`. pdl.pattern : benefit(1) { // External input operand values are specified via `pdl.input` operations. // Result types are constrainted via `pdl.type` operations. %resultType = pdl.type %inputOperand = pdl.input %root, %results = pdl.operation "foo.op"(%inputOperand) -> %resultType pdl.rewrite(%root) { pdl.replace %root with (%inputOperand) } } ``` This is a culmination of the work originally discussed here: https://groups.google.com/a/tensorflow.org/g/mlir/c/j_bn74ByxlQ Differential Revision: https://reviews.llvm.org/D84578

view details

push time in a month

issue commentRiver707/decomp-editor

Failed to load "Trainer Database"

Seems weird. The failure seems to hint that something went wrong when parsing the battle AI defines. Was it able to successfully parse the Battle AI Database? Also, have you modified constants/battle_ai.h at all or is it still the same as master?

vexio

comment created time in a month

push eventllvm/llvm-project

River Riddle

commit sha c996d49c6987b78bceab55cf36f236c7de066b31

[mlir] Update the documentation for defining types The documentation needs a refresh now that "kinds" are no longer a concept. This revision also adds mentions to a few other new concepts, e.g. traits and interfaces. Differential Revision: https://reviews.llvm.org/D86182

view details

push time in a month

push eventllvm/llvm-project

River Riddle

commit sha 250f43d3ecc8d6a3780c9aa2e3770c0193a28850

[mlir] Remove the use of "kinds" from Attributes and Types This greatly simplifies a large portion of the underlying infrastructure, allows for lookups of singleton classes to be much more efficient and always thread-safe(no locking). As a result of this, the dialect symbol registry has been removed as it is no longer necessary. For users broken by this change, an alert was sent out(https://llvm.discourse.group/t/removing-kinds-from-attributes-and-types) that helps prevent a majority of the breakage surface area. All that should be necessary, if the advice in that alert was followed, is removing the kind passed to the ::get methods. Differential Revision: https://reviews.llvm.org/D86121

view details

push time in a month

issue commentgoogle/iree

Figure out type conversion issues in std->vm conversion

If the VMLA type converter is provided to the ConstantOpConversion when inserted:

patterns.insert<ConstantOpConversion>(typeConverter, context);

MLIR's behavior changes and things get broken:

D:\Dev\iree/iree/compiler/Dialect/HAL/Target/VMLA/test/smoketest.mlir split at line #83:8:12: error: failed to materialize conversion for result #0 of operation 'std.constant' that remained live after conversion
      %0 = "mhlo.reduce"(%arg0, %cst) ( {

Removing the TypeConverter makes things work. No clue why.

The switch to make dialect conversion type-safe is only half way done. If you don't provide a type converter, it defaults to ignoring type safety to preserve those currently dependent on the old behavior. I'll try to break down exactly what is happening for this particular situation:

To start off with, let's look at the error that is being generated:

iree/compiler/Dialect/HAL/Target/VMLA/test/smoketest.mlir:90:12: error: failed to materialize conversion for result #0 of operation 'std.constant' that remained live after conversion
      %0 = "mhlo.reduce"(%arg0, %cst) ( {
           ^
iree/compiler/Dialect/HAL/Target/VMLA/test/smoketest.mlir:88:5: note: called from
    func @reduction_ex_dispatch_0(%arg0: tensor<4x8xf32>) -> tensor<4xf32> {
    ^
iree/compiler/Dialect/HAL/Target/VMLA/test/smoketest.mlir:90:12: note: see current operation: %c4_8 = "std.constant"() {value = 4 : index} : () -> index
      %0 = "mhlo.reduce"(%arg0, %cst) ( {
           ^
iree/compiler/Dialect/HAL/Target/VMLA/test/smoketest.mlir:90:12: note: see existing live user here: vm.call.variadic @vmla.reduce.sum.f32(%ref_6, [%c4_8, %c8_9], %ref, [], %c1_i32, %ref_7, [%c4_11]) : (!vm.ref<!vmla.buffer>, i32 ..., !vm.ref<!vmla.buffer>, i32 ..., i32, !vm.ref<!vmla.buffer>, i32 ...)

Here it is complaining that the conversion framework could not materialize a conversion for result #0 of a std.constant that remained live after conversion. This means that the std.constant was replaced by a value of a different type, but the original value still has users that will be live(i.e. not replaced/erased) after the conversion finishes. The particular user that remains live is vm.call.variadic @vmla.reduce.sum.f32. (I really need to make that error message much longer, pretty bad right now)

  • The live vm.call.variadic is generated during VMLA -> VM when it converts the following operation that was produced at some point when mhlo.reduce was lowered:
"vmla.reduce.sum"(%3, %rs4_8, %1, %rs, %4, %rs4) {dimension = 1 : i32, element_type = f32} : (!vmla.buffer, !shapex.ranked_shape<[4,8]>, !vmla.buffer, !shapex.ranked_shape<[]>, !vmla.buffer, !shapex.ranked_shape<[4]>) -> ()

We can confirm this by looking for our vm.call.variadic in the debug output:

//===-------------------------------------------===//
Legalizing operation : 'vmla.reduce.sum'(0x4b879e0) {
  "vmla.reduce.sum"(%3, %rs4_8, %1, %rs, %4, %rs4) {dimension = 1 : i32, element_type = f32} : (!vmla.buffer, !shapex.ranked_shape<[4,8]>, !vmla.buffer, !shapex.ranked_shape<[]>, !vmla.buffer, !shapex.ranked_shape<[4]>) -> ()

  * Fold {
  } -> FAILURE : unable to fold

  * Pattern : 'vmla.reduce.sum -> ()' {
    ** Insert  : 'std.constant'(0x4b5c540)
    ** Insert  : 'std.constant'(0x4b6a8d0)
    ** Insert  : 'std.constant'(0x4b96d20)
    ** Insert  : 'std.constant'(0x4b96dd0)
    ** Insert  : 'vm.call.variadic'(0x4b86570)
    ** Replace : 'vmla.reduce.sum'(0x4b879e0)

We also see some std.constants here, which is a pretty good indicator that the problem is originating here. The lowering for vmla.reduce.sum is implemented with rewriteToCall(in ImportUtils.h), which lowers to our vm.call.variadic. This lowering has special logic when the original operands of the operation being converted are of shapex.ranked_shape, which is the case here.

        // Expand a ranked_shape into its dimensions.
        // We need to rematerialize the static dimensions and then pass through
        // the new dynamic dimensions that we have the SSA values for.
        auto rankedShapeType = oldOperands[0]
                                   .getType()
                                   .template dyn_cast<Shape::RankedShapeType>();
        for (int i = 0; i < rankedShapeType.getRank(); ++i) {
          auto dimOp = rewriter.createOrFold<Shape::RankedDimOp>(
              op.getLoc(), oldOperands[0], i);
          state.addOperands(dimOp);
        }

The std.constant operations from above are generated with a result type of index when folding this Shape::RankedDimOp. These index constants are then fed to vm.call.variadic when it gets generated. Our generated vm.call.variadic is then marked as legal for the conversion target while still having the index operands:

    //===-------------------------------------------===//
    Legalizing operation : 'vm.call.variadic'(0x4b86570) {
      "vm.call.variadic"(%ref_6, %c4_8, %c8_9, %ref, %c1_i32, %ref_7, %c4_11) {callee = @vmla.reduce.sum.f32, segment_sizes = dense<[-1, 2, -1, 0, -1, -1, 1]> : vector<7xi16>, segment_types = [!vm.ref<!vmla.buffer>, i32, !vm.ref<!vmla.buffer>, i32, i32, !vm.ref<!vmla.buffer>, i32]} : (!vm.ref<!vmla.buffer>, index, index, !vm.ref<!vmla.buffer>, i32, !vm.ref<!vmla.buffer>, index) -> ()

    } -> SUCCESS : operation marked legal by the target
    //===-------------------------------------------===//

Herein lies the problem. From the perspective of the conversion framework, the use of a value and its definition establish a "type contract" that is not allowed to be broken. If an operation consumes a specific type, in this case index, the type being consumed will not implicitly change, even if the producer type changes. In such cases where the producer type changes, the framework will materialize a conversion from the new producer type to the old consumed type. You might say "wait a minute, I made sure to legalize the index to the proper type afterwards", which is true. If we look at the output, the index constants are properly legalized:

    //===-------------------------------------------===//
    Legalizing operation : 'std.constant'(0x4b5c540) {
      %c4 = "std.constant"() {value = 4 : index} : () -> index

      * Fold {
      } -> FAILURE : unable to fold

      * Pattern : 'std.constant -> ()' {
        ** Insert  : 'vm.const.i32'(0x4b96e90)
        ** Replace : 'std.constant'(0x4b5c540)

        //===-------------------------------------------===//
        Legalizing operation : 'vm.const.i32'(0x4b96e90) {
          %c4 = "vm.const.i32"() {value = 4 : i32} : () -> i32

        } -> SUCCESS : operation marked legal by the target
        //===-------------------------------------------===//
      } -> SUCCESS : pattern applied successfully
    } -> SUCCESS
    //===-------------------------------------------===//

The problem is that by this point a type contract has already been established between the old std.constant and the vm.call.variadic. This means that even though the std.constant was legalized to something producing the type you expected, i32, but the vm.call.variadic is already expecting to consume an index. Now at this point the question is probably "well that sucks, what am I supposed to do about that?". There are a couple options that still preserve the nature of "type contract", but help prevent annoyances such as this:

*) Don't create and consume a known illegal type The easiest option is to not create and consume an illegal type in the first place. In this situation, you already know that a value of index is never going to be valid. The handling of ranked_shape could be modified to make sure the constant is a valid type.

*) Add a passthrough type conversion-esque pattern If you don't know that you are generating something illegal inside of the pattern itself, the common thing is to have a type conversion pattern for the operations that are effectively 1-1. This is like the CallOpTypeConversion pattern in the standard dialect.

*) Have some mechanism to know if an operation can just accept the new type This one is a bit tricky, because in the context of conversions it can get really weird to assume things. One thing the conversion infra "might" be able to do is just know that "Hey, this op was created in a pattern with this TypeConverter, so I'm just going to assume that I can update the type of the operand because the TypeConverter says the new type is legal". This feels a little dirty, because one could technically intend to create something with an illegal operand(not sure why).

Open to other suggestions as well.

benvanik

comment created time in a month

Pull request review commentgoogle/iree

Remove uses of kindof/getKind on derived mlir Attribute and Type classes

 class TFStringsType : public Type {  public:   using Type::Type; -  static bool classof(Type type) {-    return type.getKind() >= TFStringsTypes::FIRST_USED_STRINGS_TYPE &&-           type.getKind() <= TFStringsTypes::LAST_USED_STRINGS_TYPE;-  }+  static bool classof(Type type);

I put it in tf_strings/ir/dialect.cpp.

River707

comment created time in a month

push eventllvm/llvm-project

River Riddle

commit sha fa4b3147e3368f63e27b86ef66cd35f484ceb6d6

[mlir][DialectConversion] Update the documentation for dialect conversion This revision updates the documentation for dialect conversion, as many concepts have changed/evolved over time. Differential Revision: https://reviews.llvm.org/D85167

view details

River Riddle

commit sha f7a13479b809cdeb9d63d0daa0d6ab61f04d5f7a

[mlir][docs] Update/Add documentation for MLIRs Pattern Rewrite infrastructure This infrastructure has evolved a lot over the course of MLIRs lifetime, and has never truly been documented outside of rationale or proposals. This revision aims to document the infrastructure and user facing API, with the rationale specific portions moved to the Rationale folder and updated. Differential Revision: https://reviews.llvm.org/D85260

view details

push time in a month

push eventllvm/llvm-project

River Riddle

commit sha 65277126bf90401436e018fcce0fc636d34ea771

[mlir][Type] Remove the remaining usages of Type::getKind in preparation for its removal This revision removes all of the lingering usages of Type::getKind. A consequence of this is that FloatType is now split into 4 derived types that represent each of the possible float types(BFloat16Type, Float16Type, Float32Type, and Float64Type). Other than this split, this revision is NFC. Reviewed By: mehdi_amini Differential Revision: https://reviews.llvm.org/D85566

view details

push time in 2 months

PR opened google/iree

Remove uses of kindof/getKind on derived mlir Attribute and Type classes

The need/existence of these are being removed from upstream MLIR.

PiperOrigin-RevId: 325874770

+99 -195

0 comment

20 changed files

pr created time in 2 months

create barnchRiver707/iree

branch : test_325874770

created branch time in 2 months

created tagRiver707/decomp-editor

tagv0.2.0-alpha

Collection of tools for the Poke-Emerald disassembly project

created time in 2 months

push eventRiver707/decomp-editor

River

commit sha b94c620320020b944c06c718ab40b718d1ba7410

Update the static file comment in the autogen files This also fixes a bug in event object identifier replacement

view details

push time in 2 months

push eventRiver707/decomp-editor

River

commit sha 6d74decc2926ed1d1fc64ef5e1deb843de66a5bc

Make sure logs are placed in the application data directory

view details

push time in 2 months

push eventRiver707/decomp-editor

River

commit sha 44248dab10149c274ea195c8bd289dfc8de21f0c

Make sure logs are placed in the application data directory

view details

push time in 2 months

push eventRiver707/decomp-editor

River

commit sha 6f7713a3eaade65610137e7f2bbc0d23d9d1046a

Ensure that databases are reset when trying to upgrade

view details

push time in 2 months

push eventRiver707/decomp-editor

River

commit sha 310e886242f8026ddd8c8c3d8af2538e27c30c62

Simplify the inja templates used during project conversion

view details

push time in 2 months

push eventRiver707/decomp-editor

River

commit sha f68168f187ed98471e3626d29f61cfe310a9bff7

Fix a few errors in the inja json templates

view details

push time in 2 months

push eventllvm/llvm-project

River Riddle

commit sha 86646be3158933330bf3342e9d7e4250945bb70c

[mlir] Refactor StorageUniquer to require registration of possible storage types This allows for bucketing the different possible storage types, with each bucket having its own allocator/mutex/instance map. This greatly reduces the amount of lock contention when multi-threading is enabled. On some non-trivial .mlir modules (>300K operations), this led to a compile time decrease of a single conversion pass by around half a second(>25%). Differential Revision: https://reviews.llvm.org/D82596

view details

River Riddle

commit sha 9f24640b7e6e61b0f293c724155a90a5e446dd7a

[mlir] Add a utility class, ThreadLocalCache, for storing non static thread local objects. This class allows for defining thread local objects that have a set non-static lifetime. This internals of the cache use a static thread_local map between the various different non-static objects and the desired value type. When a non-static object destructs, it simply nulls out the entry in the static map. This will leave an entry in the map, but erase any of the data for the associated value. The current use cases for this are in the MLIRContext, meaning that the number of items in the static map is ~1-2 which aren't particularly costly enough to warrant the complexity of pruning. If a use case arises that requires pruning of the map, the functionality can be added. This is especially useful in the context of MLIR for implementing thread-local caching of context level objects that would otherwise have very high lock contention. This revision adds a thread local cache in the MLIRContext for attributes, identifiers, and types to reduce some of the locking burden. This led to a speedup of several hundred miliseconds when compiling a conversion pass on a very large mlir module(>300K operations). Differential Revision: https://reviews.llvm.org/D82597

view details

River Riddle

commit sha dd48773396f77fd7b19adc43b23d41aef356809a

[mlir][Types] Remove the subclass data from Type Subclass data is useful when a certain amount of memory is allocated, but not all of it is used. In the case of Type, that hasn't been the case for a while and the subclass is just taking up a full `unsigned`. Removing this frees up ~8 bytes for almost every type instance. Differential Revision: https://reviews.llvm.org/D85348

view details

River Riddle

commit sha 1d6a8deb41221f73c57b57fe9add180da34af77f

[mlir] Remove the need to define `kindof` on attribute and type classes. This revision refactors the default definition of the attribute and type `classof` methods to use the TypeID of the concrete class instead of invoking the `kindof` method. The TypeID is already used as part of uniquing, and this allows for removing the need for users to define any of the type casting utilities themselves. Differential Revision: https://reviews.llvm.org/D85356

view details

River Riddle

commit sha fff39b62bb4078ce78813f25c04e0da435a8feb3

[mlir][Attribute] Remove usages of Attribute::getKind This is in preparation for removing the use of "kinds" within attributes and types in MLIR. Differential Revision: https://reviews.llvm.org/D85370

view details

River Riddle

commit sha c8c45985fba935f28943d6218915d7fe5a5fc807

[mlir][Type] Remove usages of Type::getKind This is in preparation for removing the use of "kinds" within attributes and types in MLIR. Differential Revision: https://reviews.llvm.org/D85475

view details

River Riddle

commit sha 82fd1392016984c81c6037e147ee2dd36cf91f4c

[flang] Update FirOpsDialect constructor to pass its TypeID

view details

push time in 2 months

push eventRiver707/decomp-editor

River

commit sha 7f0a2797c20bbb28aa86655dc562fe08a0303ef4

Start sketching out a new representation for object events

view details

River

commit sha 580d05c8f80a1d9457c57b38cd2e3a9b1546dfe9

Finish sketching out a conversion from the default objevent format to json and add logging This revision finishes the initial work of converting the default project format for object events into a json based format that is more easily transformable/consumable. This revision also adds support for logging project serialization events.

view details

push time in 2 months

issue commentRiver707/decomp-editor

Crash & Freeze when attempting to open a project

If this was caused by the tool, I would be interested in a reproducer for this situation if possible.

vexio

comment created time in 2 months

issue commentRiver707/decomp-editor

Crash & Freeze when attempting to open a project

You would need to resolve the duplicates in both the tag definition and in the sprite palette array definition, i.e. sObjectEventSpritePalettes. gObjectEventPalette_Girl1 and gObjectEventPalette1 are both mapped to OBJ_EVENT_PAL_TAG_1.

vexio

comment created time in 2 months

issue commentRiver707/decomp-editor

Crash & Freeze when attempting to open a project

Took a quick look and seems like somehow you have a duplicate definition OBJ_EVENT_PAL_TAG_1 which was causing an exception to be thrown. It shouldn't crash it that case, but removing the duplicate definition allowed for the project to load successfully with 0.1.4.

vexio

comment created time in 2 months

push eventllvm/llvm-project

River Riddle

commit sha 8c39e70679e93da3af9f881d314940c570d5d822

[mlir][OpFormatGen] Add support for eliding UnitAttr when used to anchor an optional group Unit attributes are given meaning by their existence, and thus have no meaningful value beyond "is it present". As such, in the format of an operation unit attributes are generally used to guard the printing of other elements and aren't generally printed themselves; as the presence of the group when parsing means that the unit attribute should be added. This revision adds support to the declarative format for eliding unit attributes in situations where they anchor an optional group, but aren't the first element. For example, ``` let assemblyFormat = "(`is_optional` $unit_attr^)? attr-dict"; ``` would print `foo.op is_optional` when $unit_attr is present, instead of the current `foo.op is_optional unit`. Differential Revision: https://reviews.llvm.org/D84577

view details

push time in 2 months

push eventllvm/llvm-project

River Riddle

commit sha 2a6c8b2e9581ebca4b05d1e64458f2dccf3db61f

[mlir][PassIncGen] Refactor how pass registration is generated The current output is a bit clunky and requires including files+macros everywhere, or manually wrapping the file inclusion in a registration function. This revision refactors the pass backend to automatically generate `registerFooPass`/`registerFooPasses` functions that wrap the pass registration. `gen-pass-decls` now takes a `-name` input that specifies a tag name for the group of passes that are being generated. For each pass, the generator now produces a `registerFooPass` where `Foo` is the name of the definition specified in tablegen. It also generates a `registerGroupPasses`, where `Group` is the tag provided via the `-name` input parameter, that registers all of the passes present. Differential Revision: https://reviews.llvm.org/D84983

view details

push time in 2 months

push eventllvm/llvm-project

River Riddle

commit sha 4589dd924dfc43c846652b85825e291af0d7428a

[mlir][DialectConversion] Enable deeper integration of type conversions This revision adds support for much deeper type conversion integration into the conversion process, and enables auto-generating cast operations when necessary. Type conversions are now largely automatically managed by the conversion infra when using a ConversionPattern with a provided TypeConverter. This removes the need for patterns to do type cast wrapping themselves and moves the burden to the infra. This makes it much easier to perform partial lowerings when type conversions are involved, as any lingering type conversions will be automatically resolved/legalized by the conversion infra. To support this new integration, a few changes have been made to the type materialization API on TypeConverter. Materialization has been split into three separate categories: * Argument Materialization: This type of materialization is used when converting the type of block arguments when calling `convertRegionTypes`. This is useful for contextually inserting additional conversion operations when converting a block argument type, such as when converting the types of a function signature. * Source Materialization: This type of materialization is used to convert a legal type of the converter into a non-legal type, generally a source type. This may be called when uses of a non-legal type persist after the conversion process has finished. * Target Materialization: This type of materialization is used to convert a non-legal, or source, type into a legal, or target, type. This type of materialization is used when applying a pattern on an operation, but the types of the operands have not yet been converted. Differential Revision: https://reviews.llvm.org/D82831

view details

push time in 2 months

push eventllvm/llvm-project

Chris Morin

commit sha 3d9967039d4191b77f939ddc6c6ff4275df620c2

[mlir][docs] Fix Markdown format in Language Reference Differential Revision: https://reviews.llvm.org/D84271

view details

push time in 2 months

push eventllvm/llvm-project

River Riddle

commit sha 6b476e2426e9cfa442dac5deed2ceae890513f18

[mlir] Add support for parsing optional Attribute values. This adds a `parseOptionalAttribute` method to the OpAsmParser that allows for parsing optional attributes, in a similar fashion to how optional types are parsed. This also enables the use of attribute values as the first element of an assembly format optional group. Differential Revision: https://reviews.llvm.org/D83712

view details

push time in 2 months

push eventllvm/llvm-project

River Riddle

commit sha b98f414a04e19202669a4273e620bc12b5054413

[mlir][DialectConversion] Emit an error if an operation marked as erased has live users after conversion Up until now, there has been an implicit agreement that when an operation is marked as "erased" all uses of that operation's results are guaranteed to be removed during conversion. How this works in practice is that there is either an assert/crash/asan failure/etc. This revision adds support for properly detecting when an erased operation has dangling users, emits and error and fails the conversion. Differential Revision: https://reviews.llvm.org/D82830

view details

push time in 2 months

push eventllvm/llvm-project

River Riddle

commit sha 572c2905aeaef00a6fedfc4c54f21856ba4cc34e

[mlir][ODS] Add support for specifying the namespace of an interface. The namespace can be specified using the `cppNamespace` field. This matches the functionality already present on dialects, enums, etc. This fixes problems with using interfaces on operations in a different namespace than the interface was defined in. Differential Revision: https://reviews.llvm.org/D83604

view details

push time in 3 months

Pull request review commentcirct/circt

[LLHD] Add equality operations

 OpFoldResult llhd::SModOp::fold(ArrayRef<Attribute> operands) {   }); } +//===----------------------------------------------------------------------===//+// EqOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::EqOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.eq(x, 1) -> x+  if (matchPattern(rhs(), m_One()) && lhs().getType().isSignlessInteger(1))+    return lhs();++  /// llhs.eq(x,x) -> 1+  if (lhs() == rhs())+    return BoolAttr::get(true, getContext());++  if (!operands[0] || !operands[1])+    return {};++  if (auto lhs = operands[0].dyn_cast<IntegerAttr>()) {+    if (auto rhs = operands[1].dyn_cast<IntegerAttr>()) {+      return BoolAttr::get(lhs.getValue() == rhs.getValue(), getContext());

nit: Attributes are unique'd, so you should be able to check pointer equality here and below.

maerhart

comment created time in 3 months

push eventllvm/llvm-project

River Riddle

commit sha 24aa4efffd831a1125b4eb835e1911fa38f501d7

[mlir] Print 0 element DenseElementsAttr as dense<> to fix parser bugs with expected shape. Depending on where the 0 dimension is within the shape, the parser will currently reject .mlir generated by the printer. Differential Revision: https://reviews.llvm.org/D83445

view details

push time in 3 months

Pull request review commentcirct/circt

Add -remove-block-structure for Handshake dialect

 struct FuncOpLowering : public OpConversionPattern<mlir::FuncOp> { };  namespace {+struct DFRemoveBlockPass+    : public PassWrapper<DFRemoveBlockPass, OperationPass<handshake::FuncOp>> {+  void runOnOperation() override {+    auto funcOp = getOperation();+    auto builder = OpBuilder(funcOp.getContext());+    auto *termOp = funcOp.getBody().front().getTerminator();++    for (auto &block : funcOp) {+      if (block.isEntryBlock())+        continue;++      // Move all operations to the first block except for terminator operations+      // which can be safely dropped.+      while (!block.empty()) {

nit: Can you splice the operation lists instead of cloning? You can access the raw llvm::iplist viablock.getOperations(). You can then use splice to move the operations over to the back of the entry block.

hanchenye

comment created time in 3 months

Pull request review commentcirct/circt

Add -remove-block-structure for Handshake dialect

 struct FuncOpLowering : public OpConversionPattern<mlir::FuncOp> { };  namespace {+struct DFRemoveBlockPass+    : public PassWrapper<DFRemoveBlockPass, OperationPass<handshake::FuncOp>> {+  void runOnOperation() override {+    auto funcOp = getOperation();+    auto builder = OpBuilder(funcOp.getContext());+    auto *termOp = funcOp.getBody().front().getTerminator();++    for (auto &block : funcOp) {

nit: Can you do something like llvm::drop_begin(llvm::make_early_inc_range(funcOp), 1)

That would let you erase blocks inside of the loop, and remove the check for isEntryBlock.

hanchenye

comment created time in 3 months

Pull request review commentcirct/circt

Add -remove-block-structure for Handshake dialect

 struct FuncOpLowering : public OpConversionPattern<mlir::FuncOp> { };  namespace {+struct DFRemoveBlockPass+    : public PassWrapper<DFRemoveBlockPass, OperationPass<handshake::FuncOp>> {+  void runOnOperation() override {+    auto funcOp = getOperation();+    auto builder = OpBuilder(funcOp.getContext());+    auto *termOp = funcOp.getBody().front().getTerminator();++    for (auto &block : funcOp) {+      if (block.isEntryBlock())+        continue;++      // Move all operations to the first block except for terminator operations+      // which can be safely dropped.+      while (!block.empty()) {+        Operation &op = block.front();+        if (isa<handshake::TerminatorOp>(op)) {+          op.erase();+        } else if (isa<handshake::ReturnOp>(op)) {

For my own edification, is it ever possible to have multiple ReturnOps in here?

hanchenye

comment created time in 3 months

created tagRiver707/decomp-editor

tagv0.1.4-alpha

Collection of tools for the Poke-Emerald disassembly project

created time in 3 months

release River707/decomp-editor

v0.1.4-alpha

released time in 3 months

push eventRiver707/decomp-editor

River

commit sha 3fbb0009a0d8f07040ce6fa1bd8f9bddfaf47bcc

Fix bug when saving trainer classes. The ending brace was no longer being emitted.

view details

push time in 3 months

push eventllvm/llvm-project

River Riddle

commit sha 9db53a182705ac1f652c6ee375735bea5539272c

[mlir][NFC] Remove usernames and google bug numbers from TODO comments. These were largely leftover from when MLIR was a google project, and don't really follow LLVM guidelines.

view details

push time in 3 months

push eventllvm/llvm-project

River Riddle

commit sha c59aec0ca1edac409d8789956049ae13af24e370

[mlir][OpFormatGen] Add support for resolving variadic types from non-variadic This enables better support for traits such as SameOperandsAndResultType, and other situations in which a variadic operand may be resolved from a non-variadic. Differential Revision: https://reviews.llvm.org/D83011

view details

push time in 3 months

pull request commentcirct/circt

make and, or, xor, add - variadic

It's parsed/printed as a list, i.e. you are missing a comma between the SSA values.

./bin/circt-opt ../test/rtl/bitwise.mlir -debug
../test/rtl/bitwise.mlir:8:19: error: custom op 'rtl.and' 2 operands present, but expected 1
  %and1 = rtl.and %a, %b : i7

Took a look and seems like OpFormatGen doesn't support inferring a variadic from a non-variadic, should be fixed with https://reviews.llvm.org/D83011 if someone wants to take a look. I'll be OOO until next thursday starting tomorrow, so it'd need to be reviewed soon to make sure it is in before I leave.

drom

comment created time in 3 months

pull request commentcirct/circt

make and, or, xor, add - variadic

Copy-pasted River's fix to unblock variadic stuff and no longer getting the compiler error from before. However, I am still seeing some unexpected behavior. This is the variadic ops:

class VariadicRTLOp<string mnemonic, list<OpTrait> traits = []> :
      RTLOp<mnemonic, !listconcat(traits, [NoSideEffect])> {
  let arguments = (ins Variadic<AnySignlessInteger>:$operands);
  let results = (outs AnySignlessInteger:$result);

  let assemblyFormat = [{
    $operands  attr-dict `:` functional-type($operands, results)
  }];
}

class UTVariadicRTLOp<string mnemonic, list<OpTrait> traits = []> :
      VariadicRTLOp<mnemonic,
               !listconcat(traits,
                           [SameTypeOperands, SameOperandsAndResultType])> {
   let assemblyFormat = [{
    $operands  attr-dict `:` type($result)
  }];

def AndOp : UTVariadicRTLOp<"and", [Commutative]>;
def OrOp  : UTVariadicRTLOp<"or", [Commutative]>;
def XorOp : UTVariadicRTLOp<"xor", [Commutative]>;

And getting this error from a test:

./bin/circt-opt ../test/rtl/bitwise.mlir -debug
Args: ./bin/circt-opt ../test/rtl/bitwise.mlir -debug 
../test/rtl/bitwise.mlir:8:22: error: expected ':'
  %and1 = rtl.and %a %b : i7
                     ^

From what I can tell (from this and some other experiments) it is expecting one operand, even though the and op is specified as being variadic. Again, I'm not sure if this is a bug or a me-problem.

It's parsed/printed as a list, i.e. you are missing a comma between the SSA values.

drom

comment created time in 3 months

push eventllvm/llvm-project

River Riddle

commit sha f625f5231ab8e76b1367f70aeed56b6389d83471

[mlir] Remove the default template parameters from AttrBase and TypeBase. MSVC 2017 doesn't support the case where a trailing variadic template list comes after template types with default parameters. Until we upgrade to VS 2019, we can't use the simplified definitions.

view details

push time in 3 months

issue openedhuderlem/porymap

Suggestion: Keep focus on the currently selected tile when zooming in the map view

I tend to use ctrl+scroll to zoom when working on a map. It would be nice if the currently selected tile, i.e. the one the mouse cursor is currently hovering over, was kept in focus when zooming. This is a fairly common feature in many image editors, and it seems like it would be a good addition to porymap.

created time in 3 months

push eventllvm/llvm-project

River Riddle

commit sha 9fbb2de8e475cbb4ffa71280eb2ddc4922af05f6

[mlir] Add support for defining Traits and Interfaces on Attributes/Types. This revisions add mechanisms to Attribute/Type for attaching traits and interfaces. The mechanisms are modeled 1-1 after those for operations to keep the system consistent. AttrBase and TypeBase now accepts a trailing list of `Trait` types that will be attached to the object. These traits should inherit from AttributeTrait::TraitBase and TypeTrait::TraitBase respectively as necessary. A followup commit will refactor the interface gen mechanisms in ODS to support Attribute/Type interface generation and add tests for the mechanisms. Differential Revision: https://reviews.llvm.org/D81883

view details

River Riddle

commit sha 2e2cdd0a5230790300bdde7e5629fedef36d99b6

[mlir] Refactor InterfaceGen to support generating interfaces for Attributes and Types. This revision adds support to ODS for generating interfaces for attributes and types, in addition to operations. These interfaces can be specified using `AttrInterface` and `TypeInterface` in place of `OpInterface`. All of the features of `OpInterface` are supported except for the `verify` method, which does not have a matching representation in the Attribute/Type world. Generating these interface can be done using `gen-(attr|type)-interface-(defs|decls|docs)`. Differential Revision: https://reviews.llvm.org/D81884

view details

River Riddle

commit sha 5d699d18b32c0e0c27eceec026ed399e76e7e8ef

[mlir] Remove locking for dialect/operation registration. Moving forward dialects should only be registered in a thread safe context. This matches the existing usage we have today, but it allows for removing quite a bit of expensive locking from the context. This led to ~.5 a second compile time improvement when running one conversion pass on a very large .mlir file(hundreds of thousands of operations). Differential Revision: https://reviews.llvm.org/D82595

view details

push time in 3 months

pull request commentcirct/circt

make and, or, xor, add - variadic

Is the ODS wrong? Are variadic things finicky? What's up with generating the operands.front() call if ValueRange doesn't have a front()? Looked through the ODS docs and other td files, but still a bit stuck. Commenting out the tabelgen'd code that causes the error doesn't seem like a good solution.

I suspect ODS is wrong when dealing with Variadic operations and SameOperandsAndResultType. ValueRange used to be a vector, but now its not, so I suspect this case just didn't get fixed. It should probably be something like *(range.getTypes().begin())

Yes, I don't think this wasactually tested anywhere which is unfortunate. Should be fixed in https://github.com/llvm/llvm-project/commit/6b9a706200cbb27e5e863cfd41fb3684ee616e23

drom

comment created time in 3 months

push eventllvm/llvm-project

River Riddle

commit sha 6b9a706200cbb27e5e863cfd41fb3684ee616e23

Add front/back accessors to indexed_accessor_range. These map to the similar accessors on ArrayRef and other random access containers. This fixes a compilation error on MLIR ODS for variadic operands/results, which relied on the availability of front in certain situations.

view details

push time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Conversion/LLHDToLLVM/LLHDToLLVM.h"+#include "circt/Dialect/LLHD/IR/LLHDDialect.h"+#include "circt/Dialect/LLHD/IR/LLHDOps.h"++#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h"+#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h"+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Dominance.h"+#include "mlir/Pass/Pass.h"+#include "mlir/Transforms/DialectConversion.h"++namespace mlir {+namespace llhd {+#define GEN_PASS_CLASSES+#include "circt/Conversion/LLHDToLLVM/Passes.h.inc"+} // namespace llhd+} // namespace mlir++using namespace mlir;+using namespace mlir::llhd;++// keep a counter to infer a signal's index in his entity's signal table+static int signalCounter = 0;+// keep a counter to infer the resume index after a wait instruction in a+// process+static int resumeCounter = 0;+//===----------------------------------------------------------------------===//+// Helpers+//===----------------------------------------------------------------------===//++/// Get an existing global string+static Value getGlobalString(Location loc, OpBuilder &builder,+                             LLVMTypeConverter &typeConverter,+                             LLVM::GlobalOp &str) {+  auto i8PtrTy = LLVM::LLVMType::getInt8PtrTy(typeConverter.getDialect());+  auto i32Ty = LLVM::LLVMType::getInt32Ty(typeConverter.getDialect());++  auto addr = builder.create<LLVM::AddressOfOp>(+      loc, str.getType().getPointerTo(), str.getName());+  auto idx = builder.create<LLVM::ConstantOp>(loc, i32Ty,+                                              builder.getI32IntegerAttr(0));+  llvm::SmallVector<Value, 2> idxs({idx, idx});+  auto gep = builder.create<LLVM::GEPOp>(loc, i8PtrTy, addr, idxs);

nit: Can you just return the result directly?

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Dialect/LLHD/IR/LLHDOps.h"+#include "circt/Dialect/LLHD/Transforms/Passes.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"++using namespace mlir;++namespace {++struct ProcessLoweringPass+    : public llhd::ProcessLoweringBase<ProcessLoweringPass> {+  void runOnOperation() override;+};++void ProcessLoweringPass::runOnOperation() {+  ModuleOp module = getOperation();++  module.walk([this](llhd::ProcOp op) {+    // Check invariants+    size_t numBlocks = op.body().getBlocks().size();+    if (numBlocks == 1) {+      if (!isa<llhd::HaltOp>(op.body().back().getTerminator())) {+        op.emitOpError("Process-lowering: Entry block is required to be "+                       "terminated by a HaltOp from the LLHD dialect.");+        signalPassFailure();

Did you intend to return after these failures?

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Dialect/LLHD/IR/LLHDOps.h"+#include "circt/Dialect/LLHD/IR/LLHDDialect.h"+#include "mlir/Dialect/CommonFolders.h"+#include "mlir/IR/Attributes.h"+#include "mlir/IR/Matchers.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/OpImplementation.h"+#include "mlir/IR/PatternMatch.h"+#include "mlir/IR/Region.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/IR/Types.h"+#include "mlir/IR/Value.h"+#include "mlir/Support/LogicalResult.h"+#include "llvm/ADT/ArrayRef.h"+#include "llvm/ADT/SmallVector.h"+#include "llvm/ADT/StringMap.h"++using namespace mlir;++namespace {++template <class AttrElementT,+          class ElementValueT = typename AttrElementT::ValueType,+          class CalculationT = function_ref<ElementValueT(ElementValueT)>>+Attribute constFoldUnaryOp(ArrayRef<Attribute> operands,+                           const CalculationT &calculate) {+  assert(operands.size() == 1 && "unary op takes one operand");+  if (!operands[0])+    return {};++  if (auto val = operands[0].dyn_cast<AttrElementT>()) {+    return AttrElementT::get(val.getType(), calculate(val.getValue()));+  } else if (auto val = operands[0].dyn_cast<SplatElementsAttr>()) {+    // Operand is a splat so we can avoid expanding the value out and+    // just fold based on the splat value.+    auto elementResult = calculate(val.getSplatValue<ElementValueT>());+    return DenseElementsAttr::get(val.getType(), elementResult);+  }+  if (auto val = operands[0].dyn_cast<ElementsAttr>()) {+    // Operand is ElementsAttr-derived; perform an element-wise fold by+    // expanding the values.+    auto valIt = val.getValues<ElementValueT>().begin();+    SmallVector<ElementValueT, 4> elementResults;+    elementResults.reserve(val.getNumElements());+    for (size_t i = 0, e = val.getNumElements(); i < e; ++i, ++valIt)+      elementResults.push_back(calculate(*valIt));+    return DenseElementsAttr::get(val.getType(), elementResults);+  }+  return {};+}++template <class AttrElementT,+          class ElementValueT = typename AttrElementT::ValueType,+          class CalculationT = function_ref<+              ElementValueT(ElementValueT, ElementValueT, ElementValueT)>>+Attribute constFoldTernaryOp(ArrayRef<Attribute> operands,+                             const CalculationT &calculate) {+  assert(operands.size() == 3 && "ternary op takes three operands");+  if (!operands[0] || !operands[1] || !operands[2])+    return {};+  if (operands[0].getType() != operands[1].getType())+    return {};+  if (operands[0].getType() != operands[2].getType())+    return {};++  if (operands[0].isa<AttrElementT>() && operands[1].isa<AttrElementT>() &&+      operands[2].isa<AttrElementT>()) {+    auto fst = operands[0].cast<AttrElementT>();+    auto snd = operands[1].cast<AttrElementT>();+    auto trd = operands[2].cast<AttrElementT>();++    return AttrElementT::get(+        fst.getType(),+        calculate(fst.getValue(), snd.getValue(), trd.getValue()));+  }+  if (operands[0].isa<SplatElementsAttr>() &&+      operands[1].isa<SplatElementsAttr>() &&+      operands[2].isa<SplatElementsAttr>()) {+    // Operands are splats so we can avoid expanding the values out and+    // just fold based on the splat value.+    auto fst = operands[0].cast<SplatElementsAttr>();+    auto snd = operands[1].cast<SplatElementsAttr>();+    auto trd = operands[2].cast<SplatElementsAttr>();++    auto elementResult = calculate(fst.getSplatValue<ElementValueT>(),+                                   snd.getSplatValue<ElementValueT>(),+                                   trd.getSplatValue<ElementValueT>());+    return DenseElementsAttr::get(fst.getType(), elementResult);+  }+  if (operands[0].isa<ElementsAttr>() && operands[1].isa<ElementsAttr>() &&+      operands[2].isa<ElementsAttr>()) {+    // Operands are ElementsAttr-derived; perform an element-wise fold by+    // expanding the values.+    auto fst = operands[0].cast<ElementsAttr>();+    auto snd = operands[1].cast<ElementsAttr>();+    auto trd = operands[2].cast<ElementsAttr>();++    auto fstIt = fst.getValues<ElementValueT>().begin();+    auto sndIt = snd.getValues<ElementValueT>().begin();+    auto trdIt = trd.getValues<ElementValueT>().begin();+    SmallVector<ElementValueT, 4> elementResults;+    elementResults.reserve(fst.getNumElements());+    for (size_t i = 0, e = fst.getNumElements(); i < e;+         ++i, ++fstIt, ++sndIt, ++trdIt)+      elementResults.push_back(calculate(*fstIt, *sndIt, *trdIt));+    return DenseElementsAttr::get(fst.getType(), elementResults);+  }+  return {};+}++struct constant_int_all_ones_matcher {+  bool match(Operation *op) {+    APInt value;+    return mlir::detail::constant_int_op_binder(&value).match(op) &&+           value.isAllOnesValue();+  }+};++} // anonymous namespace++//===---------------------------------------------------------------------===//+// LLHD Trait Helper Functions+//===---------------------------------------------------------------------===//++static bool sameKindArbitraryWidth(Type lhsType, Type rhsType) {+  return (lhsType.getKind() == rhsType.getKind()) &&+         (!lhsType.isa<ShapedType>() ||+          (lhsType.cast<ShapedType>().getElementType() ==+           rhsType.cast<ShapedType>().getElementType()));+}++//===---------------------------------------------------------------------===//+// LLHD Operations+//===---------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// ConstOp+//===----------------------------------------------------------------------===//++static ParseResult parseConstOp(OpAsmParser &parser, OperationState &result) {+  Attribute val;+  Type type;+  if (parser.parseAttribute(val, "value", result.attributes) ||+      parser.parseOptionalAttrDict(result.attributes))+    return failure();+  // parse the type for attributes that do not print the type by default+  if (parser.parseOptionalColon().value ||+      !parser.parseOptionalType(type).hasValue())+    type = val.getType();+  return parser.addTypeToList(val.getType(), result.types);+}++static void print(OpAsmPrinter &printer, llhd::ConstOp op) {+  printer << op.getOperationName() << " ";+  // The custom time attribute is not printing the attribute type by default for+  // some reason. Work around by printing the attribute without type, explicitly+  // followed by the operation type+  printer.printAttributeWithoutType(op.valueAttr());+  printer.printOptionalAttrDict(op.getAttrs(), {"value"});+  printer << " : ";+  printer.printType(op.getType());+}++OpFoldResult llhd::ConstOp::fold(ArrayRef<Attribute> operands) {+  assert(operands.empty() && "const has no operands");+  return value();+}++//===----------------------------------------------------------------------===//+// DextsOp+//===----------------------------------------------------------------------===//++unsigned llhd::DextsOp::getSliceWidth() {+  auto resultTy = result().getType();+  if (resultTy.isSignlessInteger()) {+    return resultTy.getIntOrFloatBitWidth();+  } else if (auto sigRes = resultTy.dyn_cast<llhd::SigType>()) {+    return sigRes.getUnderlyingType().getIntOrFloatBitWidth();+  } else if (auto vecRes = resultTy.dyn_cast<VectorType>()) {+    return vecRes.getNumElements();+  }+  return 0;+}++unsigned llhd::DextsOp::getTargetWidth() {+  auto targetTy = target().getType();+  if (targetTy.isSignlessInteger()) {+    return targetTy.getIntOrFloatBitWidth();+  } else if (auto sigRes = targetTy.dyn_cast<llhd::SigType>()) {+    return sigRes.getUnderlyingType().getIntOrFloatBitWidth();+  } else if (auto vecRes = targetTy.dyn_cast<VectorType>()) {+    return vecRes.getNumElements();+  }+  return 0;+}++//===----------------------------------------------------------------------===//+// NegOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::NegOp::fold(ArrayRef<Attribute> operands) {+  return constFoldUnaryOp<IntegerAttr>(operands, [](APInt a) { return -a; });+}++//===----------------------------------------------------------------------===//+// SModOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::SModOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.smod(x, 1) -> 0+  if (matchPattern(rhs(), m_One()))+    return Builder(getContext()).getZeroAttr(getType());++  /// llhd.smod(0, x) -> 0+  if (matchPattern(lhs(), m_Zero()))+    return Builder(getContext()).getZeroAttr(getType());++  /// llhs.smod(x,x) -> 0+  if (lhs() == rhs())+    return Builder(getContext()).getZeroAttr(getType());++  return constFoldBinaryOp<IntegerAttr>(operands, [](APInt lhs, APInt rhs) {+    APInt result = lhs.srem(rhs);+    if ((lhs.isNegative() && rhs.isNonNegative()) ||+        (lhs.isNonNegative() && rhs.isNegative())) {+      result += rhs;+    }+    return result;+  });+}++//===----------------------------------------------------------------------===//+// NotOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::NotOp::fold(ArrayRef<Attribute> operands) {+  return constFoldUnaryOp<IntegerAttr>(operands, [](APInt a) { return ~a; });+}++//===----------------------------------------------------------------------===//+// AndOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::AndOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.and(x, 0) -> 0+  if (matchPattern(rhs(), m_Zero()))+    return rhs();++  /// llhd.and(x, all_bits_set) -> x+  if (matchPattern(rhs(), constant_int_all_ones_matcher()))+    return lhs();++  // llhd.and(x, x) -> x+  if (rhs() == lhs())+    return rhs();++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a & b; });+}++//===----------------------------------------------------------------------===//+// OrOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::OrOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.or(x, 0) -> x+  if (matchPattern(rhs(), m_Zero()))+    return lhs();++  /// llhd.or(x, all_bits_set) -> all_bits_set+  if (matchPattern(rhs(), constant_int_all_ones_matcher()))+    return rhs();++  // llhd.or(x, x) -> x+  if (rhs() == lhs())+    return rhs();++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a | b; });+}++//===----------------------------------------------------------------------===//+// XorOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::XorOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.xor(x, 0) -> x+  if (matchPattern(rhs(), m_Zero()))+    return lhs();++  /// llhs.xor(x,x) -> 0+  if (lhs() == rhs())+    return Builder(getContext()).getZeroAttr(getType());++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a ^ b; });+}++//===----------------------------------------------------------------------===//+// ShlOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::ShlOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.shl(base, hidden, 0) -> base+  if (matchPattern(amount(), m_Zero()))+    return base();++  return constFoldTernaryOp<IntegerAttr>(+      operands, [](APInt base, APInt hidden, APInt amt) {+        base <<= amt;+        base += hidden.getHiBits(amt.getZExtValue());+        return base;+      });+}++static LogicalResult verify(llhd::ShlOp op) {+  if (op.base().getType() != op.result().getType()) {+    op.emitError("The output of the Shl operation is required to have the "+                 "same type as the base value (first operand), (")+        << op.base().getType() << " vs. " << op.result().getType() << ")";+    return failure();+  }++  // TODO: verify that T and Th only differ in the number of bits or elements++  return success();+}++//===----------------------------------------------------------------------===//+// ShrOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::ShrOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.shl(base, hidden, 0) -> base+  if (matchPattern(amount(), m_Zero()))+    return base();++  return constFoldTernaryOp<IntegerAttr>(+      operands, [](APInt base, APInt hidden, APInt amt) {+        base = base.getHiBits(base.getBitWidth() - amt.getZExtValue());+        hidden = hidden.getLoBits(amt.getZExtValue());+        hidden <<= base.getBitWidth() - amt.getZExtValue();+        return base + hidden;+      });+}++static LogicalResult verify(llhd::ShrOp op) {+  if (op.base().getType() != op.result().getType()) {+    op.emitError("The output of the Shr operation is required to have the "+                 "same type as the base value (first operand), (")+        << op.base().getType() << " vs. " << op.result().getType() << ")";+    return failure();+  }++  // TODO: verify that T and Th only differ in the number of bits or elements++  return success();+}++//===----------------------------------------------------------------------===//+// WaitOp+//===----------------------------------------------------------------------===//++// Implement this operation for the BranchOpInterface+Optional<MutableOperandRange>+llhd::WaitOp::getMutableSuccessorOperands(unsigned index) {+  assert(index == 0 && "invalid successor index");+  return destOpsMutable();+}++//===----------------------------------------------------------------------===//+// EntityOp+//===----------------------------------------------------------------------===//++/// Parse an argument list of an entity operation.+/// The argument list and argument types are returned in args and argTypes+/// respectively.+static ParseResult+parseArgumentList(OpAsmParser &parser,+                  SmallVectorImpl<OpAsmParser::OperandType> &args,+                  SmallVectorImpl<Type> &argTypes) {+  if (parser.parseLParen())+    return failure();++  do {+    OpAsmParser::OperandType argument;+    Type argType;+    if (succeeded(parser.parseOptionalRegionArgument(argument))) {+      if (!argument.name.empty() && succeeded(parser.parseColonType(argType))) {+        args.push_back(argument);+        argTypes.push_back(argType);+      }+    }+  } while (succeeded(parser.parseOptionalComma()));++  if (parser.parseRParen())+    return failure();++  return success();+}++/// parse an entity signature with syntax:+/// (%arg0 : T0, %arg1 : T1, <...>) -> (%out0 : T0, %out1 : T1, <...>)+static ParseResult+parseEntitySignature(OpAsmParser &parser, OperationState &result,+                     SmallVectorImpl<OpAsmParser::OperandType> &args,+                     SmallVectorImpl<Type> &argTypes) {+  if (parseArgumentList(parser, args, argTypes))+    return failure();+  // create the integer attribute with the number of inputs.+  IntegerAttr insAttr = parser.getBuilder().getI64IntegerAttr(args.size());+  result.addAttribute("ins", insAttr);+  if (parser.parseArrow() || parseArgumentList(parser, args, argTypes))+    return failure();++  return success();+}++static ParseResult parseEntityOp(OpAsmParser &parser, OperationState &result) {+  StringAttr entityName;+  SmallVector<OpAsmParser::OperandType, 4> args;+  SmallVector<Type, 4> argTypes;++  if (parser.parseSymbolName(entityName, SymbolTable::getSymbolAttrName(),+                             result.attributes))+    return failure();++  parseEntitySignature(parser, result, args, argTypes);++  if (parser.parseOptionalAttrDictWithKeyword(result.attributes))+    return failure();++  auto type = parser.getBuilder().getFunctionType(argTypes, llvm::None);+  result.addAttribute(mlir::llhd::EntityOp::getTypeAttrName(),+                      TypeAttr::get(type));++  auto *body = result.addRegion();+  parser.parseRegion(*body, args, argTypes);+  llhd::EntityOp::ensureTerminator(*body, parser.getBuilder(), result.location);+  return success();+}++static void printArgumentList(OpAsmPrinter &printer,+                              std::vector<BlockArgument> args) {+  printer << "(";+  for (size_t i = 0, e = args.size(); i < e; ++i) {+    printer << args[i] << " : ";+    printer.printType(args[i].getType());+    if (i < args.size() - 1)+      printer << ", ";+  }+  printer << ")";+}++static void print(OpAsmPrinter &printer, llhd::EntityOp op) {+  std::vector<BlockArgument> ins, outs;+  uint64_t n_ins = op.insAttr().getInt();+  for (uint64_t i = 0; i < op.body().front().getArguments().size(); ++i) {+    // no furter verification for the attribute type is required, already+    // handled by verify.+    if (i < n_ins) {+      ins.push_back(op.body().front().getArguments()[i]);+    } else {+      outs.push_back(op.body().front().getArguments()[i]);+    }+  }+  auto entityName =+      op.getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()).getValue();+  printer << op.getOperationName() << " ";+  printer.printSymbolName(entityName);+  printer << " ";+  printArgumentList(printer, ins);+  printer << " -> ";+  printArgumentList(printer, outs);+  printer.printOptionalAttrDictWithKeyword(+      op.getAttrs(),+      /*elidedAttrs =*/{SymbolTable::getSymbolAttrName(),+                        llhd::EntityOp::getTypeAttrName(), "ins"});+  printer.printRegion(op.body(), false, false);+}++static LogicalResult verify(llhd::EntityOp op) {+  uint64_t numArgs = op.getNumArguments();+  uint64_t nIns = op.insAttr().getInt();+  // check that there is at most one flag for each argument+  if (numArgs < nIns) {+    op.emitError("Cannot have more inputs than arguments, expected at most ")+        << numArgs << " but got: " << nIns;+    return failure();+  }+  return success();+}++LogicalResult mlir::llhd::EntityOp::verifyType() {+  // Fail if function returns any values. An entity's outputs are specially+  // marked arguments.+  if (this->getNumResults() > 0) {+    this->emitOpError("an entity cannot have return types.");+    return failure();+  }+  // Check that all operands are of signal type+  for (int i = 0, e = this->getNumFuncArguments(); i < e; ++i) {+    if (!llhd::SigType::kindof(this->getArgument(i).getType().getKind())) {+      this->emitOpError("usage of invalid argument type. Got ")+          << this->getArgument(i).getType() << ", expected LLHD signal type";+      return failure();+    }+  }+  return success();+}++LogicalResult mlir::llhd::EntityOp::verifyBody() {+  // Body must not be empty.+  if (this->isExternal()) {+    this->emitOpError("defining external entity with the entity instruction "+                      "is not allowed, use the intended instruction instead.");+    return failure();+  }++  // check signal names are unique+  llvm::StringMap<bool> sigMap;+  llvm::StringMap<bool> instMap;+  auto walkResult = walk([&sigMap, &instMap](Operation *op) -> WalkResult {+    if (auto sigOp = dyn_cast<SigOp>(op)) {+      if (sigMap[sigOp.name()]) {+        return sigOp.emitError("Redefinition of signal named '")+               << sigOp.name() << "'!";+      }+      sigMap.insert_or_assign(sigOp.name(), true);+    } else if (auto instOp = dyn_cast<InstOp>(op)) {+      if (instMap[instOp.name()]) {+        return instOp.emitError("Redefinition of instance named '")+               << instOp.name() << "'!";+      }+      instMap.insert_or_assign(instOp.name(), true);+    }+    return WalkResult::advance();+  });++  return failure(walkResult.wasInterrupted());+}++Region *llhd::EntityOp::getCallableRegion() {+  return isExternal() ? nullptr : &getBody();+}++ArrayRef<Type> llhd::EntityOp::getCallableResults() {+  return getType().getResults();+}++//===----------------------------------------------------------------------===//+// ProcOp+//===----------------------------------------------------------------------===//++LogicalResult mlir::llhd::ProcOp::verifyType() {+  // Fail if function returns more than zero values. This is because the+  // outputs of a process are specially marked arguments.+  if (this->getNumResults() > 0) {+    this->emitOpError(+        "process has more than zero return types, this is not allowed");+    return failure();+  }+  // Check that all operands are of signal type+  for (int i = 0, e = this->getNumFuncArguments(); i < e; ++i) {+    if (!llhd::SigType::kindof(this->getArgument(i).getType().getKind())) {+      this->emitOpError("usage of invalid argument type, was ")+          << this->getArgument(i).getType() << ", expected LLHD signal type";+      return failure();+    }+  }+  return success();+}++LogicalResult mlir::llhd::ProcOp::verifyBody() {+  // Body must not be empty, this indicates an external process. We use+  // another instruction to reference external processes.+  if (this->isExternal()) {+    this->emitOpError("defining external processes with the proc instruction "+                      "is not allowed, use the intended instruction instead.");+    return failure();+  }+  return success();+}++static LogicalResult verify(llhd::ProcOp op) {+  // Check that the ins attribute is smaller or equal the number of+  // arguments+  uint64_t numArgs = op.getNumArguments();+  uint64_t numIns = op.insAttr().getInt();+  if (numArgs < numIns) {+    op.emitOpError("Cannot have more inputs than arguments, expected at most ")+        << numArgs << ", got " << numIns;+    return failure();+  }+  return success();+}++static ParseResult+parseProcArgumentList(OpAsmParser &parser, SmallVectorImpl<Type> &argTypes,+                      SmallVectorImpl<OpAsmParser::OperandType> &argNames) {+  if (parser.parseLParen())+    return failure();++  // The argument list either has to consistently have ssa-id's followed by+  // types, or just be a type list.  It isn't ok to sometimes have SSA ID's+  // and sometimes not.+  auto parseArgument = [&]() -> ParseResult {+    llvm::SMLoc loc = parser.getCurrentLocation();++    // Parse argument name if present.+    OpAsmParser::OperandType argument;+    Type argumentType;+    if (succeeded(parser.parseOptionalRegionArgument(argument)) &&+        !argument.name.empty()) {+      // Reject this if the preceding argument was missing a name.+      if (argNames.empty() && !argTypes.empty())+        return parser.emitError(loc, "expected type instead of SSA identifier");+      argNames.push_back(argument);++      if (parser.parseColonType(argumentType))+        return failure();+    } else if (!argNames.empty()) {+      // Reject this if the preceding argument had a name.+      return parser.emitError(loc, "expected SSA identifier");+    } else if (parser.parseType(argumentType)) {+      return failure();+    }++    // Add the argument type.+    argTypes.push_back(argumentType);++    return success();+  };++  // Parse the function arguments.+  if (failed(parser.parseOptionalRParen())) {+    do {+      unsigned numTypedArguments = argTypes.size();+      if (parseArgument())+        return failure();++      llvm::SMLoc loc = parser.getCurrentLocation();+      if (argTypes.size() == numTypedArguments &&+          succeeded(parser.parseOptionalComma()))+        return parser.emitError(loc, "variadic arguments are not allowed");+    } while (succeeded(parser.parseOptionalComma()));+    parser.parseRParen();+  }++  return success();+}++static ParseResult parseProcOp(OpAsmParser &parser, OperationState &result) {+  StringAttr procName;+  SmallVector<OpAsmParser::OperandType, 8> argNames;+  SmallVector<Type, 8> argTypes;+  Builder &builder = parser.getBuilder();++  if (parser.parseSymbolName(procName, SymbolTable::getSymbolAttrName(),+                             result.attributes))+    return failure();++  if (parseProcArgumentList(parser, argTypes, argNames))+    return failure();++  result.addAttribute("ins", builder.getI64IntegerAttr(argTypes.size()));+  parser.parseArrow();++  if (parseProcArgumentList(parser, argTypes, argNames))+    return failure();++  auto type = builder.getFunctionType(argTypes, llvm::None);+  result.addAttribute(mlir::llhd::ProcOp::getTypeAttrName(),+                      TypeAttr::get(type));++  auto *body = result.addRegion();+  parser.parseRegion(*body, argNames,+                     argNames.empty() ? ArrayRef<Type>() : argTypes);++  return success();+}++/// Print the signature of the `proc` unit. Assumes that it passed the+/// verification.+static void printProcArguments(OpAsmPrinter &p, Operation *op,+                               ArrayRef<Type> types, uint64_t numIns) {+  Region &body = op->getRegion(0);+  auto printList = [&](unsigned i, unsigned max) -> void {+    for (; i < max; ++i) {+      p.printOperand(body.front().getArgument(i));

nit: You can stream operands in directly, just like types.

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Dialect/LLHD/IR/LLHDOps.h"+#include "circt/Dialect/LLHD/IR/LLHDDialect.h"+#include "mlir/Dialect/CommonFolders.h"+#include "mlir/IR/Attributes.h"+#include "mlir/IR/Matchers.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/OpImplementation.h"+#include "mlir/IR/PatternMatch.h"+#include "mlir/IR/Region.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/IR/Types.h"+#include "mlir/IR/Value.h"+#include "mlir/Support/LogicalResult.h"+#include "llvm/ADT/ArrayRef.h"+#include "llvm/ADT/SmallVector.h"+#include "llvm/ADT/StringMap.h"++using namespace mlir;++namespace {++template <class AttrElementT,+          class ElementValueT = typename AttrElementT::ValueType,+          class CalculationT = function_ref<ElementValueT(ElementValueT)>>+Attribute constFoldUnaryOp(ArrayRef<Attribute> operands,+                           const CalculationT &calculate) {+  assert(operands.size() == 1 && "unary op takes one operand");+  if (!operands[0])+    return {};++  if (auto val = operands[0].dyn_cast<AttrElementT>()) {+    return AttrElementT::get(val.getType(), calculate(val.getValue()));+  } else if (auto val = operands[0].dyn_cast<SplatElementsAttr>()) {+    // Operand is a splat so we can avoid expanding the value out and+    // just fold based on the splat value.+    auto elementResult = calculate(val.getSplatValue<ElementValueT>());+    return DenseElementsAttr::get(val.getType(), elementResult);+  }+  if (auto val = operands[0].dyn_cast<ElementsAttr>()) {+    // Operand is ElementsAttr-derived; perform an element-wise fold by+    // expanding the values.+    auto valIt = val.getValues<ElementValueT>().begin();+    SmallVector<ElementValueT, 4> elementResults;+    elementResults.reserve(val.getNumElements());+    for (size_t i = 0, e = val.getNumElements(); i < e; ++i, ++valIt)+      elementResults.push_back(calculate(*valIt));+    return DenseElementsAttr::get(val.getType(), elementResults);+  }+  return {};+}++template <class AttrElementT,+          class ElementValueT = typename AttrElementT::ValueType,+          class CalculationT = function_ref<+              ElementValueT(ElementValueT, ElementValueT, ElementValueT)>>+Attribute constFoldTernaryOp(ArrayRef<Attribute> operands,+                             const CalculationT &calculate) {+  assert(operands.size() == 3 && "ternary op takes three operands");+  if (!operands[0] || !operands[1] || !operands[2])+    return {};+  if (operands[0].getType() != operands[1].getType())+    return {};+  if (operands[0].getType() != operands[2].getType())+    return {};++  if (operands[0].isa<AttrElementT>() && operands[1].isa<AttrElementT>() &&+      operands[2].isa<AttrElementT>()) {+    auto fst = operands[0].cast<AttrElementT>();+    auto snd = operands[1].cast<AttrElementT>();+    auto trd = operands[2].cast<AttrElementT>();++    return AttrElementT::get(+        fst.getType(),+        calculate(fst.getValue(), snd.getValue(), trd.getValue()));+  }+  if (operands[0].isa<SplatElementsAttr>() &&+      operands[1].isa<SplatElementsAttr>() &&+      operands[2].isa<SplatElementsAttr>()) {+    // Operands are splats so we can avoid expanding the values out and+    // just fold based on the splat value.+    auto fst = operands[0].cast<SplatElementsAttr>();+    auto snd = operands[1].cast<SplatElementsAttr>();+    auto trd = operands[2].cast<SplatElementsAttr>();++    auto elementResult = calculate(fst.getSplatValue<ElementValueT>(),+                                   snd.getSplatValue<ElementValueT>(),+                                   trd.getSplatValue<ElementValueT>());+    return DenseElementsAttr::get(fst.getType(), elementResult);+  }+  if (operands[0].isa<ElementsAttr>() && operands[1].isa<ElementsAttr>() &&+      operands[2].isa<ElementsAttr>()) {+    // Operands are ElementsAttr-derived; perform an element-wise fold by+    // expanding the values.+    auto fst = operands[0].cast<ElementsAttr>();+    auto snd = operands[1].cast<ElementsAttr>();+    auto trd = operands[2].cast<ElementsAttr>();++    auto fstIt = fst.getValues<ElementValueT>().begin();+    auto sndIt = snd.getValues<ElementValueT>().begin();+    auto trdIt = trd.getValues<ElementValueT>().begin();+    SmallVector<ElementValueT, 4> elementResults;+    elementResults.reserve(fst.getNumElements());+    for (size_t i = 0, e = fst.getNumElements(); i < e;+         ++i, ++fstIt, ++sndIt, ++trdIt)+      elementResults.push_back(calculate(*fstIt, *sndIt, *trdIt));+    return DenseElementsAttr::get(fst.getType(), elementResults);+  }+  return {};+}++struct constant_int_all_ones_matcher {+  bool match(Operation *op) {+    APInt value;+    return mlir::detail::constant_int_op_binder(&value).match(op) &&+           value.isAllOnesValue();+  }+};++} // anonymous namespace++//===---------------------------------------------------------------------===//+// LLHD Trait Helper Functions+//===---------------------------------------------------------------------===//++static bool sameKindArbitraryWidth(Type lhsType, Type rhsType) {+  return (lhsType.getKind() == rhsType.getKind()) &&+         (!lhsType.isa<ShapedType>() ||+          (lhsType.cast<ShapedType>().getElementType() ==+           rhsType.cast<ShapedType>().getElementType()));+}++//===---------------------------------------------------------------------===//+// LLHD Operations+//===---------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// ConstOp+//===----------------------------------------------------------------------===//++static ParseResult parseConstOp(OpAsmParser &parser, OperationState &result) {+  Attribute val;+  Type type;+  if (parser.parseAttribute(val, "value", result.attributes) ||+      parser.parseOptionalAttrDict(result.attributes))+    return failure();+  // parse the type for attributes that do not print the type by default+  if (parser.parseOptionalColon().value ||+      !parser.parseOptionalType(type).hasValue())+    type = val.getType();+  return parser.addTypeToList(val.getType(), result.types);+}++static void print(OpAsmPrinter &printer, llhd::ConstOp op) {+  printer << op.getOperationName() << " ";+  // The custom time attribute is not printing the attribute type by default for+  // some reason. Work around by printing the attribute without type, explicitly+  // followed by the operation type+  printer.printAttributeWithoutType(op.valueAttr());+  printer.printOptionalAttrDict(op.getAttrs(), {"value"});+  printer << " : ";+  printer.printType(op.getType());+}++OpFoldResult llhd::ConstOp::fold(ArrayRef<Attribute> operands) {+  assert(operands.empty() && "const has no operands");+  return value();+}++//===----------------------------------------------------------------------===//+// DextsOp+//===----------------------------------------------------------------------===//++unsigned llhd::DextsOp::getSliceWidth() {+  auto resultTy = result().getType();+  if (resultTy.isSignlessInteger()) {+    return resultTy.getIntOrFloatBitWidth();+  } else if (auto sigRes = resultTy.dyn_cast<llhd::SigType>()) {+    return sigRes.getUnderlyingType().getIntOrFloatBitWidth();+  } else if (auto vecRes = resultTy.dyn_cast<VectorType>()) {+    return vecRes.getNumElements();+  }+  return 0;+}++unsigned llhd::DextsOp::getTargetWidth() {+  auto targetTy = target().getType();+  if (targetTy.isSignlessInteger()) {+    return targetTy.getIntOrFloatBitWidth();+  } else if (auto sigRes = targetTy.dyn_cast<llhd::SigType>()) {+    return sigRes.getUnderlyingType().getIntOrFloatBitWidth();+  } else if (auto vecRes = targetTy.dyn_cast<VectorType>()) {+    return vecRes.getNumElements();+  }+  return 0;+}++//===----------------------------------------------------------------------===//+// NegOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::NegOp::fold(ArrayRef<Attribute> operands) {+  return constFoldUnaryOp<IntegerAttr>(operands, [](APInt a) { return -a; });+}++//===----------------------------------------------------------------------===//+// SModOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::SModOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.smod(x, 1) -> 0+  if (matchPattern(rhs(), m_One()))+    return Builder(getContext()).getZeroAttr(getType());++  /// llhd.smod(0, x) -> 0+  if (matchPattern(lhs(), m_Zero()))+    return Builder(getContext()).getZeroAttr(getType());++  /// llhs.smod(x,x) -> 0+  if (lhs() == rhs())+    return Builder(getContext()).getZeroAttr(getType());++  return constFoldBinaryOp<IntegerAttr>(operands, [](APInt lhs, APInt rhs) {+    APInt result = lhs.srem(rhs);+    if ((lhs.isNegative() && rhs.isNonNegative()) ||+        (lhs.isNonNegative() && rhs.isNegative())) {+      result += rhs;+    }+    return result;+  });+}++//===----------------------------------------------------------------------===//+// NotOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::NotOp::fold(ArrayRef<Attribute> operands) {+  return constFoldUnaryOp<IntegerAttr>(operands, [](APInt a) { return ~a; });+}++//===----------------------------------------------------------------------===//+// AndOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::AndOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.and(x, 0) -> 0+  if (matchPattern(rhs(), m_Zero()))+    return rhs();++  /// llhd.and(x, all_bits_set) -> x+  if (matchPattern(rhs(), constant_int_all_ones_matcher()))+    return lhs();++  // llhd.and(x, x) -> x+  if (rhs() == lhs())+    return rhs();++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a & b; });+}++//===----------------------------------------------------------------------===//+// OrOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::OrOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.or(x, 0) -> x+  if (matchPattern(rhs(), m_Zero()))+    return lhs();++  /// llhd.or(x, all_bits_set) -> all_bits_set+  if (matchPattern(rhs(), constant_int_all_ones_matcher()))+    return rhs();++  // llhd.or(x, x) -> x+  if (rhs() == lhs())+    return rhs();++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a | b; });+}++//===----------------------------------------------------------------------===//+// XorOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::XorOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.xor(x, 0) -> x+  if (matchPattern(rhs(), m_Zero()))+    return lhs();++  /// llhs.xor(x,x) -> 0+  if (lhs() == rhs())+    return Builder(getContext()).getZeroAttr(getType());++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a ^ b; });+}++//===----------------------------------------------------------------------===//+// ShlOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::ShlOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.shl(base, hidden, 0) -> base+  if (matchPattern(amount(), m_Zero()))+    return base();++  return constFoldTernaryOp<IntegerAttr>(+      operands, [](APInt base, APInt hidden, APInt amt) {+        base <<= amt;+        base += hidden.getHiBits(amt.getZExtValue());+        return base;+      });+}++static LogicalResult verify(llhd::ShlOp op) {+  if (op.base().getType() != op.result().getType()) {+    op.emitError("The output of the Shl operation is required to have the "+                 "same type as the base value (first operand), (")+        << op.base().getType() << " vs. " << op.result().getType() << ")";+    return failure();+  }++  // TODO: verify that T and Th only differ in the number of bits or elements++  return success();+}++//===----------------------------------------------------------------------===//+// ShrOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::ShrOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.shl(base, hidden, 0) -> base+  if (matchPattern(amount(), m_Zero()))+    return base();++  return constFoldTernaryOp<IntegerAttr>(+      operands, [](APInt base, APInt hidden, APInt amt) {+        base = base.getHiBits(base.getBitWidth() - amt.getZExtValue());+        hidden = hidden.getLoBits(amt.getZExtValue());+        hidden <<= base.getBitWidth() - amt.getZExtValue();+        return base + hidden;+      });+}++static LogicalResult verify(llhd::ShrOp op) {+  if (op.base().getType() != op.result().getType()) {+    op.emitError("The output of the Shr operation is required to have the "+                 "same type as the base value (first operand), (")+        << op.base().getType() << " vs. " << op.result().getType() << ")";+    return failure();+  }++  // TODO: verify that T and Th only differ in the number of bits or elements++  return success();+}++//===----------------------------------------------------------------------===//+// WaitOp+//===----------------------------------------------------------------------===//++// Implement this operation for the BranchOpInterface+Optional<MutableOperandRange>+llhd::WaitOp::getMutableSuccessorOperands(unsigned index) {+  assert(index == 0 && "invalid successor index");+  return destOpsMutable();+}++//===----------------------------------------------------------------------===//+// EntityOp+//===----------------------------------------------------------------------===//++/// Parse an argument list of an entity operation.+/// The argument list and argument types are returned in args and argTypes+/// respectively.+static ParseResult+parseArgumentList(OpAsmParser &parser,+                  SmallVectorImpl<OpAsmParser::OperandType> &args,+                  SmallVectorImpl<Type> &argTypes) {+  if (parser.parseLParen())+    return failure();++  do {+    OpAsmParser::OperandType argument;+    Type argType;+    if (succeeded(parser.parseOptionalRegionArgument(argument))) {+      if (!argument.name.empty() && succeeded(parser.parseColonType(argType))) {+        args.push_back(argument);+        argTypes.push_back(argType);+      }+    }+  } while (succeeded(parser.parseOptionalComma()));++  if (parser.parseRParen())+    return failure();++  return success();+}++/// parse an entity signature with syntax:+/// (%arg0 : T0, %arg1 : T1, <...>) -> (%out0 : T0, %out1 : T1, <...>)+static ParseResult+parseEntitySignature(OpAsmParser &parser, OperationState &result,+                     SmallVectorImpl<OpAsmParser::OperandType> &args,+                     SmallVectorImpl<Type> &argTypes) {+  if (parseArgumentList(parser, args, argTypes))+    return failure();+  // create the integer attribute with the number of inputs.+  IntegerAttr insAttr = parser.getBuilder().getI64IntegerAttr(args.size());+  result.addAttribute("ins", insAttr);+  if (parser.parseArrow() || parseArgumentList(parser, args, argTypes))+    return failure();++  return success();+}++static ParseResult parseEntityOp(OpAsmParser &parser, OperationState &result) {+  StringAttr entityName;+  SmallVector<OpAsmParser::OperandType, 4> args;+  SmallVector<Type, 4> argTypes;++  if (parser.parseSymbolName(entityName, SymbolTable::getSymbolAttrName(),+                             result.attributes))+    return failure();++  parseEntitySignature(parser, result, args, argTypes);++  if (parser.parseOptionalAttrDictWithKeyword(result.attributes))+    return failure();++  auto type = parser.getBuilder().getFunctionType(argTypes, llvm::None);+  result.addAttribute(mlir::llhd::EntityOp::getTypeAttrName(),+                      TypeAttr::get(type));++  auto *body = result.addRegion();+  parser.parseRegion(*body, args, argTypes);+  llhd::EntityOp::ensureTerminator(*body, parser.getBuilder(), result.location);+  return success();+}++static void printArgumentList(OpAsmPrinter &printer,+                              std::vector<BlockArgument> args) {+  printer << "(";+  for (size_t i = 0, e = args.size(); i < e; ++i) {+    printer << args[i] << " : ";+    printer.printType(args[i].getType());+    if (i < args.size() - 1)+      printer << ", ";+  }+  printer << ")";+}++static void print(OpAsmPrinter &printer, llhd::EntityOp op) {+  std::vector<BlockArgument> ins, outs;+  uint64_t n_ins = op.insAttr().getInt();+  for (uint64_t i = 0; i < op.body().front().getArguments().size(); ++i) {+    // no furter verification for the attribute type is required, already+    // handled by verify.+    if (i < n_ins) {+      ins.push_back(op.body().front().getArguments()[i]);+    } else {+      outs.push_back(op.body().front().getArguments()[i]);+    }+  }+  auto entityName =+      op.getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()).getValue();+  printer << op.getOperationName() << " ";+  printer.printSymbolName(entityName);+  printer << " ";+  printArgumentList(printer, ins);+  printer << " -> ";+  printArgumentList(printer, outs);+  printer.printOptionalAttrDictWithKeyword(+      op.getAttrs(),+      /*elidedAttrs =*/{SymbolTable::getSymbolAttrName(),+                        llhd::EntityOp::getTypeAttrName(), "ins"});+  printer.printRegion(op.body(), false, false);+}++static LogicalResult verify(llhd::EntityOp op) {+  uint64_t numArgs = op.getNumArguments();+  uint64_t nIns = op.insAttr().getInt();+  // check that there is at most one flag for each argument+  if (numArgs < nIns) {+    op.emitError("Cannot have more inputs than arguments, expected at most ")+        << numArgs << " but got: " << nIns;+    return failure();+  }+  return success();+}++LogicalResult mlir::llhd::EntityOp::verifyType() {+  // Fail if function returns any values. An entity's outputs are specially+  // marked arguments.+  if (this->getNumResults() > 0) {+    this->emitOpError("an entity cannot have return types.");+    return failure();+  }+  // Check that all operands are of signal type+  for (int i = 0, e = this->getNumFuncArguments(); i < e; ++i) {+    if (!llhd::SigType::kindof(this->getArgument(i).getType().getKind())) {

Please use .isa<> and not kindof.

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Target/Verilog/VerilogPrinter.h"+#include "mlir/Support/LogicalResult.h"++using namespace mlir;++LogicalResult mlir::llhd::VerilogPrinter::printModule(ModuleOp module) {+  bool didFail = false;+  module.walk([&didFail, this](llhd::EntityOp entity) {+    // An EntityOp always has a single block+    Block &entryBlock = entity.body().front();++    // Print the module signature+    out << "module _" << entity.getName();+    if (!entryBlock.args_empty()) {+      out << "(";+      for (unsigned int i = 0; i < entryBlock.getNumArguments(); i++) {+        out << (i > 0 ? ", " : "")+            << (i < entity.ins().getZExtValue() ? "input " : "output ");+        printType(entryBlock.getArgument(i).getType());+        out << " " << getVariableName(entryBlock.getArgument(i));+      }+      out << ")";+    }+    out << ";\n";++    // Print the operations within the entity+    for (auto iter = entryBlock.begin();+         iter != entryBlock.end() && !dyn_cast<llhd::TerminatorOp>(iter);+         iter++) {+      if (failed(printOperation(&(*iter), 4))) {+        emitError(iter->getLoc(), "Operation not supported!");+        didFail = true;+        return;+      }+    }++    out << "endmodule\n";+    // Reset variable name counter as variables only have to be unique within a+    // module+    nextValueNum = 0;+  });+  // if printing of a single operation failed, fail the whole translation+  if (didFail)+    return failure();++  return success();+}++LogicalResult mlir::llhd::VerilogPrinter::printBinaryOp(Operation *inst,+                                                        StringRef opSymbol,+                                                        unsigned indentAmount) {+  // Check that the operation is indeed a binary operation+  if (inst->getNumOperands() != 2) {+    emitError(inst->getLoc(), "This operation does not have two operands!");+    return failure();+  }+  if (inst->getNumResults() != 1) {+    emitError(inst->getLoc(), "This operation does not have one result!");+    return failure();+  }++  // Print the operation+  out.PadToColumn(indentAmount);+  out << "wire ";+  if (failed(printType(inst->getResult(0).getType())))+    return failure();+  out << " " << getVariableName(inst->getResult(0)) << " = "+      << getVariableName(inst->getOperand(0)) << " " << opSymbol << " "+      << getVariableName(inst->getOperand(1)) << ";\n";+  return success();+}++LogicalResult mlir::llhd::VerilogPrinter::printSignedBinaryOp(+    Operation *inst, StringRef opSymbol, unsigned indentAmount) {+  // Note: the wire is not declared as signed, because if you have the result+  // of two signed operation as an input to an unsigned operation, it would+  // perform the signed version (sometimes this is not a problem because it's+  // the same, but e.g. for modulo it would make a difference). Alternatively+  // we could use $unsigned at every operation where it makes a difference,+  // but that would look a lot uglier, we could also track which variable is+  // signed and which unsigned and only do the conversion when necessary, but+  // that is more effort. Also, because we have the explicit $signed at every+  // signed operation, this isn't a problem for further signed operations.+  //+  // Check that the operation is indeed a binary operation+  if (inst->getNumOperands() != 2) {+    emitError(inst->getLoc(), "This operation does not have two operands!");+    return failure();+  }+  if (inst->getNumResults() != 1) {+    emitError(inst->getLoc(), "This operation does not have one result!");+    return failure();+  }++  // Print the operation+  out.PadToColumn(indentAmount);+  out << "wire ";+  if (failed(printType(inst->getResult(0).getType())))+    return failure();+  out << " " << getVariableName(inst->getResult(0)) << " = $signed("+      << getVariableName(inst->getOperand(0)) << ") " << opSymbol << " $signed("+      << getVariableName(inst->getOperand(1)) << ");\n";+  return success();+}++LogicalResult mlir::llhd::VerilogPrinter::printUnaryOp(Operation *inst,+                                                       StringRef opSymbol,+                                                       unsigned indentAmount) {+  // Check that the operation is indeed a unary operation+  if (inst->getNumOperands() != 1) {+    emitError(inst->getLoc(),+              "This operation does not have exactly one operand!");+    return failure();+  }+  if (inst->getNumResults() != 1) {+    emitError(inst->getLoc(), "This operation does not have one result!");+    return failure();+  }++  // Print the operation+  out.PadToColumn(indentAmount);+  out << "wire ";+  if (failed(printType(inst->getResult(0).getType())))+    return failure();+  out << " " << getVariableName(inst->getResult(0)) << " = " << opSymbol+      << getVariableName(inst->getOperand(0)) << ";\n";+  return success();+}++LogicalResult+mlir::llhd::VerilogPrinter::printOperation(Operation *inst,+                                           unsigned indentAmount) {+  if (auto op = dyn_cast<llhd::ConstOp>(inst)) {+    if (op.value().getKind() == StandardAttributes::Integer) {+      // Integer constant+      out.PadToColumn(indentAmount);+      out << "wire ";+      if (failed(printType(inst->getResult(0).getType())))+        return failure();+      out << " " << getVariableName(inst->getResult(0)) << " = "+          << op.getResult().getType().getIntOrFloatBitWidth() << "'d"+          << op.value().cast<IntegerAttr>().getValue().getZExtValue() << ";\n";+      return success();+    } else if (llhd::TimeAttr::kindof(op.value().getKind())) {+      // Time Constant+      auto timeAttr = op.value().cast<TimeAttr>();+      if (timeAttr.getTime() == 0 && timeAttr.getDelta() != 1) {+        emitError(op.getLoc(),+                  "Not possible to translate a time attribute with 0 real "+                  "time and non-1 delta.");+        return failure();+      } else {

nit: Drop else after return.

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Dialect/LLHD/IR/LLHDOps.h"+#include "circt/Dialect/LLHD/IR/LLHDDialect.h"+#include "mlir/Dialect/CommonFolders.h"+#include "mlir/IR/Attributes.h"+#include "mlir/IR/Matchers.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/OpImplementation.h"+#include "mlir/IR/PatternMatch.h"+#include "mlir/IR/Region.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/IR/Types.h"+#include "mlir/IR/Value.h"+#include "mlir/Support/LogicalResult.h"+#include "llvm/ADT/ArrayRef.h"+#include "llvm/ADT/SmallVector.h"+#include "llvm/ADT/StringMap.h"++using namespace mlir;++namespace {++template <class AttrElementT,+          class ElementValueT = typename AttrElementT::ValueType,+          class CalculationT = function_ref<ElementValueT(ElementValueT)>>+Attribute constFoldUnaryOp(ArrayRef<Attribute> operands,+                           const CalculationT &calculate) {+  assert(operands.size() == 1 && "unary op takes one operand");+  if (!operands[0])+    return {};++  if (auto val = operands[0].dyn_cast<AttrElementT>()) {+    return AttrElementT::get(val.getType(), calculate(val.getValue()));+  } else if (auto val = operands[0].dyn_cast<SplatElementsAttr>()) {+    // Operand is a splat so we can avoid expanding the value out and+    // just fold based on the splat value.+    auto elementResult = calculate(val.getSplatValue<ElementValueT>());+    return DenseElementsAttr::get(val.getType(), elementResult);+  }+  if (auto val = operands[0].dyn_cast<ElementsAttr>()) {+    // Operand is ElementsAttr-derived; perform an element-wise fold by+    // expanding the values.+    auto valIt = val.getValues<ElementValueT>().begin();+    SmallVector<ElementValueT, 4> elementResults;+    elementResults.reserve(val.getNumElements());+    for (size_t i = 0, e = val.getNumElements(); i < e; ++i, ++valIt)+      elementResults.push_back(calculate(*valIt));+    return DenseElementsAttr::get(val.getType(), elementResults);+  }+  return {};+}++template <class AttrElementT,+          class ElementValueT = typename AttrElementT::ValueType,+          class CalculationT = function_ref<+              ElementValueT(ElementValueT, ElementValueT, ElementValueT)>>+Attribute constFoldTernaryOp(ArrayRef<Attribute> operands,+                             const CalculationT &calculate) {+  assert(operands.size() == 3 && "ternary op takes three operands");+  if (!operands[0] || !operands[1] || !operands[2])+    return {};+  if (operands[0].getType() != operands[1].getType())+    return {};+  if (operands[0].getType() != operands[2].getType())+    return {};++  if (operands[0].isa<AttrElementT>() && operands[1].isa<AttrElementT>() &&+      operands[2].isa<AttrElementT>()) {+    auto fst = operands[0].cast<AttrElementT>();+    auto snd = operands[1].cast<AttrElementT>();+    auto trd = operands[2].cast<AttrElementT>();++    return AttrElementT::get(+        fst.getType(),+        calculate(fst.getValue(), snd.getValue(), trd.getValue()));+  }+  if (operands[0].isa<SplatElementsAttr>() &&+      operands[1].isa<SplatElementsAttr>() &&+      operands[2].isa<SplatElementsAttr>()) {+    // Operands are splats so we can avoid expanding the values out and+    // just fold based on the splat value.+    auto fst = operands[0].cast<SplatElementsAttr>();+    auto snd = operands[1].cast<SplatElementsAttr>();+    auto trd = operands[2].cast<SplatElementsAttr>();++    auto elementResult = calculate(fst.getSplatValue<ElementValueT>(),+                                   snd.getSplatValue<ElementValueT>(),+                                   trd.getSplatValue<ElementValueT>());+    return DenseElementsAttr::get(fst.getType(), elementResult);+  }+  if (operands[0].isa<ElementsAttr>() && operands[1].isa<ElementsAttr>() &&+      operands[2].isa<ElementsAttr>()) {+    // Operands are ElementsAttr-derived; perform an element-wise fold by+    // expanding the values.+    auto fst = operands[0].cast<ElementsAttr>();+    auto snd = operands[1].cast<ElementsAttr>();+    auto trd = operands[2].cast<ElementsAttr>();++    auto fstIt = fst.getValues<ElementValueT>().begin();+    auto sndIt = snd.getValues<ElementValueT>().begin();+    auto trdIt = trd.getValues<ElementValueT>().begin();+    SmallVector<ElementValueT, 4> elementResults;+    elementResults.reserve(fst.getNumElements());+    for (size_t i = 0, e = fst.getNumElements(); i < e;+         ++i, ++fstIt, ++sndIt, ++trdIt)+      elementResults.push_back(calculate(*fstIt, *sndIt, *trdIt));+    return DenseElementsAttr::get(fst.getType(), elementResults);+  }+  return {};+}++struct constant_int_all_ones_matcher {+  bool match(Operation *op) {+    APInt value;+    return mlir::detail::constant_int_op_binder(&value).match(op) &&+           value.isAllOnesValue();+  }+};++} // anonymous namespace++//===---------------------------------------------------------------------===//+// LLHD Trait Helper Functions+//===---------------------------------------------------------------------===//++static bool sameKindArbitraryWidth(Type lhsType, Type rhsType) {+  return (lhsType.getKind() == rhsType.getKind()) &&+         (!lhsType.isa<ShapedType>() ||+          (lhsType.cast<ShapedType>().getElementType() ==+           rhsType.cast<ShapedType>().getElementType()));+}++//===---------------------------------------------------------------------===//+// LLHD Operations+//===---------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// ConstOp+//===----------------------------------------------------------------------===//++static ParseResult parseConstOp(OpAsmParser &parser, OperationState &result) {+  Attribute val;+  Type type;+  if (parser.parseAttribute(val, "value", result.attributes) ||+      parser.parseOptionalAttrDict(result.attributes))+    return failure();+  // parse the type for attributes that do not print the type by default+  if (parser.parseOptionalColon().value ||+      !parser.parseOptionalType(type).hasValue())+    type = val.getType();+  return parser.addTypeToList(val.getType(), result.types);+}++static void print(OpAsmPrinter &printer, llhd::ConstOp op) {+  printer << op.getOperationName() << " ";+  // The custom time attribute is not printing the attribute type by default for+  // some reason. Work around by printing the attribute without type, explicitly+  // followed by the operation type+  printer.printAttributeWithoutType(op.valueAttr());+  printer.printOptionalAttrDict(op.getAttrs(), {"value"});+  printer << " : ";+  printer.printType(op.getType());+}++OpFoldResult llhd::ConstOp::fold(ArrayRef<Attribute> operands) {+  assert(operands.empty() && "const has no operands");+  return value();+}++//===----------------------------------------------------------------------===//+// DextsOp+//===----------------------------------------------------------------------===//++unsigned llhd::DextsOp::getSliceWidth() {+  auto resultTy = result().getType();+  if (resultTy.isSignlessInteger()) {+    return resultTy.getIntOrFloatBitWidth();+  } else if (auto sigRes = resultTy.dyn_cast<llhd::SigType>()) {+    return sigRes.getUnderlyingType().getIntOrFloatBitWidth();+  } else if (auto vecRes = resultTy.dyn_cast<VectorType>()) {+    return vecRes.getNumElements();+  }+  return 0;+}++unsigned llhd::DextsOp::getTargetWidth() {+  auto targetTy = target().getType();+  if (targetTy.isSignlessInteger()) {+    return targetTy.getIntOrFloatBitWidth();+  } else if (auto sigRes = targetTy.dyn_cast<llhd::SigType>()) {+    return sigRes.getUnderlyingType().getIntOrFloatBitWidth();+  } else if (auto vecRes = targetTy.dyn_cast<VectorType>()) {+    return vecRes.getNumElements();+  }+  return 0;+}++//===----------------------------------------------------------------------===//+// NegOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::NegOp::fold(ArrayRef<Attribute> operands) {+  return constFoldUnaryOp<IntegerAttr>(operands, [](APInt a) { return -a; });+}++//===----------------------------------------------------------------------===//+// SModOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::SModOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.smod(x, 1) -> 0+  if (matchPattern(rhs(), m_One()))+    return Builder(getContext()).getZeroAttr(getType());++  /// llhd.smod(0, x) -> 0+  if (matchPattern(lhs(), m_Zero()))+    return Builder(getContext()).getZeroAttr(getType());++  /// llhs.smod(x,x) -> 0+  if (lhs() == rhs())+    return Builder(getContext()).getZeroAttr(getType());++  return constFoldBinaryOp<IntegerAttr>(operands, [](APInt lhs, APInt rhs) {+    APInt result = lhs.srem(rhs);+    if ((lhs.isNegative() && rhs.isNonNegative()) ||+        (lhs.isNonNegative() && rhs.isNegative())) {+      result += rhs;+    }+    return result;+  });+}++//===----------------------------------------------------------------------===//+// NotOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::NotOp::fold(ArrayRef<Attribute> operands) {+  return constFoldUnaryOp<IntegerAttr>(operands, [](APInt a) { return ~a; });+}++//===----------------------------------------------------------------------===//+// AndOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::AndOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.and(x, 0) -> 0+  if (matchPattern(rhs(), m_Zero()))+    return rhs();++  /// llhd.and(x, all_bits_set) -> x+  if (matchPattern(rhs(), constant_int_all_ones_matcher()))+    return lhs();++  // llhd.and(x, x) -> x+  if (rhs() == lhs())+    return rhs();++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a & b; });+}++//===----------------------------------------------------------------------===//+// OrOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::OrOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.or(x, 0) -> x+  if (matchPattern(rhs(), m_Zero()))+    return lhs();++  /// llhd.or(x, all_bits_set) -> all_bits_set+  if (matchPattern(rhs(), constant_int_all_ones_matcher()))+    return rhs();++  // llhd.or(x, x) -> x+  if (rhs() == lhs())+    return rhs();++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a | b; });+}++//===----------------------------------------------------------------------===//+// XorOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::XorOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.xor(x, 0) -> x+  if (matchPattern(rhs(), m_Zero()))+    return lhs();++  /// llhs.xor(x,x) -> 0+  if (lhs() == rhs())+    return Builder(getContext()).getZeroAttr(getType());++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a ^ b; });+}++//===----------------------------------------------------------------------===//+// ShlOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::ShlOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.shl(base, hidden, 0) -> base+  if (matchPattern(amount(), m_Zero()))+    return base();++  return constFoldTernaryOp<IntegerAttr>(+      operands, [](APInt base, APInt hidden, APInt amt) {+        base <<= amt;+        base += hidden.getHiBits(amt.getZExtValue());+        return base;+      });+}++static LogicalResult verify(llhd::ShlOp op) {+  if (op.base().getType() != op.result().getType()) {+    op.emitError("The output of the Shl operation is required to have the "+                 "same type as the base value (first operand), (")+        << op.base().getType() << " vs. " << op.result().getType() << ")";+    return failure();+  }++  // TODO: verify that T and Th only differ in the number of bits or elements++  return success();+}++//===----------------------------------------------------------------------===//+// ShrOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::ShrOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.shl(base, hidden, 0) -> base+  if (matchPattern(amount(), m_Zero()))+    return base();++  return constFoldTernaryOp<IntegerAttr>(+      operands, [](APInt base, APInt hidden, APInt amt) {+        base = base.getHiBits(base.getBitWidth() - amt.getZExtValue());+        hidden = hidden.getLoBits(amt.getZExtValue());+        hidden <<= base.getBitWidth() - amt.getZExtValue();+        return base + hidden;+      });+}++static LogicalResult verify(llhd::ShrOp op) {+  if (op.base().getType() != op.result().getType()) {+    op.emitError("The output of the Shr operation is required to have the "+                 "same type as the base value (first operand), (")+        << op.base().getType() << " vs. " << op.result().getType() << ")";+    return failure();+  }++  // TODO: verify that T and Th only differ in the number of bits or elements++  return success();+}++//===----------------------------------------------------------------------===//+// WaitOp+//===----------------------------------------------------------------------===//++// Implement this operation for the BranchOpInterface+Optional<MutableOperandRange>+llhd::WaitOp::getMutableSuccessorOperands(unsigned index) {+  assert(index == 0 && "invalid successor index");+  return destOpsMutable();+}++//===----------------------------------------------------------------------===//+// EntityOp+//===----------------------------------------------------------------------===//++/// Parse an argument list of an entity operation.+/// The argument list and argument types are returned in args and argTypes+/// respectively.+static ParseResult+parseArgumentList(OpAsmParser &parser,+                  SmallVectorImpl<OpAsmParser::OperandType> &args,+                  SmallVectorImpl<Type> &argTypes) {+  if (parser.parseLParen())+    return failure();++  do {+    OpAsmParser::OperandType argument;+    Type argType;+    if (succeeded(parser.parseOptionalRegionArgument(argument))) {+      if (!argument.name.empty() && succeeded(parser.parseColonType(argType))) {+        args.push_back(argument);+        argTypes.push_back(argType);+      }+    }+  } while (succeeded(parser.parseOptionalComma()));++  if (parser.parseRParen())+    return failure();++  return success();+}++/// parse an entity signature with syntax:+/// (%arg0 : T0, %arg1 : T1, <...>) -> (%out0 : T0, %out1 : T1, <...>)+static ParseResult+parseEntitySignature(OpAsmParser &parser, OperationState &result,+                     SmallVectorImpl<OpAsmParser::OperandType> &args,+                     SmallVectorImpl<Type> &argTypes) {+  if (parseArgumentList(parser, args, argTypes))+    return failure();+  // create the integer attribute with the number of inputs.+  IntegerAttr insAttr = parser.getBuilder().getI64IntegerAttr(args.size());+  result.addAttribute("ins", insAttr);+  if (parser.parseArrow() || parseArgumentList(parser, args, argTypes))+    return failure();++  return success();+}++static ParseResult parseEntityOp(OpAsmParser &parser, OperationState &result) {+  StringAttr entityName;+  SmallVector<OpAsmParser::OperandType, 4> args;+  SmallVector<Type, 4> argTypes;++  if (parser.parseSymbolName(entityName, SymbolTable::getSymbolAttrName(),+                             result.attributes))+    return failure();++  parseEntitySignature(parser, result, args, argTypes);++  if (parser.parseOptionalAttrDictWithKeyword(result.attributes))+    return failure();++  auto type = parser.getBuilder().getFunctionType(argTypes, llvm::None);+  result.addAttribute(mlir::llhd::EntityOp::getTypeAttrName(),+                      TypeAttr::get(type));++  auto *body = result.addRegion();+  parser.parseRegion(*body, args, argTypes);+  llhd::EntityOp::ensureTerminator(*body, parser.getBuilder(), result.location);+  return success();+}++static void printArgumentList(OpAsmPrinter &printer,+                              std::vector<BlockArgument> args) {+  printer << "(";+  for (size_t i = 0, e = args.size(); i < e; ++i) {

Could just use llvm::interleaveComma here instead?

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Dialect/LLHD/IR/LLHDOps.h"+#include "circt/Dialect/LLHD/IR/LLHDDialect.h"+#include "mlir/Dialect/CommonFolders.h"+#include "mlir/IR/Attributes.h"+#include "mlir/IR/Matchers.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/OpImplementation.h"+#include "mlir/IR/PatternMatch.h"+#include "mlir/IR/Region.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/IR/Types.h"+#include "mlir/IR/Value.h"+#include "mlir/Support/LogicalResult.h"+#include "llvm/ADT/ArrayRef.h"+#include "llvm/ADT/SmallVector.h"+#include "llvm/ADT/StringMap.h"++using namespace mlir;++namespace {++template <class AttrElementT,+          class ElementValueT = typename AttrElementT::ValueType,+          class CalculationT = function_ref<ElementValueT(ElementValueT)>>+Attribute constFoldUnaryOp(ArrayRef<Attribute> operands,+                           const CalculationT &calculate) {+  assert(operands.size() == 1 && "unary op takes one operand");+  if (!operands[0])+    return {};++  if (auto val = operands[0].dyn_cast<AttrElementT>()) {+    return AttrElementT::get(val.getType(), calculate(val.getValue()));+  } else if (auto val = operands[0].dyn_cast<SplatElementsAttr>()) {+    // Operand is a splat so we can avoid expanding the value out and+    // just fold based on the splat value.+    auto elementResult = calculate(val.getSplatValue<ElementValueT>());+    return DenseElementsAttr::get(val.getType(), elementResult);+  }+  if (auto val = operands[0].dyn_cast<ElementsAttr>()) {+    // Operand is ElementsAttr-derived; perform an element-wise fold by+    // expanding the values.+    auto valIt = val.getValues<ElementValueT>().begin();+    SmallVector<ElementValueT, 4> elementResults;+    elementResults.reserve(val.getNumElements());+    for (size_t i = 0, e = val.getNumElements(); i < e; ++i, ++valIt)+      elementResults.push_back(calculate(*valIt));+    return DenseElementsAttr::get(val.getType(), elementResults);+  }+  return {};+}++template <class AttrElementT,+          class ElementValueT = typename AttrElementT::ValueType,+          class CalculationT = function_ref<+              ElementValueT(ElementValueT, ElementValueT, ElementValueT)>>+Attribute constFoldTernaryOp(ArrayRef<Attribute> operands,+                             const CalculationT &calculate) {+  assert(operands.size() == 3 && "ternary op takes three operands");+  if (!operands[0] || !operands[1] || !operands[2])+    return {};+  if (operands[0].getType() != operands[1].getType())+    return {};+  if (operands[0].getType() != operands[2].getType())+    return {};++  if (operands[0].isa<AttrElementT>() && operands[1].isa<AttrElementT>() &&+      operands[2].isa<AttrElementT>()) {+    auto fst = operands[0].cast<AttrElementT>();+    auto snd = operands[1].cast<AttrElementT>();+    auto trd = operands[2].cast<AttrElementT>();++    return AttrElementT::get(+        fst.getType(),+        calculate(fst.getValue(), snd.getValue(), trd.getValue()));+  }+  if (operands[0].isa<SplatElementsAttr>() &&+      operands[1].isa<SplatElementsAttr>() &&+      operands[2].isa<SplatElementsAttr>()) {+    // Operands are splats so we can avoid expanding the values out and+    // just fold based on the splat value.+    auto fst = operands[0].cast<SplatElementsAttr>();+    auto snd = operands[1].cast<SplatElementsAttr>();+    auto trd = operands[2].cast<SplatElementsAttr>();++    auto elementResult = calculate(fst.getSplatValue<ElementValueT>(),+                                   snd.getSplatValue<ElementValueT>(),+                                   trd.getSplatValue<ElementValueT>());+    return DenseElementsAttr::get(fst.getType(), elementResult);+  }+  if (operands[0].isa<ElementsAttr>() && operands[1].isa<ElementsAttr>() &&+      operands[2].isa<ElementsAttr>()) {+    // Operands are ElementsAttr-derived; perform an element-wise fold by+    // expanding the values.+    auto fst = operands[0].cast<ElementsAttr>();+    auto snd = operands[1].cast<ElementsAttr>();+    auto trd = operands[2].cast<ElementsAttr>();++    auto fstIt = fst.getValues<ElementValueT>().begin();+    auto sndIt = snd.getValues<ElementValueT>().begin();+    auto trdIt = trd.getValues<ElementValueT>().begin();+    SmallVector<ElementValueT, 4> elementResults;+    elementResults.reserve(fst.getNumElements());+    for (size_t i = 0, e = fst.getNumElements(); i < e;+         ++i, ++fstIt, ++sndIt, ++trdIt)+      elementResults.push_back(calculate(*fstIt, *sndIt, *trdIt));+    return DenseElementsAttr::get(fst.getType(), elementResults);+  }+  return {};+}++struct constant_int_all_ones_matcher {+  bool match(Operation *op) {+    APInt value;+    return mlir::detail::constant_int_op_binder(&value).match(op) &&+           value.isAllOnesValue();+  }+};++} // anonymous namespace++//===---------------------------------------------------------------------===//+// LLHD Trait Helper Functions+//===---------------------------------------------------------------------===//++static bool sameKindArbitraryWidth(Type lhsType, Type rhsType) {+  return (lhsType.getKind() == rhsType.getKind()) &&+         (!lhsType.isa<ShapedType>() ||+          (lhsType.cast<ShapedType>().getElementType() ==+           rhsType.cast<ShapedType>().getElementType()));+}++//===---------------------------------------------------------------------===//+// LLHD Operations+//===---------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// ConstOp+//===----------------------------------------------------------------------===//++static ParseResult parseConstOp(OpAsmParser &parser, OperationState &result) {+  Attribute val;+  Type type;+  if (parser.parseAttribute(val, "value", result.attributes) ||+      parser.parseOptionalAttrDict(result.attributes))+    return failure();+  // parse the type for attributes that do not print the type by default+  if (parser.parseOptionalColon().value ||+      !parser.parseOptionalType(type).hasValue())+    type = val.getType();+  return parser.addTypeToList(val.getType(), result.types);+}++static void print(OpAsmPrinter &printer, llhd::ConstOp op) {+  printer << op.getOperationName() << " ";+  // The custom time attribute is not printing the attribute type by default for+  // some reason. Work around by printing the attribute without type, explicitly+  // followed by the operation type+  printer.printAttributeWithoutType(op.valueAttr());+  printer.printOptionalAttrDict(op.getAttrs(), {"value"});+  printer << " : ";+  printer.printType(op.getType());+}++OpFoldResult llhd::ConstOp::fold(ArrayRef<Attribute> operands) {+  assert(operands.empty() && "const has no operands");+  return value();+}++//===----------------------------------------------------------------------===//+// DextsOp+//===----------------------------------------------------------------------===//++unsigned llhd::DextsOp::getSliceWidth() {+  auto resultTy = result().getType();+  if (resultTy.isSignlessInteger()) {+    return resultTy.getIntOrFloatBitWidth();+  } else if (auto sigRes = resultTy.dyn_cast<llhd::SigType>()) {+    return sigRes.getUnderlyingType().getIntOrFloatBitWidth();+  } else if (auto vecRes = resultTy.dyn_cast<VectorType>()) {+    return vecRes.getNumElements();+  }+  return 0;+}++unsigned llhd::DextsOp::getTargetWidth() {+  auto targetTy = target().getType();+  if (targetTy.isSignlessInteger()) {+    return targetTy.getIntOrFloatBitWidth();+  } else if (auto sigRes = targetTy.dyn_cast<llhd::SigType>()) {+    return sigRes.getUnderlyingType().getIntOrFloatBitWidth();+  } else if (auto vecRes = targetTy.dyn_cast<VectorType>()) {+    return vecRes.getNumElements();+  }+  return 0;+}++//===----------------------------------------------------------------------===//+// NegOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::NegOp::fold(ArrayRef<Attribute> operands) {+  return constFoldUnaryOp<IntegerAttr>(operands, [](APInt a) { return -a; });+}++//===----------------------------------------------------------------------===//+// SModOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::SModOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.smod(x, 1) -> 0+  if (matchPattern(rhs(), m_One()))+    return Builder(getContext()).getZeroAttr(getType());++  /// llhd.smod(0, x) -> 0+  if (matchPattern(lhs(), m_Zero()))+    return Builder(getContext()).getZeroAttr(getType());++  /// llhs.smod(x,x) -> 0+  if (lhs() == rhs())+    return Builder(getContext()).getZeroAttr(getType());++  return constFoldBinaryOp<IntegerAttr>(operands, [](APInt lhs, APInt rhs) {+    APInt result = lhs.srem(rhs);+    if ((lhs.isNegative() && rhs.isNonNegative()) ||+        (lhs.isNonNegative() && rhs.isNegative())) {+      result += rhs;+    }+    return result;+  });+}++//===----------------------------------------------------------------------===//+// NotOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::NotOp::fold(ArrayRef<Attribute> operands) {+  return constFoldUnaryOp<IntegerAttr>(operands, [](APInt a) { return ~a; });+}++//===----------------------------------------------------------------------===//+// AndOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::AndOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.and(x, 0) -> 0+  if (matchPattern(rhs(), m_Zero()))+    return rhs();++  /// llhd.and(x, all_bits_set) -> x+  if (matchPattern(rhs(), constant_int_all_ones_matcher()))+    return lhs();++  // llhd.and(x, x) -> x+  if (rhs() == lhs())+    return rhs();++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a & b; });+}++//===----------------------------------------------------------------------===//+// OrOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::OrOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.or(x, 0) -> x+  if (matchPattern(rhs(), m_Zero()))+    return lhs();++  /// llhd.or(x, all_bits_set) -> all_bits_set+  if (matchPattern(rhs(), constant_int_all_ones_matcher()))+    return rhs();++  // llhd.or(x, x) -> x+  if (rhs() == lhs())+    return rhs();++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a | b; });+}++//===----------------------------------------------------------------------===//+// XorOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::XorOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.xor(x, 0) -> x+  if (matchPattern(rhs(), m_Zero()))+    return lhs();++  /// llhs.xor(x,x) -> 0+  if (lhs() == rhs())+    return Builder(getContext()).getZeroAttr(getType());++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a ^ b; });+}++//===----------------------------------------------------------------------===//+// ShlOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::ShlOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.shl(base, hidden, 0) -> base+  if (matchPattern(amount(), m_Zero()))+    return base();++  return constFoldTernaryOp<IntegerAttr>(+      operands, [](APInt base, APInt hidden, APInt amt) {+        base <<= amt;+        base += hidden.getHiBits(amt.getZExtValue());+        return base;+      });+}++static LogicalResult verify(llhd::ShlOp op) {+  if (op.base().getType() != op.result().getType()) {+    op.emitError("The output of the Shl operation is required to have the "+                 "same type as the base value (first operand), (")+        << op.base().getType() << " vs. " << op.result().getType() << ")";+    return failure();+  }++  // TODO: verify that T and Th only differ in the number of bits or elements++  return success();+}++//===----------------------------------------------------------------------===//+// ShrOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::ShrOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.shl(base, hidden, 0) -> base+  if (matchPattern(amount(), m_Zero()))+    return base();++  return constFoldTernaryOp<IntegerAttr>(+      operands, [](APInt base, APInt hidden, APInt amt) {+        base = base.getHiBits(base.getBitWidth() - amt.getZExtValue());+        hidden = hidden.getLoBits(amt.getZExtValue());+        hidden <<= base.getBitWidth() - amt.getZExtValue();+        return base + hidden;+      });+}++static LogicalResult verify(llhd::ShrOp op) {+  if (op.base().getType() != op.result().getType()) {+    op.emitError("The output of the Shr operation is required to have the "+                 "same type as the base value (first operand), (")+        << op.base().getType() << " vs. " << op.result().getType() << ")";+    return failure();+  }++  // TODO: verify that T and Th only differ in the number of bits or elements++  return success();+}++//===----------------------------------------------------------------------===//+// WaitOp+//===----------------------------------------------------------------------===//++// Implement this operation for the BranchOpInterface+Optional<MutableOperandRange>+llhd::WaitOp::getMutableSuccessorOperands(unsigned index) {+  assert(index == 0 && "invalid successor index");+  return destOpsMutable();+}++//===----------------------------------------------------------------------===//+// EntityOp+//===----------------------------------------------------------------------===//++/// Parse an argument list of an entity operation.+/// The argument list and argument types are returned in args and argTypes+/// respectively.+static ParseResult+parseArgumentList(OpAsmParser &parser,+                  SmallVectorImpl<OpAsmParser::OperandType> &args,+                  SmallVectorImpl<Type> &argTypes) {+  if (parser.parseLParen())+    return failure();++  do {+    OpAsmParser::OperandType argument;+    Type argType;+    if (succeeded(parser.parseOptionalRegionArgument(argument))) {+      if (!argument.name.empty() && succeeded(parser.parseColonType(argType))) {+        args.push_back(argument);+        argTypes.push_back(argType);+      }+    }+  } while (succeeded(parser.parseOptionalComma()));++  if (parser.parseRParen())+    return failure();++  return success();+}++/// parse an entity signature with syntax:+/// (%arg0 : T0, %arg1 : T1, <...>) -> (%out0 : T0, %out1 : T1, <...>)+static ParseResult+parseEntitySignature(OpAsmParser &parser, OperationState &result,+                     SmallVectorImpl<OpAsmParser::OperandType> &args,+                     SmallVectorImpl<Type> &argTypes) {+  if (parseArgumentList(parser, args, argTypes))+    return failure();+  // create the integer attribute with the number of inputs.+  IntegerAttr insAttr = parser.getBuilder().getI64IntegerAttr(args.size());+  result.addAttribute("ins", insAttr);+  if (parser.parseArrow() || parseArgumentList(parser, args, argTypes))+    return failure();++  return success();+}++static ParseResult parseEntityOp(OpAsmParser &parser, OperationState &result) {+  StringAttr entityName;+  SmallVector<OpAsmParser::OperandType, 4> args;+  SmallVector<Type, 4> argTypes;++  if (parser.parseSymbolName(entityName, SymbolTable::getSymbolAttrName(),+                             result.attributes))+    return failure();++  parseEntitySignature(parser, result, args, argTypes);++  if (parser.parseOptionalAttrDictWithKeyword(result.attributes))+    return failure();++  auto type = parser.getBuilder().getFunctionType(argTypes, llvm::None);+  result.addAttribute(mlir::llhd::EntityOp::getTypeAttrName(),+                      TypeAttr::get(type));++  auto *body = result.addRegion();+  parser.parseRegion(*body, args, argTypes);+  llhd::EntityOp::ensureTerminator(*body, parser.getBuilder(), result.location);+  return success();+}++static void printArgumentList(OpAsmPrinter &printer,+                              std::vector<BlockArgument> args) {+  printer << "(";+  for (size_t i = 0, e = args.size(); i < e; ++i) {+    printer << args[i] << " : ";+    printer.printType(args[i].getType());+    if (i < args.size() - 1)+      printer << ", ";+  }+  printer << ")";+}++static void print(OpAsmPrinter &printer, llhd::EntityOp op) {+  std::vector<BlockArgument> ins, outs;+  uint64_t n_ins = op.insAttr().getInt();+  for (uint64_t i = 0; i < op.body().front().getArguments().size(); ++i) {+    // no furter verification for the attribute type is required, already+    // handled by verify.+    if (i < n_ins) {+      ins.push_back(op.body().front().getArguments()[i]);+    } else {+      outs.push_back(op.body().front().getArguments()[i]);+    }+  }+  auto entityName =+      op.getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()).getValue();+  printer << op.getOperationName() << " ";+  printer.printSymbolName(entityName);+  printer << " ";+  printArgumentList(printer, ins);+  printer << " -> ";+  printArgumentList(printer, outs);+  printer.printOptionalAttrDictWithKeyword(+      op.getAttrs(),+      /*elidedAttrs =*/{SymbolTable::getSymbolAttrName(),+                        llhd::EntityOp::getTypeAttrName(), "ins"});+  printer.printRegion(op.body(), false, false);+}++static LogicalResult verify(llhd::EntityOp op) {+  uint64_t numArgs = op.getNumArguments();+  uint64_t nIns = op.insAttr().getInt();+  // check that there is at most one flag for each argument+  if (numArgs < nIns) {+    op.emitError("Cannot have more inputs than arguments, expected at most ")+        << numArgs << " but got: " << nIns;+    return failure();+  }+  return success();+}++LogicalResult mlir::llhd::EntityOp::verifyType() {+  // Fail if function returns any values. An entity's outputs are specially+  // marked arguments.+  if (this->getNumResults() > 0) {+    this->emitOpError("an entity cannot have return types.");+    return failure();+  }+  // Check that all operands are of signal type+  for (int i = 0, e = this->getNumFuncArguments(); i < e; ++i) {+    if (!llhd::SigType::kindof(this->getArgument(i).getType().getKind())) {+      this->emitOpError("usage of invalid argument type. Got ")+          << this->getArgument(i).getType() << ", expected LLHD signal type";+      return failure();+    }+  }+  return success();+}++LogicalResult mlir::llhd::EntityOp::verifyBody() {+  // Body must not be empty.+  if (this->isExternal()) {+    this->emitOpError("defining external entity with the entity instruction "+                      "is not allowed, use the intended instruction instead.");+    return failure();+  }++  // check signal names are unique+  llvm::StringMap<bool> sigMap;+  llvm::StringMap<bool> instMap;+  auto walkResult = walk([&sigMap, &instMap](Operation *op) -> WalkResult {+    if (auto sigOp = dyn_cast<SigOp>(op)) {+      if (sigMap[sigOp.name()]) {+        return sigOp.emitError("Redefinition of signal named '")+               << sigOp.name() << "'!";+      }+      sigMap.insert_or_assign(sigOp.name(), true);+    } else if (auto instOp = dyn_cast<InstOp>(op)) {+      if (instMap[instOp.name()]) {+        return instOp.emitError("Redefinition of instance named '")+               << instOp.name() << "'!";+      }+      instMap.insert_or_assign(instOp.name(), true);+    }+    return WalkResult::advance();+  });++  return failure(walkResult.wasInterrupted());+}++Region *llhd::EntityOp::getCallableRegion() {+  return isExternal() ? nullptr : &getBody();+}++ArrayRef<Type> llhd::EntityOp::getCallableResults() {+  return getType().getResults();+}++//===----------------------------------------------------------------------===//+// ProcOp+//===----------------------------------------------------------------------===//++LogicalResult mlir::llhd::ProcOp::verifyType() {+  // Fail if function returns more than zero values. This is because the+  // outputs of a process are specially marked arguments.+  if (this->getNumResults() > 0) {+    this->emitOpError(+        "process has more than zero return types, this is not allowed");+    return failure();+  }+  // Check that all operands are of signal type+  for (int i = 0, e = this->getNumFuncArguments(); i < e; ++i) {+    if (!llhd::SigType::kindof(this->getArgument(i).getType().getKind())) {+      this->emitOpError("usage of invalid argument type, was ")+          << this->getArgument(i).getType() << ", expected LLHD signal type";+      return failure();+    }+  }+  return success();+}++LogicalResult mlir::llhd::ProcOp::verifyBody() {+  // Body must not be empty, this indicates an external process. We use+  // another instruction to reference external processes.+  if (this->isExternal()) {+    this->emitOpError("defining external processes with the proc instruction "+                      "is not allowed, use the intended instruction instead.");+    return failure();+  }+  return success();+}++static LogicalResult verify(llhd::ProcOp op) {+  // Check that the ins attribute is smaller or equal the number of+  // arguments+  uint64_t numArgs = op.getNumArguments();+  uint64_t numIns = op.insAttr().getInt();+  if (numArgs < numIns) {+    op.emitOpError("Cannot have more inputs than arguments, expected at most ")+        << numArgs << ", got " << numIns;+    return failure();+  }+  return success();+}++static ParseResult+parseProcArgumentList(OpAsmParser &parser, SmallVectorImpl<Type> &argTypes,+                      SmallVectorImpl<OpAsmParser::OperandType> &argNames) {+  if (parser.parseLParen())+    return failure();++  // The argument list either has to consistently have ssa-id's followed by+  // types, or just be a type list.  It isn't ok to sometimes have SSA ID's+  // and sometimes not.+  auto parseArgument = [&]() -> ParseResult {+    llvm::SMLoc loc = parser.getCurrentLocation();++    // Parse argument name if present.+    OpAsmParser::OperandType argument;+    Type argumentType;+    if (succeeded(parser.parseOptionalRegionArgument(argument)) &&+        !argument.name.empty()) {+      // Reject this if the preceding argument was missing a name.+      if (argNames.empty() && !argTypes.empty())+        return parser.emitError(loc, "expected type instead of SSA identifier");+      argNames.push_back(argument);++      if (parser.parseColonType(argumentType))+        return failure();+    } else if (!argNames.empty()) {+      // Reject this if the preceding argument had a name.+      return parser.emitError(loc, "expected SSA identifier");+    } else if (parser.parseType(argumentType)) {+      return failure();+    }++    // Add the argument type.+    argTypes.push_back(argumentType);++    return success();+  };++  // Parse the function arguments.+  if (failed(parser.parseOptionalRParen())) {+    do {+      unsigned numTypedArguments = argTypes.size();+      if (parseArgument())+        return failure();++      llvm::SMLoc loc = parser.getCurrentLocation();+      if (argTypes.size() == numTypedArguments &&+          succeeded(parser.parseOptionalComma()))+        return parser.emitError(loc, "variadic arguments are not allowed");+    } while (succeeded(parser.parseOptionalComma()));+    parser.parseRParen();+  }++  return success();+}++static ParseResult parseProcOp(OpAsmParser &parser, OperationState &result) {+  StringAttr procName;+  SmallVector<OpAsmParser::OperandType, 8> argNames;+  SmallVector<Type, 8> argTypes;+  Builder &builder = parser.getBuilder();++  if (parser.parseSymbolName(procName, SymbolTable::getSymbolAttrName(),+                             result.attributes))+    return failure();++  if (parseProcArgumentList(parser, argTypes, argNames))+    return failure();++  result.addAttribute("ins", builder.getI64IntegerAttr(argTypes.size()));+  parser.parseArrow();

Please check for failure here.

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Dialect/LLHD/IR/LLHDOps.h"+#include "circt/Dialect/LLHD/IR/LLHDDialect.h"+#include "mlir/Dialect/CommonFolders.h"+#include "mlir/IR/Attributes.h"+#include "mlir/IR/Matchers.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/OpImplementation.h"+#include "mlir/IR/PatternMatch.h"+#include "mlir/IR/Region.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/IR/Types.h"+#include "mlir/IR/Value.h"+#include "mlir/Support/LogicalResult.h"+#include "llvm/ADT/ArrayRef.h"+#include "llvm/ADT/SmallVector.h"+#include "llvm/ADT/StringMap.h"++using namespace mlir;++namespace {++template <class AttrElementT,+          class ElementValueT = typename AttrElementT::ValueType,+          class CalculationT = function_ref<ElementValueT(ElementValueT)>>+Attribute constFoldUnaryOp(ArrayRef<Attribute> operands,+                           const CalculationT &calculate) {+  assert(operands.size() == 1 && "unary op takes one operand");+  if (!operands[0])+    return {};++  if (auto val = operands[0].dyn_cast<AttrElementT>()) {+    return AttrElementT::get(val.getType(), calculate(val.getValue()));+  } else if (auto val = operands[0].dyn_cast<SplatElementsAttr>()) {+    // Operand is a splat so we can avoid expanding the value out and+    // just fold based on the splat value.+    auto elementResult = calculate(val.getSplatValue<ElementValueT>());+    return DenseElementsAttr::get(val.getType(), elementResult);+  }+  if (auto val = operands[0].dyn_cast<ElementsAttr>()) {+    // Operand is ElementsAttr-derived; perform an element-wise fold by+    // expanding the values.+    auto valIt = val.getValues<ElementValueT>().begin();+    SmallVector<ElementValueT, 4> elementResults;+    elementResults.reserve(val.getNumElements());+    for (size_t i = 0, e = val.getNumElements(); i < e; ++i, ++valIt)+      elementResults.push_back(calculate(*valIt));+    return DenseElementsAttr::get(val.getType(), elementResults);+  }+  return {};+}++template <class AttrElementT,+          class ElementValueT = typename AttrElementT::ValueType,+          class CalculationT = function_ref<+              ElementValueT(ElementValueT, ElementValueT, ElementValueT)>>+Attribute constFoldTernaryOp(ArrayRef<Attribute> operands,+                             const CalculationT &calculate) {+  assert(operands.size() == 3 && "ternary op takes three operands");+  if (!operands[0] || !operands[1] || !operands[2])+    return {};+  if (operands[0].getType() != operands[1].getType())+    return {};+  if (operands[0].getType() != operands[2].getType())+    return {};++  if (operands[0].isa<AttrElementT>() && operands[1].isa<AttrElementT>() &&+      operands[2].isa<AttrElementT>()) {+    auto fst = operands[0].cast<AttrElementT>();+    auto snd = operands[1].cast<AttrElementT>();+    auto trd = operands[2].cast<AttrElementT>();++    return AttrElementT::get(+        fst.getType(),+        calculate(fst.getValue(), snd.getValue(), trd.getValue()));+  }+  if (operands[0].isa<SplatElementsAttr>() &&+      operands[1].isa<SplatElementsAttr>() &&+      operands[2].isa<SplatElementsAttr>()) {+    // Operands are splats so we can avoid expanding the values out and+    // just fold based on the splat value.+    auto fst = operands[0].cast<SplatElementsAttr>();+    auto snd = operands[1].cast<SplatElementsAttr>();+    auto trd = operands[2].cast<SplatElementsAttr>();++    auto elementResult = calculate(fst.getSplatValue<ElementValueT>(),+                                   snd.getSplatValue<ElementValueT>(),+                                   trd.getSplatValue<ElementValueT>());+    return DenseElementsAttr::get(fst.getType(), elementResult);+  }+  if (operands[0].isa<ElementsAttr>() && operands[1].isa<ElementsAttr>() &&+      operands[2].isa<ElementsAttr>()) {+    // Operands are ElementsAttr-derived; perform an element-wise fold by+    // expanding the values.+    auto fst = operands[0].cast<ElementsAttr>();+    auto snd = operands[1].cast<ElementsAttr>();+    auto trd = operands[2].cast<ElementsAttr>();++    auto fstIt = fst.getValues<ElementValueT>().begin();+    auto sndIt = snd.getValues<ElementValueT>().begin();+    auto trdIt = trd.getValues<ElementValueT>().begin();+    SmallVector<ElementValueT, 4> elementResults;+    elementResults.reserve(fst.getNumElements());+    for (size_t i = 0, e = fst.getNumElements(); i < e;+         ++i, ++fstIt, ++sndIt, ++trdIt)+      elementResults.push_back(calculate(*fstIt, *sndIt, *trdIt));+    return DenseElementsAttr::get(fst.getType(), elementResults);+  }+  return {};+}++struct constant_int_all_ones_matcher {+  bool match(Operation *op) {+    APInt value;+    return mlir::detail::constant_int_op_binder(&value).match(op) &&+           value.isAllOnesValue();+  }+};++} // anonymous namespace++//===---------------------------------------------------------------------===//+// LLHD Trait Helper Functions+//===---------------------------------------------------------------------===//++static bool sameKindArbitraryWidth(Type lhsType, Type rhsType) {+  return (lhsType.getKind() == rhsType.getKind()) &&+         (!lhsType.isa<ShapedType>() ||+          (lhsType.cast<ShapedType>().getElementType() ==+           rhsType.cast<ShapedType>().getElementType()));+}++//===---------------------------------------------------------------------===//+// LLHD Operations+//===---------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// ConstOp+//===----------------------------------------------------------------------===//++static ParseResult parseConstOp(OpAsmParser &parser, OperationState &result) {+  Attribute val;+  Type type;+  if (parser.parseAttribute(val, "value", result.attributes) ||+      parser.parseOptionalAttrDict(result.attributes))+    return failure();+  // parse the type for attributes that do not print the type by default+  if (parser.parseOptionalColon().value ||+      !parser.parseOptionalType(type).hasValue())+    type = val.getType();+  return parser.addTypeToList(val.getType(), result.types);+}++static void print(OpAsmPrinter &printer, llhd::ConstOp op) {+  printer << op.getOperationName() << " ";+  // The custom time attribute is not printing the attribute type by default for+  // some reason. Work around by printing the attribute without type, explicitly+  // followed by the operation type+  printer.printAttributeWithoutType(op.valueAttr());+  printer.printOptionalAttrDict(op.getAttrs(), {"value"});+  printer << " : ";+  printer.printType(op.getType());+}++OpFoldResult llhd::ConstOp::fold(ArrayRef<Attribute> operands) {+  assert(operands.empty() && "const has no operands");+  return value();+}++//===----------------------------------------------------------------------===//+// DextsOp+//===----------------------------------------------------------------------===//++unsigned llhd::DextsOp::getSliceWidth() {+  auto resultTy = result().getType();+  if (resultTy.isSignlessInteger()) {+    return resultTy.getIntOrFloatBitWidth();+  } else if (auto sigRes = resultTy.dyn_cast<llhd::SigType>()) {+    return sigRes.getUnderlyingType().getIntOrFloatBitWidth();+  } else if (auto vecRes = resultTy.dyn_cast<VectorType>()) {+    return vecRes.getNumElements();+  }+  return 0;+}++unsigned llhd::DextsOp::getTargetWidth() {+  auto targetTy = target().getType();+  if (targetTy.isSignlessInteger()) {+    return targetTy.getIntOrFloatBitWidth();+  } else if (auto sigRes = targetTy.dyn_cast<llhd::SigType>()) {+    return sigRes.getUnderlyingType().getIntOrFloatBitWidth();+  } else if (auto vecRes = targetTy.dyn_cast<VectorType>()) {+    return vecRes.getNumElements();+  }+  return 0;+}++//===----------------------------------------------------------------------===//+// NegOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::NegOp::fold(ArrayRef<Attribute> operands) {+  return constFoldUnaryOp<IntegerAttr>(operands, [](APInt a) { return -a; });+}++//===----------------------------------------------------------------------===//+// SModOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::SModOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.smod(x, 1) -> 0+  if (matchPattern(rhs(), m_One()))+    return Builder(getContext()).getZeroAttr(getType());++  /// llhd.smod(0, x) -> 0+  if (matchPattern(lhs(), m_Zero()))+    return Builder(getContext()).getZeroAttr(getType());++  /// llhs.smod(x,x) -> 0+  if (lhs() == rhs())+    return Builder(getContext()).getZeroAttr(getType());++  return constFoldBinaryOp<IntegerAttr>(operands, [](APInt lhs, APInt rhs) {+    APInt result = lhs.srem(rhs);+    if ((lhs.isNegative() && rhs.isNonNegative()) ||+        (lhs.isNonNegative() && rhs.isNegative())) {+      result += rhs;+    }+    return result;+  });+}++//===----------------------------------------------------------------------===//+// NotOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::NotOp::fold(ArrayRef<Attribute> operands) {+  return constFoldUnaryOp<IntegerAttr>(operands, [](APInt a) { return ~a; });+}++//===----------------------------------------------------------------------===//+// AndOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::AndOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.and(x, 0) -> 0+  if (matchPattern(rhs(), m_Zero()))+    return rhs();++  /// llhd.and(x, all_bits_set) -> x+  if (matchPattern(rhs(), constant_int_all_ones_matcher()))+    return lhs();++  // llhd.and(x, x) -> x+  if (rhs() == lhs())+    return rhs();++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a & b; });+}++//===----------------------------------------------------------------------===//+// OrOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::OrOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.or(x, 0) -> x+  if (matchPattern(rhs(), m_Zero()))+    return lhs();++  /// llhd.or(x, all_bits_set) -> all_bits_set+  if (matchPattern(rhs(), constant_int_all_ones_matcher()))+    return rhs();++  // llhd.or(x, x) -> x+  if (rhs() == lhs())+    return rhs();++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a | b; });+}++//===----------------------------------------------------------------------===//+// XorOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::XorOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.xor(x, 0) -> x+  if (matchPattern(rhs(), m_Zero()))+    return lhs();++  /// llhs.xor(x,x) -> 0+  if (lhs() == rhs())+    return Builder(getContext()).getZeroAttr(getType());++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a ^ b; });+}++//===----------------------------------------------------------------------===//+// ShlOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::ShlOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.shl(base, hidden, 0) -> base+  if (matchPattern(amount(), m_Zero()))+    return base();++  return constFoldTernaryOp<IntegerAttr>(+      operands, [](APInt base, APInt hidden, APInt amt) {+        base <<= amt;+        base += hidden.getHiBits(amt.getZExtValue());+        return base;+      });+}++static LogicalResult verify(llhd::ShlOp op) {+  if (op.base().getType() != op.result().getType()) {+    op.emitError("The output of the Shl operation is required to have the "

nit: Just return the diagnostic directly. It always converts to failure.

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Dialect/LLHD/IR/LLHDOps.h"+#include "circt/Dialect/LLHD/IR/LLHDDialect.h"+#include "mlir/Dialect/CommonFolders.h"+#include "mlir/IR/Attributes.h"+#include "mlir/IR/Matchers.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/OpImplementation.h"+#include "mlir/IR/PatternMatch.h"+#include "mlir/IR/Region.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/IR/Types.h"+#include "mlir/IR/Value.h"+#include "mlir/Support/LogicalResult.h"+#include "llvm/ADT/ArrayRef.h"+#include "llvm/ADT/SmallVector.h"+#include "llvm/ADT/StringMap.h"++using namespace mlir;++namespace {++template <class AttrElementT,+          class ElementValueT = typename AttrElementT::ValueType,+          class CalculationT = function_ref<ElementValueT(ElementValueT)>>+Attribute constFoldUnaryOp(ArrayRef<Attribute> operands,+                           const CalculationT &calculate) {+  assert(operands.size() == 1 && "unary op takes one operand");+  if (!operands[0])+    return {};++  if (auto val = operands[0].dyn_cast<AttrElementT>()) {+    return AttrElementT::get(val.getType(), calculate(val.getValue()));+  } else if (auto val = operands[0].dyn_cast<SplatElementsAttr>()) {+    // Operand is a splat so we can avoid expanding the value out and+    // just fold based on the splat value.+    auto elementResult = calculate(val.getSplatValue<ElementValueT>());+    return DenseElementsAttr::get(val.getType(), elementResult);+  }+  if (auto val = operands[0].dyn_cast<ElementsAttr>()) {+    // Operand is ElementsAttr-derived; perform an element-wise fold by+    // expanding the values.+    auto valIt = val.getValues<ElementValueT>().begin();+    SmallVector<ElementValueT, 4> elementResults;+    elementResults.reserve(val.getNumElements());+    for (size_t i = 0, e = val.getNumElements(); i < e; ++i, ++valIt)+      elementResults.push_back(calculate(*valIt));+    return DenseElementsAttr::get(val.getType(), elementResults);+  }+  return {};+}++template <class AttrElementT,+          class ElementValueT = typename AttrElementT::ValueType,+          class CalculationT = function_ref<+              ElementValueT(ElementValueT, ElementValueT, ElementValueT)>>+Attribute constFoldTernaryOp(ArrayRef<Attribute> operands,+                             const CalculationT &calculate) {+  assert(operands.size() == 3 && "ternary op takes three operands");+  if (!operands[0] || !operands[1] || !operands[2])+    return {};+  if (operands[0].getType() != operands[1].getType())+    return {};+  if (operands[0].getType() != operands[2].getType())+    return {};++  if (operands[0].isa<AttrElementT>() && operands[1].isa<AttrElementT>() &&+      operands[2].isa<AttrElementT>()) {+    auto fst = operands[0].cast<AttrElementT>();+    auto snd = operands[1].cast<AttrElementT>();+    auto trd = operands[2].cast<AttrElementT>();++    return AttrElementT::get(+        fst.getType(),+        calculate(fst.getValue(), snd.getValue(), trd.getValue()));+  }+  if (operands[0].isa<SplatElementsAttr>() &&+      operands[1].isa<SplatElementsAttr>() &&+      operands[2].isa<SplatElementsAttr>()) {+    // Operands are splats so we can avoid expanding the values out and+    // just fold based on the splat value.+    auto fst = operands[0].cast<SplatElementsAttr>();+    auto snd = operands[1].cast<SplatElementsAttr>();+    auto trd = operands[2].cast<SplatElementsAttr>();++    auto elementResult = calculate(fst.getSplatValue<ElementValueT>(),+                                   snd.getSplatValue<ElementValueT>(),+                                   trd.getSplatValue<ElementValueT>());+    return DenseElementsAttr::get(fst.getType(), elementResult);+  }+  if (operands[0].isa<ElementsAttr>() && operands[1].isa<ElementsAttr>() &&+      operands[2].isa<ElementsAttr>()) {+    // Operands are ElementsAttr-derived; perform an element-wise fold by+    // expanding the values.+    auto fst = operands[0].cast<ElementsAttr>();+    auto snd = operands[1].cast<ElementsAttr>();+    auto trd = operands[2].cast<ElementsAttr>();++    auto fstIt = fst.getValues<ElementValueT>().begin();+    auto sndIt = snd.getValues<ElementValueT>().begin();+    auto trdIt = trd.getValues<ElementValueT>().begin();+    SmallVector<ElementValueT, 4> elementResults;+    elementResults.reserve(fst.getNumElements());+    for (size_t i = 0, e = fst.getNumElements(); i < e;+         ++i, ++fstIt, ++sndIt, ++trdIt)+      elementResults.push_back(calculate(*fstIt, *sndIt, *trdIt));+    return DenseElementsAttr::get(fst.getType(), elementResults);+  }+  return {};+}++struct constant_int_all_ones_matcher {+  bool match(Operation *op) {+    APInt value;+    return mlir::detail::constant_int_op_binder(&value).match(op) &&+           value.isAllOnesValue();+  }+};++} // anonymous namespace++//===---------------------------------------------------------------------===//+// LLHD Trait Helper Functions+//===---------------------------------------------------------------------===//++static bool sameKindArbitraryWidth(Type lhsType, Type rhsType) {+  return (lhsType.getKind() == rhsType.getKind()) &&+         (!lhsType.isa<ShapedType>() ||+          (lhsType.cast<ShapedType>().getElementType() ==+           rhsType.cast<ShapedType>().getElementType()));+}++//===---------------------------------------------------------------------===//+// LLHD Operations+//===---------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// ConstOp+//===----------------------------------------------------------------------===//++static ParseResult parseConstOp(OpAsmParser &parser, OperationState &result) {+  Attribute val;+  Type type;+  if (parser.parseAttribute(val, "value", result.attributes) ||+      parser.parseOptionalAttrDict(result.attributes))+    return failure();+  // parse the type for attributes that do not print the type by default+  if (parser.parseOptionalColon().value ||+      !parser.parseOptionalType(type).hasValue())+    type = val.getType();+  return parser.addTypeToList(val.getType(), result.types);+}++static void print(OpAsmPrinter &printer, llhd::ConstOp op) {+  printer << op.getOperationName() << " ";+  // The custom time attribute is not printing the attribute type by default for+  // some reason. Work around by printing the attribute without type, explicitly+  // followed by the operation type+  printer.printAttributeWithoutType(op.valueAttr());+  printer.printOptionalAttrDict(op.getAttrs(), {"value"});+  printer << " : ";+  printer.printType(op.getType());+}++OpFoldResult llhd::ConstOp::fold(ArrayRef<Attribute> operands) {+  assert(operands.empty() && "const has no operands");+  return value();+}++//===----------------------------------------------------------------------===//+// DextsOp+//===----------------------------------------------------------------------===//++unsigned llhd::DextsOp::getSliceWidth() {+  auto resultTy = result().getType();+  if (resultTy.isSignlessInteger()) {+    return resultTy.getIntOrFloatBitWidth();+  } else if (auto sigRes = resultTy.dyn_cast<llhd::SigType>()) {+    return sigRes.getUnderlyingType().getIntOrFloatBitWidth();+  } else if (auto vecRes = resultTy.dyn_cast<VectorType>()) {+    return vecRes.getNumElements();+  }+  return 0;+}++unsigned llhd::DextsOp::getTargetWidth() {+  auto targetTy = target().getType();+  if (targetTy.isSignlessInteger()) {+    return targetTy.getIntOrFloatBitWidth();+  } else if (auto sigRes = targetTy.dyn_cast<llhd::SigType>()) {+    return sigRes.getUnderlyingType().getIntOrFloatBitWidth();+  } else if (auto vecRes = targetTy.dyn_cast<VectorType>()) {+    return vecRes.getNumElements();+  }+  return 0;+}++//===----------------------------------------------------------------------===//+// NegOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::NegOp::fold(ArrayRef<Attribute> operands) {+  return constFoldUnaryOp<IntegerAttr>(operands, [](APInt a) { return -a; });+}++//===----------------------------------------------------------------------===//+// SModOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::SModOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.smod(x, 1) -> 0+  if (matchPattern(rhs(), m_One()))+    return Builder(getContext()).getZeroAttr(getType());++  /// llhd.smod(0, x) -> 0+  if (matchPattern(lhs(), m_Zero()))+    return Builder(getContext()).getZeroAttr(getType());++  /// llhs.smod(x,x) -> 0+  if (lhs() == rhs())+    return Builder(getContext()).getZeroAttr(getType());++  return constFoldBinaryOp<IntegerAttr>(operands, [](APInt lhs, APInt rhs) {+    APInt result = lhs.srem(rhs);+    if ((lhs.isNegative() && rhs.isNonNegative()) ||+        (lhs.isNonNegative() && rhs.isNegative())) {+      result += rhs;+    }+    return result;+  });+}++//===----------------------------------------------------------------------===//+// NotOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::NotOp::fold(ArrayRef<Attribute> operands) {+  return constFoldUnaryOp<IntegerAttr>(operands, [](APInt a) { return ~a; });+}++//===----------------------------------------------------------------------===//+// AndOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::AndOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.and(x, 0) -> 0+  if (matchPattern(rhs(), m_Zero()))+    return rhs();++  /// llhd.and(x, all_bits_set) -> x+  if (matchPattern(rhs(), constant_int_all_ones_matcher()))+    return lhs();++  // llhd.and(x, x) -> x+  if (rhs() == lhs())+    return rhs();++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a & b; });+}++//===----------------------------------------------------------------------===//+// OrOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::OrOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.or(x, 0) -> x+  if (matchPattern(rhs(), m_Zero()))+    return lhs();++  /// llhd.or(x, all_bits_set) -> all_bits_set+  if (matchPattern(rhs(), constant_int_all_ones_matcher()))+    return rhs();++  // llhd.or(x, x) -> x+  if (rhs() == lhs())+    return rhs();++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a | b; });+}++//===----------------------------------------------------------------------===//+// XorOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::XorOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.xor(x, 0) -> x+  if (matchPattern(rhs(), m_Zero()))+    return lhs();++  /// llhs.xor(x,x) -> 0+  if (lhs() == rhs())+    return Builder(getContext()).getZeroAttr(getType());++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a ^ b; });+}++//===----------------------------------------------------------------------===//+// ShlOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::ShlOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.shl(base, hidden, 0) -> base+  if (matchPattern(amount(), m_Zero()))+    return base();++  return constFoldTernaryOp<IntegerAttr>(+      operands, [](APInt base, APInt hidden, APInt amt) {+        base <<= amt;+        base += hidden.getHiBits(amt.getZExtValue());+        return base;+      });+}++static LogicalResult verify(llhd::ShlOp op) {+  if (op.base().getType() != op.result().getType()) {+    op.emitError("The output of the Shl operation is required to have the "+                 "same type as the base value (first operand), (")+        << op.base().getType() << " vs. " << op.result().getType() << ")";+    return failure();+  }++  // TODO: verify that T and Th only differ in the number of bits or elements++  return success();+}++//===----------------------------------------------------------------------===//+// ShrOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::ShrOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.shl(base, hidden, 0) -> base+  if (matchPattern(amount(), m_Zero()))+    return base();++  return constFoldTernaryOp<IntegerAttr>(+      operands, [](APInt base, APInt hidden, APInt amt) {+        base = base.getHiBits(base.getBitWidth() - amt.getZExtValue());+        hidden = hidden.getLoBits(amt.getZExtValue());+        hidden <<= base.getBitWidth() - amt.getZExtValue();+        return base + hidden;+      });+}++static LogicalResult verify(llhd::ShrOp op) {+  if (op.base().getType() != op.result().getType()) {+    op.emitError("The output of the Shr operation is required to have the "+                 "same type as the base value (first operand), (")+        << op.base().getType() << " vs. " << op.result().getType() << ")";+    return failure();+  }++  // TODO: verify that T and Th only differ in the number of bits or elements++  return success();+}++//===----------------------------------------------------------------------===//+// WaitOp+//===----------------------------------------------------------------------===//++// Implement this operation for the BranchOpInterface+Optional<MutableOperandRange>+llhd::WaitOp::getMutableSuccessorOperands(unsigned index) {+  assert(index == 0 && "invalid successor index");+  return destOpsMutable();+}++//===----------------------------------------------------------------------===//+// EntityOp+//===----------------------------------------------------------------------===//++/// Parse an argument list of an entity operation.+/// The argument list and argument types are returned in args and argTypes+/// respectively.+static ParseResult+parseArgumentList(OpAsmParser &parser,+                  SmallVectorImpl<OpAsmParser::OperandType> &args,+                  SmallVectorImpl<Type> &argTypes) {+  if (parser.parseLParen())+    return failure();++  do {+    OpAsmParser::OperandType argument;+    Type argType;+    if (succeeded(parser.parseOptionalRegionArgument(argument))) {+      if (!argument.name.empty() && succeeded(parser.parseColonType(argType))) {+        args.push_back(argument);+        argTypes.push_back(argType);+      }+    }+  } while (succeeded(parser.parseOptionalComma()));++  if (parser.parseRParen())+    return failure();++  return success();+}++/// parse an entity signature with syntax:+/// (%arg0 : T0, %arg1 : T1, <...>) -> (%out0 : T0, %out1 : T1, <...>)+static ParseResult+parseEntitySignature(OpAsmParser &parser, OperationState &result,+                     SmallVectorImpl<OpAsmParser::OperandType> &args,+                     SmallVectorImpl<Type> &argTypes) {+  if (parseArgumentList(parser, args, argTypes))+    return failure();+  // create the integer attribute with the number of inputs.+  IntegerAttr insAttr = parser.getBuilder().getI64IntegerAttr(args.size());+  result.addAttribute("ins", insAttr);+  if (parser.parseArrow() || parseArgumentList(parser, args, argTypes))+    return failure();++  return success();+}++static ParseResult parseEntityOp(OpAsmParser &parser, OperationState &result) {+  StringAttr entityName;+  SmallVector<OpAsmParser::OperandType, 4> args;+  SmallVector<Type, 4> argTypes;++  if (parser.parseSymbolName(entityName, SymbolTable::getSymbolAttrName(),+                             result.attributes))+    return failure();++  parseEntitySignature(parser, result, args, argTypes);++  if (parser.parseOptionalAttrDictWithKeyword(result.attributes))+    return failure();++  auto type = parser.getBuilder().getFunctionType(argTypes, llvm::None);+  result.addAttribute(mlir::llhd::EntityOp::getTypeAttrName(),+                      TypeAttr::get(type));++  auto *body = result.addRegion();+  parser.parseRegion(*body, args, argTypes);+  llhd::EntityOp::ensureTerminator(*body, parser.getBuilder(), result.location);+  return success();+}++static void printArgumentList(OpAsmPrinter &printer,+                              std::vector<BlockArgument> args) {+  printer << "(";+  for (size_t i = 0, e = args.size(); i < e; ++i) {+    printer << args[i] << " : ";+    printer.printType(args[i].getType());+    if (i < args.size() - 1)+      printer << ", ";+  }+  printer << ")";+}++static void print(OpAsmPrinter &printer, llhd::EntityOp op) {+  std::vector<BlockArgument> ins, outs;+  uint64_t n_ins = op.insAttr().getInt();+  for (uint64_t i = 0; i < op.body().front().getArguments().size(); ++i) {+    // no furter verification for the attribute type is required, already+    // handled by verify.+    if (i < n_ins) {+      ins.push_back(op.body().front().getArguments()[i]);+    } else {+      outs.push_back(op.body().front().getArguments()[i]);+    }+  }+  auto entityName =+      op.getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()).getValue();+  printer << op.getOperationName() << " ";+  printer.printSymbolName(entityName);+  printer << " ";+  printArgumentList(printer, ins);+  printer << " -> ";+  printArgumentList(printer, outs);+  printer.printOptionalAttrDictWithKeyword(+      op.getAttrs(),+      /*elidedAttrs =*/{SymbolTable::getSymbolAttrName(),+                        llhd::EntityOp::getTypeAttrName(), "ins"});+  printer.printRegion(op.body(), false, false);+}++static LogicalResult verify(llhd::EntityOp op) {+  uint64_t numArgs = op.getNumArguments();+  uint64_t nIns = op.insAttr().getInt();+  // check that there is at most one flag for each argument+  if (numArgs < nIns) {+    op.emitError("Cannot have more inputs than arguments, expected at most ")+        << numArgs << " but got: " << nIns;+    return failure();+  }+  return success();+}++LogicalResult mlir::llhd::EntityOp::verifyType() {+  // Fail if function returns any values. An entity's outputs are specially+  // marked arguments.+  if (this->getNumResults() > 0) {+    this->emitOpError("an entity cannot have return types.");+    return failure();+  }+  // Check that all operands are of signal type+  for (int i = 0, e = this->getNumFuncArguments(); i < e; ++i) {+    if (!llhd::SigType::kindof(this->getArgument(i).getType().getKind())) {+      this->emitOpError("usage of invalid argument type. Got ")

Can you drop the this-> here and throughout?

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Dialect/LLHD/IR/LLHDOps.h"+#include "circt/Dialect/LLHD/IR/LLHDDialect.h"+#include "mlir/Dialect/CommonFolders.h"+#include "mlir/IR/Attributes.h"+#include "mlir/IR/Matchers.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/OpImplementation.h"+#include "mlir/IR/PatternMatch.h"+#include "mlir/IR/Region.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/IR/Types.h"+#include "mlir/IR/Value.h"+#include "mlir/Support/LogicalResult.h"+#include "llvm/ADT/ArrayRef.h"+#include "llvm/ADT/SmallVector.h"+#include "llvm/ADT/StringMap.h"++using namespace mlir;++namespace {++template <class AttrElementT,+          class ElementValueT = typename AttrElementT::ValueType,+          class CalculationT = function_ref<ElementValueT(ElementValueT)>>+Attribute constFoldUnaryOp(ArrayRef<Attribute> operands,+                           const CalculationT &calculate) {+  assert(operands.size() == 1 && "unary op takes one operand");+  if (!operands[0])+    return {};++  if (auto val = operands[0].dyn_cast<AttrElementT>()) {+    return AttrElementT::get(val.getType(), calculate(val.getValue()));+  } else if (auto val = operands[0].dyn_cast<SplatElementsAttr>()) {+    // Operand is a splat so we can avoid expanding the value out and+    // just fold based on the splat value.+    auto elementResult = calculate(val.getSplatValue<ElementValueT>());+    return DenseElementsAttr::get(val.getType(), elementResult);+  }+  if (auto val = operands[0].dyn_cast<ElementsAttr>()) {+    // Operand is ElementsAttr-derived; perform an element-wise fold by+    // expanding the values.+    auto valIt = val.getValues<ElementValueT>().begin();+    SmallVector<ElementValueT, 4> elementResults;+    elementResults.reserve(val.getNumElements());+    for (size_t i = 0, e = val.getNumElements(); i < e; ++i, ++valIt)+      elementResults.push_back(calculate(*valIt));+    return DenseElementsAttr::get(val.getType(), elementResults);+  }+  return {};+}++template <class AttrElementT,+          class ElementValueT = typename AttrElementT::ValueType,+          class CalculationT = function_ref<+              ElementValueT(ElementValueT, ElementValueT, ElementValueT)>>+Attribute constFoldTernaryOp(ArrayRef<Attribute> operands,+                             const CalculationT &calculate) {+  assert(operands.size() == 3 && "ternary op takes three operands");+  if (!operands[0] || !operands[1] || !operands[2])+    return {};+  if (operands[0].getType() != operands[1].getType())+    return {};+  if (operands[0].getType() != operands[2].getType())+    return {};++  if (operands[0].isa<AttrElementT>() && operands[1].isa<AttrElementT>() &&+      operands[2].isa<AttrElementT>()) {+    auto fst = operands[0].cast<AttrElementT>();+    auto snd = operands[1].cast<AttrElementT>();+    auto trd = operands[2].cast<AttrElementT>();++    return AttrElementT::get(+        fst.getType(),+        calculate(fst.getValue(), snd.getValue(), trd.getValue()));+  }+  if (operands[0].isa<SplatElementsAttr>() &&+      operands[1].isa<SplatElementsAttr>() &&+      operands[2].isa<SplatElementsAttr>()) {+    // Operands are splats so we can avoid expanding the values out and+    // just fold based on the splat value.+    auto fst = operands[0].cast<SplatElementsAttr>();+    auto snd = operands[1].cast<SplatElementsAttr>();+    auto trd = operands[2].cast<SplatElementsAttr>();++    auto elementResult = calculate(fst.getSplatValue<ElementValueT>(),+                                   snd.getSplatValue<ElementValueT>(),+                                   trd.getSplatValue<ElementValueT>());+    return DenseElementsAttr::get(fst.getType(), elementResult);+  }+  if (operands[0].isa<ElementsAttr>() && operands[1].isa<ElementsAttr>() &&+      operands[2].isa<ElementsAttr>()) {+    // Operands are ElementsAttr-derived; perform an element-wise fold by+    // expanding the values.+    auto fst = operands[0].cast<ElementsAttr>();+    auto snd = operands[1].cast<ElementsAttr>();+    auto trd = operands[2].cast<ElementsAttr>();++    auto fstIt = fst.getValues<ElementValueT>().begin();+    auto sndIt = snd.getValues<ElementValueT>().begin();+    auto trdIt = trd.getValues<ElementValueT>().begin();+    SmallVector<ElementValueT, 4> elementResults;+    elementResults.reserve(fst.getNumElements());+    for (size_t i = 0, e = fst.getNumElements(); i < e;+         ++i, ++fstIt, ++sndIt, ++trdIt)+      elementResults.push_back(calculate(*fstIt, *sndIt, *trdIt));+    return DenseElementsAttr::get(fst.getType(), elementResults);+  }+  return {};+}++struct constant_int_all_ones_matcher {+  bool match(Operation *op) {+    APInt value;+    return mlir::detail::constant_int_op_binder(&value).match(op) &&+           value.isAllOnesValue();+  }+};++} // anonymous namespace++//===---------------------------------------------------------------------===//+// LLHD Trait Helper Functions+//===---------------------------------------------------------------------===//++static bool sameKindArbitraryWidth(Type lhsType, Type rhsType) {+  return (lhsType.getKind() == rhsType.getKind()) &&+         (!lhsType.isa<ShapedType>() ||+          (lhsType.cast<ShapedType>().getElementType() ==+           rhsType.cast<ShapedType>().getElementType()));+}++//===---------------------------------------------------------------------===//+// LLHD Operations+//===---------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// ConstOp+//===----------------------------------------------------------------------===//++static ParseResult parseConstOp(OpAsmParser &parser, OperationState &result) {+  Attribute val;+  Type type;+  if (parser.parseAttribute(val, "value", result.attributes) ||+      parser.parseOptionalAttrDict(result.attributes))+    return failure();+  // parse the type for attributes that do not print the type by default+  if (parser.parseOptionalColon().value ||+      !parser.parseOptionalType(type).hasValue())+    type = val.getType();+  return parser.addTypeToList(val.getType(), result.types);+}++static void print(OpAsmPrinter &printer, llhd::ConstOp op) {+  printer << op.getOperationName() << " ";+  // The custom time attribute is not printing the attribute type by default for+  // some reason. Work around by printing the attribute without type, explicitly+  // followed by the operation type+  printer.printAttributeWithoutType(op.valueAttr());+  printer.printOptionalAttrDict(op.getAttrs(), {"value"});+  printer << " : ";+  printer.printType(op.getType());+}++OpFoldResult llhd::ConstOp::fold(ArrayRef<Attribute> operands) {+  assert(operands.empty() && "const has no operands");+  return value();+}++//===----------------------------------------------------------------------===//+// DextsOp+//===----------------------------------------------------------------------===//++unsigned llhd::DextsOp::getSliceWidth() {+  auto resultTy = result().getType();+  if (resultTy.isSignlessInteger()) {+    return resultTy.getIntOrFloatBitWidth();+  } else if (auto sigRes = resultTy.dyn_cast<llhd::SigType>()) {+    return sigRes.getUnderlyingType().getIntOrFloatBitWidth();+  } else if (auto vecRes = resultTy.dyn_cast<VectorType>()) {+    return vecRes.getNumElements();+  }+  return 0;+}++unsigned llhd::DextsOp::getTargetWidth() {+  auto targetTy = target().getType();+  if (targetTy.isSignlessInteger()) {+    return targetTy.getIntOrFloatBitWidth();+  } else if (auto sigRes = targetTy.dyn_cast<llhd::SigType>()) {+    return sigRes.getUnderlyingType().getIntOrFloatBitWidth();+  } else if (auto vecRes = targetTy.dyn_cast<VectorType>()) {+    return vecRes.getNumElements();+  }+  return 0;+}++//===----------------------------------------------------------------------===//+// NegOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::NegOp::fold(ArrayRef<Attribute> operands) {+  return constFoldUnaryOp<IntegerAttr>(operands, [](APInt a) { return -a; });+}++//===----------------------------------------------------------------------===//+// SModOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::SModOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.smod(x, 1) -> 0+  if (matchPattern(rhs(), m_One()))+    return Builder(getContext()).getZeroAttr(getType());++  /// llhd.smod(0, x) -> 0+  if (matchPattern(lhs(), m_Zero()))+    return Builder(getContext()).getZeroAttr(getType());++  /// llhs.smod(x,x) -> 0+  if (lhs() == rhs())+    return Builder(getContext()).getZeroAttr(getType());++  return constFoldBinaryOp<IntegerAttr>(operands, [](APInt lhs, APInt rhs) {+    APInt result = lhs.srem(rhs);+    if ((lhs.isNegative() && rhs.isNonNegative()) ||+        (lhs.isNonNegative() && rhs.isNegative())) {+      result += rhs;+    }+    return result;+  });+}++//===----------------------------------------------------------------------===//+// NotOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::NotOp::fold(ArrayRef<Attribute> operands) {+  return constFoldUnaryOp<IntegerAttr>(operands, [](APInt a) { return ~a; });+}++//===----------------------------------------------------------------------===//+// AndOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::AndOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.and(x, 0) -> 0+  if (matchPattern(rhs(), m_Zero()))+    return rhs();++  /// llhd.and(x, all_bits_set) -> x+  if (matchPattern(rhs(), constant_int_all_ones_matcher()))+    return lhs();++  // llhd.and(x, x) -> x+  if (rhs() == lhs())+    return rhs();++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a & b; });+}++//===----------------------------------------------------------------------===//+// OrOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::OrOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.or(x, 0) -> x+  if (matchPattern(rhs(), m_Zero()))+    return lhs();++  /// llhd.or(x, all_bits_set) -> all_bits_set+  if (matchPattern(rhs(), constant_int_all_ones_matcher()))+    return rhs();++  // llhd.or(x, x) -> x+  if (rhs() == lhs())+    return rhs();++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a | b; });+}++//===----------------------------------------------------------------------===//+// XorOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::XorOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.xor(x, 0) -> x+  if (matchPattern(rhs(), m_Zero()))+    return lhs();++  /// llhs.xor(x,x) -> 0+  if (lhs() == rhs())+    return Builder(getContext()).getZeroAttr(getType());++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a ^ b; });+}++//===----------------------------------------------------------------------===//+// ShlOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::ShlOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.shl(base, hidden, 0) -> base+  if (matchPattern(amount(), m_Zero()))+    return base();++  return constFoldTernaryOp<IntegerAttr>(+      operands, [](APInt base, APInt hidden, APInt amt) {+        base <<= amt;+        base += hidden.getHiBits(amt.getZExtValue());+        return base;+      });+}++static LogicalResult verify(llhd::ShlOp op) {+  if (op.base().getType() != op.result().getType()) {+    op.emitError("The output of the Shl operation is required to have the "+                 "same type as the base value (first operand), (")+        << op.base().getType() << " vs. " << op.result().getType() << ")";+    return failure();+  }++  // TODO: verify that T and Th only differ in the number of bits or elements++  return success();+}++//===----------------------------------------------------------------------===//+// ShrOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::ShrOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.shl(base, hidden, 0) -> base+  if (matchPattern(amount(), m_Zero()))+    return base();++  return constFoldTernaryOp<IntegerAttr>(+      operands, [](APInt base, APInt hidden, APInt amt) {+        base = base.getHiBits(base.getBitWidth() - amt.getZExtValue());+        hidden = hidden.getLoBits(amt.getZExtValue());+        hidden <<= base.getBitWidth() - amt.getZExtValue();+        return base + hidden;+      });+}++static LogicalResult verify(llhd::ShrOp op) {+  if (op.base().getType() != op.result().getType()) {+    op.emitError("The output of the Shr operation is required to have the "+                 "same type as the base value (first operand), (")+        << op.base().getType() << " vs. " << op.result().getType() << ")";+    return failure();+  }++  // TODO: verify that T and Th only differ in the number of bits or elements++  return success();+}++//===----------------------------------------------------------------------===//+// WaitOp+//===----------------------------------------------------------------------===//++// Implement this operation for the BranchOpInterface+Optional<MutableOperandRange>+llhd::WaitOp::getMutableSuccessorOperands(unsigned index) {+  assert(index == 0 && "invalid successor index");+  return destOpsMutable();+}++//===----------------------------------------------------------------------===//+// EntityOp+//===----------------------------------------------------------------------===//++/// Parse an argument list of an entity operation.+/// The argument list and argument types are returned in args and argTypes+/// respectively.+static ParseResult+parseArgumentList(OpAsmParser &parser,+                  SmallVectorImpl<OpAsmParser::OperandType> &args,+                  SmallVectorImpl<Type> &argTypes) {+  if (parser.parseLParen())+    return failure();++  do {+    OpAsmParser::OperandType argument;+    Type argType;+    if (succeeded(parser.parseOptionalRegionArgument(argument))) {+      if (!argument.name.empty() && succeeded(parser.parseColonType(argType))) {+        args.push_back(argument);+        argTypes.push_back(argType);+      }+    }+  } while (succeeded(parser.parseOptionalComma()));++  if (parser.parseRParen())+    return failure();++  return success();+}++/// parse an entity signature with syntax:+/// (%arg0 : T0, %arg1 : T1, <...>) -> (%out0 : T0, %out1 : T1, <...>)+static ParseResult+parseEntitySignature(OpAsmParser &parser, OperationState &result,+                     SmallVectorImpl<OpAsmParser::OperandType> &args,+                     SmallVectorImpl<Type> &argTypes) {+  if (parseArgumentList(parser, args, argTypes))+    return failure();+  // create the integer attribute with the number of inputs.+  IntegerAttr insAttr = parser.getBuilder().getI64IntegerAttr(args.size());+  result.addAttribute("ins", insAttr);+  if (parser.parseArrow() || parseArgumentList(parser, args, argTypes))+    return failure();++  return success();+}++static ParseResult parseEntityOp(OpAsmParser &parser, OperationState &result) {+  StringAttr entityName;+  SmallVector<OpAsmParser::OperandType, 4> args;+  SmallVector<Type, 4> argTypes;++  if (parser.parseSymbolName(entityName, SymbolTable::getSymbolAttrName(),+                             result.attributes))+    return failure();++  parseEntitySignature(parser, result, args, argTypes);++  if (parser.parseOptionalAttrDictWithKeyword(result.attributes))+    return failure();++  auto type = parser.getBuilder().getFunctionType(argTypes, llvm::None);+  result.addAttribute(mlir::llhd::EntityOp::getTypeAttrName(),+                      TypeAttr::get(type));++  auto *body = result.addRegion();+  parser.parseRegion(*body, args, argTypes);+  llhd::EntityOp::ensureTerminator(*body, parser.getBuilder(), result.location);+  return success();+}++static void printArgumentList(OpAsmPrinter &printer,+                              std::vector<BlockArgument> args) {+  printer << "(";+  for (size_t i = 0, e = args.size(); i < e; ++i) {+    printer << args[i] << " : ";+    printer.printType(args[i].getType());+    if (i < args.size() - 1)+      printer << ", ";+  }+  printer << ")";+}++static void print(OpAsmPrinter &printer, llhd::EntityOp op) {+  std::vector<BlockArgument> ins, outs;+  uint64_t n_ins = op.insAttr().getInt();+  for (uint64_t i = 0; i < op.body().front().getArguments().size(); ++i) {+    // no furter verification for the attribute type is required, already+    // handled by verify.+    if (i < n_ins) {+      ins.push_back(op.body().front().getArguments()[i]);+    } else {+      outs.push_back(op.body().front().getArguments()[i]);+    }+  }+  auto entityName =+      op.getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()).getValue();+  printer << op.getOperationName() << " ";+  printer.printSymbolName(entityName);+  printer << " ";+  printArgumentList(printer, ins);+  printer << " -> ";+  printArgumentList(printer, outs);+  printer.printOptionalAttrDictWithKeyword(+      op.getAttrs(),+      /*elidedAttrs =*/{SymbolTable::getSymbolAttrName(),+                        llhd::EntityOp::getTypeAttrName(), "ins"});+  printer.printRegion(op.body(), false, false);+}++static LogicalResult verify(llhd::EntityOp op) {+  uint64_t numArgs = op.getNumArguments();+  uint64_t nIns = op.insAttr().getInt();+  // check that there is at most one flag for each argument+  if (numArgs < nIns) {+    op.emitError("Cannot have more inputs than arguments, expected at most ")+        << numArgs << " but got: " << nIns;+    return failure();+  }+  return success();+}++LogicalResult mlir::llhd::EntityOp::verifyType() {+  // Fail if function returns any values. An entity's outputs are specially+  // marked arguments.+  if (this->getNumResults() > 0) {+    this->emitOpError("an entity cannot have return types.");+    return failure();+  }+  // Check that all operands are of signal type+  for (int i = 0, e = this->getNumFuncArguments(); i < e; ++i) {+    if (!llhd::SigType::kindof(this->getArgument(i).getType().getKind())) {+      this->emitOpError("usage of invalid argument type. Got ")+          << this->getArgument(i).getType() << ", expected LLHD signal type";+      return failure();+    }+  }+  return success();+}++LogicalResult mlir::llhd::EntityOp::verifyBody() {+  // Body must not be empty.+  if (this->isExternal()) {

Same here.

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Target/Verilog/VerilogPrinter.h"+#include "mlir/Support/LogicalResult.h"++using namespace mlir;++LogicalResult mlir::llhd::VerilogPrinter::printModule(ModuleOp module) {+  bool didFail = false;+  module.walk([&didFail, this](llhd::EntityOp entity) {+    // An EntityOp always has a single block+    Block &entryBlock = entity.body().front();++    // Print the module signature+    out << "module _" << entity.getName();+    if (!entryBlock.args_empty()) {+      out << "(";+      for (unsigned int i = 0; i < entryBlock.getNumArguments(); i++) {+        out << (i > 0 ? ", " : "")+            << (i < entity.ins().getZExtValue() ? "input " : "output ");+        printType(entryBlock.getArgument(i).getType());+        out << " " << getVariableName(entryBlock.getArgument(i));+      }+      out << ")";+    }+    out << ";\n";++    // Print the operations within the entity+    for (auto iter = entryBlock.begin();+         iter != entryBlock.end() && !dyn_cast<llhd::TerminatorOp>(iter);+         iter++) {+      if (failed(printOperation(&(*iter), 4))) {+        emitError(iter->getLoc(), "Operation not supported!");+        didFail = true;+        return;+      }+    }++    out << "endmodule\n";+    // Reset variable name counter as variables only have to be unique within a+    // module+    nextValueNum = 0;+  });+  // if printing of a single operation failed, fail the whole translation+  if (didFail)

nit: return failure(didFail);

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Dialect/LLHD/IR/LLHDOps.h"+#include "circt/Dialect/LLHD/IR/LLHDDialect.h"+#include "mlir/Dialect/CommonFolders.h"+#include "mlir/IR/Attributes.h"+#include "mlir/IR/Matchers.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/OpImplementation.h"+#include "mlir/IR/PatternMatch.h"+#include "mlir/IR/Region.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/IR/Types.h"+#include "mlir/IR/Value.h"+#include "mlir/Support/LogicalResult.h"+#include "llvm/ADT/ArrayRef.h"+#include "llvm/ADT/SmallVector.h"+#include "llvm/ADT/StringMap.h"++using namespace mlir;++namespace {++template <class AttrElementT,+          class ElementValueT = typename AttrElementT::ValueType,+          class CalculationT = function_ref<ElementValueT(ElementValueT)>>+Attribute constFoldUnaryOp(ArrayRef<Attribute> operands,+                           const CalculationT &calculate) {+  assert(operands.size() == 1 && "unary op takes one operand");+  if (!operands[0])+    return {};++  if (auto val = operands[0].dyn_cast<AttrElementT>()) {+    return AttrElementT::get(val.getType(), calculate(val.getValue()));+  } else if (auto val = operands[0].dyn_cast<SplatElementsAttr>()) {+    // Operand is a splat so we can avoid expanding the value out and+    // just fold based on the splat value.+    auto elementResult = calculate(val.getSplatValue<ElementValueT>());+    return DenseElementsAttr::get(val.getType(), elementResult);+  }+  if (auto val = operands[0].dyn_cast<ElementsAttr>()) {+    // Operand is ElementsAttr-derived; perform an element-wise fold by+    // expanding the values.+    auto valIt = val.getValues<ElementValueT>().begin();+    SmallVector<ElementValueT, 4> elementResults;+    elementResults.reserve(val.getNumElements());+    for (size_t i = 0, e = val.getNumElements(); i < e; ++i, ++valIt)+      elementResults.push_back(calculate(*valIt));+    return DenseElementsAttr::get(val.getType(), elementResults);+  }+  return {};+}++template <class AttrElementT,+          class ElementValueT = typename AttrElementT::ValueType,+          class CalculationT = function_ref<+              ElementValueT(ElementValueT, ElementValueT, ElementValueT)>>+Attribute constFoldTernaryOp(ArrayRef<Attribute> operands,+                             const CalculationT &calculate) {+  assert(operands.size() == 3 && "ternary op takes three operands");+  if (!operands[0] || !operands[1] || !operands[2])+    return {};+  if (operands[0].getType() != operands[1].getType())+    return {};+  if (operands[0].getType() != operands[2].getType())+    return {};++  if (operands[0].isa<AttrElementT>() && operands[1].isa<AttrElementT>() &&+      operands[2].isa<AttrElementT>()) {+    auto fst = operands[0].cast<AttrElementT>();+    auto snd = operands[1].cast<AttrElementT>();+    auto trd = operands[2].cast<AttrElementT>();++    return AttrElementT::get(+        fst.getType(),+        calculate(fst.getValue(), snd.getValue(), trd.getValue()));+  }+  if (operands[0].isa<SplatElementsAttr>() &&+      operands[1].isa<SplatElementsAttr>() &&+      operands[2].isa<SplatElementsAttr>()) {+    // Operands are splats so we can avoid expanding the values out and+    // just fold based on the splat value.+    auto fst = operands[0].cast<SplatElementsAttr>();+    auto snd = operands[1].cast<SplatElementsAttr>();+    auto trd = operands[2].cast<SplatElementsAttr>();++    auto elementResult = calculate(fst.getSplatValue<ElementValueT>(),+                                   snd.getSplatValue<ElementValueT>(),+                                   trd.getSplatValue<ElementValueT>());+    return DenseElementsAttr::get(fst.getType(), elementResult);+  }+  if (operands[0].isa<ElementsAttr>() && operands[1].isa<ElementsAttr>() &&+      operands[2].isa<ElementsAttr>()) {+    // Operands are ElementsAttr-derived; perform an element-wise fold by+    // expanding the values.+    auto fst = operands[0].cast<ElementsAttr>();+    auto snd = operands[1].cast<ElementsAttr>();+    auto trd = operands[2].cast<ElementsAttr>();++    auto fstIt = fst.getValues<ElementValueT>().begin();+    auto sndIt = snd.getValues<ElementValueT>().begin();+    auto trdIt = trd.getValues<ElementValueT>().begin();+    SmallVector<ElementValueT, 4> elementResults;+    elementResults.reserve(fst.getNumElements());+    for (size_t i = 0, e = fst.getNumElements(); i < e;+         ++i, ++fstIt, ++sndIt, ++trdIt)+      elementResults.push_back(calculate(*fstIt, *sndIt, *trdIt));+    return DenseElementsAttr::get(fst.getType(), elementResults);+  }+  return {};+}++struct constant_int_all_ones_matcher {+  bool match(Operation *op) {+    APInt value;+    return mlir::detail::constant_int_op_binder(&value).match(op) &&+           value.isAllOnesValue();+  }+};++} // anonymous namespace++//===---------------------------------------------------------------------===//+// LLHD Trait Helper Functions+//===---------------------------------------------------------------------===//++static bool sameKindArbitraryWidth(Type lhsType, Type rhsType) {+  return (lhsType.getKind() == rhsType.getKind()) &&+         (!lhsType.isa<ShapedType>() ||+          (lhsType.cast<ShapedType>().getElementType() ==+           rhsType.cast<ShapedType>().getElementType()));+}++//===---------------------------------------------------------------------===//+// LLHD Operations+//===---------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// ConstOp+//===----------------------------------------------------------------------===//++static ParseResult parseConstOp(OpAsmParser &parser, OperationState &result) {+  Attribute val;+  Type type;+  if (parser.parseAttribute(val, "value", result.attributes) ||+      parser.parseOptionalAttrDict(result.attributes))+    return failure();+  // parse the type for attributes that do not print the type by default+  if (parser.parseOptionalColon().value ||+      !parser.parseOptionalType(type).hasValue())+    type = val.getType();+  return parser.addTypeToList(val.getType(), result.types);+}++static void print(OpAsmPrinter &printer, llhd::ConstOp op) {+  printer << op.getOperationName() << " ";+  // The custom time attribute is not printing the attribute type by default for+  // some reason. Work around by printing the attribute without type, explicitly+  // followed by the operation type+  printer.printAttributeWithoutType(op.valueAttr());+  printer.printOptionalAttrDict(op.getAttrs(), {"value"});+  printer << " : ";+  printer.printType(op.getType());+}++OpFoldResult llhd::ConstOp::fold(ArrayRef<Attribute> operands) {+  assert(operands.empty() && "const has no operands");+  return value();+}++//===----------------------------------------------------------------------===//+// DextsOp+//===----------------------------------------------------------------------===//++unsigned llhd::DextsOp::getSliceWidth() {+  auto resultTy = result().getType();+  if (resultTy.isSignlessInteger()) {+    return resultTy.getIntOrFloatBitWidth();+  } else if (auto sigRes = resultTy.dyn_cast<llhd::SigType>()) {+    return sigRes.getUnderlyingType().getIntOrFloatBitWidth();+  } else if (auto vecRes = resultTy.dyn_cast<VectorType>()) {+    return vecRes.getNumElements();+  }+  return 0;+}++unsigned llhd::DextsOp::getTargetWidth() {+  auto targetTy = target().getType();+  if (targetTy.isSignlessInteger()) {+    return targetTy.getIntOrFloatBitWidth();+  } else if (auto sigRes = targetTy.dyn_cast<llhd::SigType>()) {+    return sigRes.getUnderlyingType().getIntOrFloatBitWidth();+  } else if (auto vecRes = targetTy.dyn_cast<VectorType>()) {+    return vecRes.getNumElements();+  }+  return 0;+}++//===----------------------------------------------------------------------===//+// NegOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::NegOp::fold(ArrayRef<Attribute> operands) {+  return constFoldUnaryOp<IntegerAttr>(operands, [](APInt a) { return -a; });+}++//===----------------------------------------------------------------------===//+// SModOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::SModOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.smod(x, 1) -> 0+  if (matchPattern(rhs(), m_One()))+    return Builder(getContext()).getZeroAttr(getType());++  /// llhd.smod(0, x) -> 0+  if (matchPattern(lhs(), m_Zero()))+    return Builder(getContext()).getZeroAttr(getType());++  /// llhs.smod(x,x) -> 0+  if (lhs() == rhs())+    return Builder(getContext()).getZeroAttr(getType());++  return constFoldBinaryOp<IntegerAttr>(operands, [](APInt lhs, APInt rhs) {+    APInt result = lhs.srem(rhs);+    if ((lhs.isNegative() && rhs.isNonNegative()) ||+        (lhs.isNonNegative() && rhs.isNegative())) {+      result += rhs;+    }+    return result;+  });+}++//===----------------------------------------------------------------------===//+// NotOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::NotOp::fold(ArrayRef<Attribute> operands) {+  return constFoldUnaryOp<IntegerAttr>(operands, [](APInt a) { return ~a; });+}++//===----------------------------------------------------------------------===//+// AndOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::AndOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.and(x, 0) -> 0+  if (matchPattern(rhs(), m_Zero()))+    return rhs();++  /// llhd.and(x, all_bits_set) -> x+  if (matchPattern(rhs(), constant_int_all_ones_matcher()))+    return lhs();++  // llhd.and(x, x) -> x+  if (rhs() == lhs())+    return rhs();++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a & b; });+}++//===----------------------------------------------------------------------===//+// OrOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::OrOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.or(x, 0) -> x+  if (matchPattern(rhs(), m_Zero()))+    return lhs();++  /// llhd.or(x, all_bits_set) -> all_bits_set+  if (matchPattern(rhs(), constant_int_all_ones_matcher()))+    return rhs();++  // llhd.or(x, x) -> x+  if (rhs() == lhs())+    return rhs();++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a | b; });+}++//===----------------------------------------------------------------------===//+// XorOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::XorOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.xor(x, 0) -> x+  if (matchPattern(rhs(), m_Zero()))+    return lhs();++  /// llhs.xor(x,x) -> 0+  if (lhs() == rhs())+    return Builder(getContext()).getZeroAttr(getType());++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a ^ b; });+}++//===----------------------------------------------------------------------===//+// ShlOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::ShlOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.shl(base, hidden, 0) -> base+  if (matchPattern(amount(), m_Zero()))+    return base();++  return constFoldTernaryOp<IntegerAttr>(+      operands, [](APInt base, APInt hidden, APInt amt) {+        base <<= amt;+        base += hidden.getHiBits(amt.getZExtValue());+        return base;+      });+}++static LogicalResult verify(llhd::ShlOp op) {+  if (op.base().getType() != op.result().getType()) {+    op.emitError("The output of the Shl operation is required to have the "+                 "same type as the base value (first operand), (")+        << op.base().getType() << " vs. " << op.result().getType() << ")";+    return failure();+  }++  // TODO: verify that T and Th only differ in the number of bits or elements++  return success();+}++//===----------------------------------------------------------------------===//+// ShrOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::ShrOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.shl(base, hidden, 0) -> base+  if (matchPattern(amount(), m_Zero()))+    return base();++  return constFoldTernaryOp<IntegerAttr>(+      operands, [](APInt base, APInt hidden, APInt amt) {+        base = base.getHiBits(base.getBitWidth() - amt.getZExtValue());+        hidden = hidden.getLoBits(amt.getZExtValue());+        hidden <<= base.getBitWidth() - amt.getZExtValue();+        return base + hidden;+      });+}++static LogicalResult verify(llhd::ShrOp op) {+  if (op.base().getType() != op.result().getType()) {+    op.emitError("The output of the Shr operation is required to have the "+                 "same type as the base value (first operand), (")+        << op.base().getType() << " vs. " << op.result().getType() << ")";+    return failure();+  }++  // TODO: verify that T and Th only differ in the number of bits or elements++  return success();+}++//===----------------------------------------------------------------------===//+// WaitOp+//===----------------------------------------------------------------------===//++// Implement this operation for the BranchOpInterface+Optional<MutableOperandRange>+llhd::WaitOp::getMutableSuccessorOperands(unsigned index) {+  assert(index == 0 && "invalid successor index");+  return destOpsMutable();+}++//===----------------------------------------------------------------------===//+// EntityOp+//===----------------------------------------------------------------------===//++/// Parse an argument list of an entity operation.+/// The argument list and argument types are returned in args and argTypes+/// respectively.+static ParseResult+parseArgumentList(OpAsmParser &parser,+                  SmallVectorImpl<OpAsmParser::OperandType> &args,+                  SmallVectorImpl<Type> &argTypes) {+  if (parser.parseLParen())+    return failure();++  do {+    OpAsmParser::OperandType argument;+    Type argType;+    if (succeeded(parser.parseOptionalRegionArgument(argument))) {+      if (!argument.name.empty() && succeeded(parser.parseColonType(argType))) {+        args.push_back(argument);+        argTypes.push_back(argType);+      }+    }+  } while (succeeded(parser.parseOptionalComma()));++  if (parser.parseRParen())+    return failure();++  return success();+}++/// parse an entity signature with syntax:+/// (%arg0 : T0, %arg1 : T1, <...>) -> (%out0 : T0, %out1 : T1, <...>)+static ParseResult+parseEntitySignature(OpAsmParser &parser, OperationState &result,+                     SmallVectorImpl<OpAsmParser::OperandType> &args,+                     SmallVectorImpl<Type> &argTypes) {+  if (parseArgumentList(parser, args, argTypes))+    return failure();+  // create the integer attribute with the number of inputs.+  IntegerAttr insAttr = parser.getBuilder().getI64IntegerAttr(args.size());+  result.addAttribute("ins", insAttr);+  if (parser.parseArrow() || parseArgumentList(parser, args, argTypes))+    return failure();++  return success();+}++static ParseResult parseEntityOp(OpAsmParser &parser, OperationState &result) {+  StringAttr entityName;+  SmallVector<OpAsmParser::OperandType, 4> args;+  SmallVector<Type, 4> argTypes;++  if (parser.parseSymbolName(entityName, SymbolTable::getSymbolAttrName(),+                             result.attributes))+    return failure();++  parseEntitySignature(parser, result, args, argTypes);++  if (parser.parseOptionalAttrDictWithKeyword(result.attributes))+    return failure();++  auto type = parser.getBuilder().getFunctionType(argTypes, llvm::None);+  result.addAttribute(mlir::llhd::EntityOp::getTypeAttrName(),+                      TypeAttr::get(type));++  auto *body = result.addRegion();+  parser.parseRegion(*body, args, argTypes);+  llhd::EntityOp::ensureTerminator(*body, parser.getBuilder(), result.location);+  return success();+}++static void printArgumentList(OpAsmPrinter &printer,+                              std::vector<BlockArgument> args) {+  printer << "(";+  for (size_t i = 0, e = args.size(); i < e; ++i) {+    printer << args[i] << " : ";+    printer.printType(args[i].getType());+    if (i < args.size() - 1)+      printer << ", ";+  }+  printer << ")";+}++static void print(OpAsmPrinter &printer, llhd::EntityOp op) {+  std::vector<BlockArgument> ins, outs;+  uint64_t n_ins = op.insAttr().getInt();+  for (uint64_t i = 0; i < op.body().front().getArguments().size(); ++i) {+    // no furter verification for the attribute type is required, already+    // handled by verify.+    if (i < n_ins) {+      ins.push_back(op.body().front().getArguments()[i]);+    } else {+      outs.push_back(op.body().front().getArguments()[i]);+    }+  }+  auto entityName =+      op.getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()).getValue();+  printer << op.getOperationName() << " ";+  printer.printSymbolName(entityName);+  printer << " ";+  printArgumentList(printer, ins);+  printer << " -> ";+  printArgumentList(printer, outs);+  printer.printOptionalAttrDictWithKeyword(+      op.getAttrs(),+      /*elidedAttrs =*/{SymbolTable::getSymbolAttrName(),+                        llhd::EntityOp::getTypeAttrName(), "ins"});+  printer.printRegion(op.body(), false, false);+}++static LogicalResult verify(llhd::EntityOp op) {+  uint64_t numArgs = op.getNumArguments();+  uint64_t nIns = op.insAttr().getInt();+  // check that there is at most one flag for each argument+  if (numArgs < nIns) {+    op.emitError("Cannot have more inputs than arguments, expected at most ")

Same here and a few other places.

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Target/Verilog/VerilogPrinter.h"+#include "mlir/Support/LogicalResult.h"++using namespace mlir;++LogicalResult mlir::llhd::VerilogPrinter::printModule(ModuleOp module) {+  bool didFail = false;+  module.walk([&didFail, this](llhd::EntityOp entity) {

nit: Could you use WalkResult here to early exit the walk?

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Dialect/LLHD/IR/LLHDOps.h"+#include "circt/Dialect/LLHD/IR/LLHDDialect.h"+#include "mlir/Dialect/CommonFolders.h"+#include "mlir/IR/Attributes.h"+#include "mlir/IR/Matchers.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/OpImplementation.h"+#include "mlir/IR/PatternMatch.h"+#include "mlir/IR/Region.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/IR/Types.h"+#include "mlir/IR/Value.h"+#include "mlir/Support/LogicalResult.h"+#include "llvm/ADT/ArrayRef.h"+#include "llvm/ADT/SmallVector.h"+#include "llvm/ADT/StringMap.h"++using namespace mlir;++namespace {++template <class AttrElementT,+          class ElementValueT = typename AttrElementT::ValueType,+          class CalculationT = function_ref<ElementValueT(ElementValueT)>>+Attribute constFoldUnaryOp(ArrayRef<Attribute> operands,+                           const CalculationT &calculate) {+  assert(operands.size() == 1 && "unary op takes one operand");+  if (!operands[0])+    return {};++  if (auto val = operands[0].dyn_cast<AttrElementT>()) {+    return AttrElementT::get(val.getType(), calculate(val.getValue()));+  } else if (auto val = operands[0].dyn_cast<SplatElementsAttr>()) {+    // Operand is a splat so we can avoid expanding the value out and+    // just fold based on the splat value.+    auto elementResult = calculate(val.getSplatValue<ElementValueT>());+    return DenseElementsAttr::get(val.getType(), elementResult);+  }+  if (auto val = operands[0].dyn_cast<ElementsAttr>()) {+    // Operand is ElementsAttr-derived; perform an element-wise fold by+    // expanding the values.+    auto valIt = val.getValues<ElementValueT>().begin();+    SmallVector<ElementValueT, 4> elementResults;+    elementResults.reserve(val.getNumElements());+    for (size_t i = 0, e = val.getNumElements(); i < e; ++i, ++valIt)+      elementResults.push_back(calculate(*valIt));+    return DenseElementsAttr::get(val.getType(), elementResults);+  }+  return {};+}++template <class AttrElementT,+          class ElementValueT = typename AttrElementT::ValueType,+          class CalculationT = function_ref<+              ElementValueT(ElementValueT, ElementValueT, ElementValueT)>>+Attribute constFoldTernaryOp(ArrayRef<Attribute> operands,+                             const CalculationT &calculate) {+  assert(operands.size() == 3 && "ternary op takes three operands");+  if (!operands[0] || !operands[1] || !operands[2])+    return {};+  if (operands[0].getType() != operands[1].getType())+    return {};+  if (operands[0].getType() != operands[2].getType())+    return {};++  if (operands[0].isa<AttrElementT>() && operands[1].isa<AttrElementT>() &&+      operands[2].isa<AttrElementT>()) {+    auto fst = operands[0].cast<AttrElementT>();+    auto snd = operands[1].cast<AttrElementT>();+    auto trd = operands[2].cast<AttrElementT>();++    return AttrElementT::get(+        fst.getType(),+        calculate(fst.getValue(), snd.getValue(), trd.getValue()));+  }+  if (operands[0].isa<SplatElementsAttr>() &&+      operands[1].isa<SplatElementsAttr>() &&+      operands[2].isa<SplatElementsAttr>()) {+    // Operands are splats so we can avoid expanding the values out and+    // just fold based on the splat value.+    auto fst = operands[0].cast<SplatElementsAttr>();+    auto snd = operands[1].cast<SplatElementsAttr>();+    auto trd = operands[2].cast<SplatElementsAttr>();++    auto elementResult = calculate(fst.getSplatValue<ElementValueT>(),+                                   snd.getSplatValue<ElementValueT>(),+                                   trd.getSplatValue<ElementValueT>());+    return DenseElementsAttr::get(fst.getType(), elementResult);+  }+  if (operands[0].isa<ElementsAttr>() && operands[1].isa<ElementsAttr>() &&+      operands[2].isa<ElementsAttr>()) {+    // Operands are ElementsAttr-derived; perform an element-wise fold by+    // expanding the values.+    auto fst = operands[0].cast<ElementsAttr>();+    auto snd = operands[1].cast<ElementsAttr>();+    auto trd = operands[2].cast<ElementsAttr>();++    auto fstIt = fst.getValues<ElementValueT>().begin();+    auto sndIt = snd.getValues<ElementValueT>().begin();+    auto trdIt = trd.getValues<ElementValueT>().begin();+    SmallVector<ElementValueT, 4> elementResults;+    elementResults.reserve(fst.getNumElements());+    for (size_t i = 0, e = fst.getNumElements(); i < e;+         ++i, ++fstIt, ++sndIt, ++trdIt)+      elementResults.push_back(calculate(*fstIt, *sndIt, *trdIt));+    return DenseElementsAttr::get(fst.getType(), elementResults);+  }+  return {};+}++struct constant_int_all_ones_matcher {+  bool match(Operation *op) {+    APInt value;+    return mlir::detail::constant_int_op_binder(&value).match(op) &&+           value.isAllOnesValue();+  }+};++} // anonymous namespace++//===---------------------------------------------------------------------===//+// LLHD Trait Helper Functions+//===---------------------------------------------------------------------===//++static bool sameKindArbitraryWidth(Type lhsType, Type rhsType) {+  return (lhsType.getKind() == rhsType.getKind()) &&+         (!lhsType.isa<ShapedType>() ||+          (lhsType.cast<ShapedType>().getElementType() ==+           rhsType.cast<ShapedType>().getElementType()));+}++//===---------------------------------------------------------------------===//+// LLHD Operations+//===---------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// ConstOp+//===----------------------------------------------------------------------===//++static ParseResult parseConstOp(OpAsmParser &parser, OperationState &result) {+  Attribute val;+  Type type;+  if (parser.parseAttribute(val, "value", result.attributes) ||+      parser.parseOptionalAttrDict(result.attributes))+    return failure();+  // parse the type for attributes that do not print the type by default+  if (parser.parseOptionalColon().value ||+      !parser.parseOptionalType(type).hasValue())+    type = val.getType();+  return parser.addTypeToList(val.getType(), result.types);+}++static void print(OpAsmPrinter &printer, llhd::ConstOp op) {+  printer << op.getOperationName() << " ";+  // The custom time attribute is not printing the attribute type by default for+  // some reason. Work around by printing the attribute without type, explicitly+  // followed by the operation type+  printer.printAttributeWithoutType(op.valueAttr());+  printer.printOptionalAttrDict(op.getAttrs(), {"value"});+  printer << " : ";+  printer.printType(op.getType());

Same here, you can just stream the type directly.

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Dialect/LLHD/IR/LLHDOps.h"+#include "circt/Dialect/LLHD/IR/LLHDDialect.h"+#include "mlir/Dialect/CommonFolders.h"+#include "mlir/IR/Attributes.h"+#include "mlir/IR/Matchers.h"+#include "mlir/IR/Module.h"+#include "mlir/IR/OpImplementation.h"+#include "mlir/IR/PatternMatch.h"+#include "mlir/IR/Region.h"+#include "mlir/IR/StandardTypes.h"+#include "mlir/IR/Types.h"+#include "mlir/IR/Value.h"+#include "mlir/Support/LogicalResult.h"+#include "llvm/ADT/ArrayRef.h"+#include "llvm/ADT/SmallVector.h"+#include "llvm/ADT/StringMap.h"++using namespace mlir;++namespace {++template <class AttrElementT,+          class ElementValueT = typename AttrElementT::ValueType,+          class CalculationT = function_ref<ElementValueT(ElementValueT)>>+Attribute constFoldUnaryOp(ArrayRef<Attribute> operands,+                           const CalculationT &calculate) {+  assert(operands.size() == 1 && "unary op takes one operand");+  if (!operands[0])+    return {};++  if (auto val = operands[0].dyn_cast<AttrElementT>()) {+    return AttrElementT::get(val.getType(), calculate(val.getValue()));+  } else if (auto val = operands[0].dyn_cast<SplatElementsAttr>()) {+    // Operand is a splat so we can avoid expanding the value out and+    // just fold based on the splat value.+    auto elementResult = calculate(val.getSplatValue<ElementValueT>());+    return DenseElementsAttr::get(val.getType(), elementResult);+  }+  if (auto val = operands[0].dyn_cast<ElementsAttr>()) {+    // Operand is ElementsAttr-derived; perform an element-wise fold by+    // expanding the values.+    auto valIt = val.getValues<ElementValueT>().begin();+    SmallVector<ElementValueT, 4> elementResults;+    elementResults.reserve(val.getNumElements());+    for (size_t i = 0, e = val.getNumElements(); i < e; ++i, ++valIt)+      elementResults.push_back(calculate(*valIt));+    return DenseElementsAttr::get(val.getType(), elementResults);+  }+  return {};+}++template <class AttrElementT,+          class ElementValueT = typename AttrElementT::ValueType,+          class CalculationT = function_ref<+              ElementValueT(ElementValueT, ElementValueT, ElementValueT)>>+Attribute constFoldTernaryOp(ArrayRef<Attribute> operands,+                             const CalculationT &calculate) {+  assert(operands.size() == 3 && "ternary op takes three operands");+  if (!operands[0] || !operands[1] || !operands[2])+    return {};+  if (operands[0].getType() != operands[1].getType())+    return {};+  if (operands[0].getType() != operands[2].getType())+    return {};++  if (operands[0].isa<AttrElementT>() && operands[1].isa<AttrElementT>() &&+      operands[2].isa<AttrElementT>()) {+    auto fst = operands[0].cast<AttrElementT>();+    auto snd = operands[1].cast<AttrElementT>();+    auto trd = operands[2].cast<AttrElementT>();++    return AttrElementT::get(+        fst.getType(),+        calculate(fst.getValue(), snd.getValue(), trd.getValue()));+  }+  if (operands[0].isa<SplatElementsAttr>() &&+      operands[1].isa<SplatElementsAttr>() &&+      operands[2].isa<SplatElementsAttr>()) {+    // Operands are splats so we can avoid expanding the values out and+    // just fold based on the splat value.+    auto fst = operands[0].cast<SplatElementsAttr>();+    auto snd = operands[1].cast<SplatElementsAttr>();+    auto trd = operands[2].cast<SplatElementsAttr>();++    auto elementResult = calculate(fst.getSplatValue<ElementValueT>(),+                                   snd.getSplatValue<ElementValueT>(),+                                   trd.getSplatValue<ElementValueT>());+    return DenseElementsAttr::get(fst.getType(), elementResult);+  }+  if (operands[0].isa<ElementsAttr>() && operands[1].isa<ElementsAttr>() &&+      operands[2].isa<ElementsAttr>()) {+    // Operands are ElementsAttr-derived; perform an element-wise fold by+    // expanding the values.+    auto fst = operands[0].cast<ElementsAttr>();+    auto snd = operands[1].cast<ElementsAttr>();+    auto trd = operands[2].cast<ElementsAttr>();++    auto fstIt = fst.getValues<ElementValueT>().begin();+    auto sndIt = snd.getValues<ElementValueT>().begin();+    auto trdIt = trd.getValues<ElementValueT>().begin();+    SmallVector<ElementValueT, 4> elementResults;+    elementResults.reserve(fst.getNumElements());+    for (size_t i = 0, e = fst.getNumElements(); i < e;+         ++i, ++fstIt, ++sndIt, ++trdIt)+      elementResults.push_back(calculate(*fstIt, *sndIt, *trdIt));+    return DenseElementsAttr::get(fst.getType(), elementResults);+  }+  return {};+}++struct constant_int_all_ones_matcher {+  bool match(Operation *op) {+    APInt value;+    return mlir::detail::constant_int_op_binder(&value).match(op) &&+           value.isAllOnesValue();+  }+};++} // anonymous namespace++//===---------------------------------------------------------------------===//+// LLHD Trait Helper Functions+//===---------------------------------------------------------------------===//++static bool sameKindArbitraryWidth(Type lhsType, Type rhsType) {+  return (lhsType.getKind() == rhsType.getKind()) &&+         (!lhsType.isa<ShapedType>() ||+          (lhsType.cast<ShapedType>().getElementType() ==+           rhsType.cast<ShapedType>().getElementType()));+}++//===---------------------------------------------------------------------===//+// LLHD Operations+//===---------------------------------------------------------------------===//++//===----------------------------------------------------------------------===//+// ConstOp+//===----------------------------------------------------------------------===//++static ParseResult parseConstOp(OpAsmParser &parser, OperationState &result) {+  Attribute val;+  Type type;+  if (parser.parseAttribute(val, "value", result.attributes) ||+      parser.parseOptionalAttrDict(result.attributes))+    return failure();+  // parse the type for attributes that do not print the type by default+  if (parser.parseOptionalColon().value ||+      !parser.parseOptionalType(type).hasValue())+    type = val.getType();+  return parser.addTypeToList(val.getType(), result.types);+}++static void print(OpAsmPrinter &printer, llhd::ConstOp op) {+  printer << op.getOperationName() << " ";+  // The custom time attribute is not printing the attribute type by default for+  // some reason. Work around by printing the attribute without type, explicitly+  // followed by the operation type+  printer.printAttributeWithoutType(op.valueAttr());+  printer.printOptionalAttrDict(op.getAttrs(), {"value"});+  printer << " : ";+  printer.printType(op.getType());+}++OpFoldResult llhd::ConstOp::fold(ArrayRef<Attribute> operands) {+  assert(operands.empty() && "const has no operands");+  return value();+}++//===----------------------------------------------------------------------===//+// DextsOp+//===----------------------------------------------------------------------===//++unsigned llhd::DextsOp::getSliceWidth() {+  auto resultTy = result().getType();+  if (resultTy.isSignlessInteger()) {+    return resultTy.getIntOrFloatBitWidth();+  } else if (auto sigRes = resultTy.dyn_cast<llhd::SigType>()) {+    return sigRes.getUnderlyingType().getIntOrFloatBitWidth();+  } else if (auto vecRes = resultTy.dyn_cast<VectorType>()) {+    return vecRes.getNumElements();+  }+  return 0;+}++unsigned llhd::DextsOp::getTargetWidth() {+  auto targetTy = target().getType();+  if (targetTy.isSignlessInteger()) {+    return targetTy.getIntOrFloatBitWidth();+  } else if (auto sigRes = targetTy.dyn_cast<llhd::SigType>()) {+    return sigRes.getUnderlyingType().getIntOrFloatBitWidth();+  } else if (auto vecRes = targetTy.dyn_cast<VectorType>()) {+    return vecRes.getNumElements();+  }+  return 0;+}++//===----------------------------------------------------------------------===//+// NegOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::NegOp::fold(ArrayRef<Attribute> operands) {+  return constFoldUnaryOp<IntegerAttr>(operands, [](APInt a) { return -a; });+}++//===----------------------------------------------------------------------===//+// SModOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::SModOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.smod(x, 1) -> 0+  if (matchPattern(rhs(), m_One()))+    return Builder(getContext()).getZeroAttr(getType());++  /// llhd.smod(0, x) -> 0+  if (matchPattern(lhs(), m_Zero()))+    return Builder(getContext()).getZeroAttr(getType());++  /// llhs.smod(x,x) -> 0+  if (lhs() == rhs())+    return Builder(getContext()).getZeroAttr(getType());++  return constFoldBinaryOp<IntegerAttr>(operands, [](APInt lhs, APInt rhs) {+    APInt result = lhs.srem(rhs);+    if ((lhs.isNegative() && rhs.isNonNegative()) ||+        (lhs.isNonNegative() && rhs.isNegative())) {+      result += rhs;+    }+    return result;+  });+}++//===----------------------------------------------------------------------===//+// NotOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::NotOp::fold(ArrayRef<Attribute> operands) {+  return constFoldUnaryOp<IntegerAttr>(operands, [](APInt a) { return ~a; });+}++//===----------------------------------------------------------------------===//+// AndOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::AndOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.and(x, 0) -> 0+  if (matchPattern(rhs(), m_Zero()))+    return rhs();++  /// llhd.and(x, all_bits_set) -> x+  if (matchPattern(rhs(), constant_int_all_ones_matcher()))+    return lhs();++  // llhd.and(x, x) -> x+  if (rhs() == lhs())+    return rhs();++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a & b; });+}++//===----------------------------------------------------------------------===//+// OrOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::OrOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.or(x, 0) -> x+  if (matchPattern(rhs(), m_Zero()))+    return lhs();++  /// llhd.or(x, all_bits_set) -> all_bits_set+  if (matchPattern(rhs(), constant_int_all_ones_matcher()))+    return rhs();++  // llhd.or(x, x) -> x+  if (rhs() == lhs())+    return rhs();++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a | b; });+}++//===----------------------------------------------------------------------===//+// XorOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::XorOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.xor(x, 0) -> x+  if (matchPattern(rhs(), m_Zero()))+    return lhs();++  /// llhs.xor(x,x) -> 0+  if (lhs() == rhs())+    return Builder(getContext()).getZeroAttr(getType());++  return constFoldBinaryOp<IntegerAttr>(operands,+                                        [](APInt a, APInt b) { return a ^ b; });+}++//===----------------------------------------------------------------------===//+// ShlOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::ShlOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.shl(base, hidden, 0) -> base+  if (matchPattern(amount(), m_Zero()))+    return base();++  return constFoldTernaryOp<IntegerAttr>(+      operands, [](APInt base, APInt hidden, APInt amt) {+        base <<= amt;+        base += hidden.getHiBits(amt.getZExtValue());+        return base;+      });+}++static LogicalResult verify(llhd::ShlOp op) {+  if (op.base().getType() != op.result().getType()) {+    op.emitError("The output of the Shl operation is required to have the "+                 "same type as the base value (first operand), (")+        << op.base().getType() << " vs. " << op.result().getType() << ")";+    return failure();+  }++  // TODO: verify that T and Th only differ in the number of bits or elements++  return success();+}++//===----------------------------------------------------------------------===//+// ShrOp+//===----------------------------------------------------------------------===//++OpFoldResult llhd::ShrOp::fold(ArrayRef<Attribute> operands) {+  /// llhd.shl(base, hidden, 0) -> base+  if (matchPattern(amount(), m_Zero()))+    return base();++  return constFoldTernaryOp<IntegerAttr>(+      operands, [](APInt base, APInt hidden, APInt amt) {+        base = base.getHiBits(base.getBitWidth() - amt.getZExtValue());+        hidden = hidden.getLoBits(amt.getZExtValue());+        hidden <<= base.getBitWidth() - amt.getZExtValue();+        return base + hidden;+      });+}++static LogicalResult verify(llhd::ShrOp op) {+  if (op.base().getType() != op.result().getType()) {+    op.emitError("The output of the Shr operation is required to have the "

Same here.

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Dialect/LLHD/IR/LLHDDialect.h"+#include "circt/Dialect/LLHD/IR/LLHDOps.h"+#include "mlir/IR/Builders.h"+#include "mlir/IR/DialectImplementation.h"+#include "mlir/Transforms/InliningUtils.h"+#include "llvm/ADT/ArrayRef.h"+#include "llvm/ADT/StringRef.h"++using namespace mlir;+using namespace mlir::llhd;++//===----------------------------------------------------------------------===//+// LLHDDialect Interfaces+//===----------------------------------------------------------------------===//++namespace {+/// This class defines the interface for handling inlining with LLHD operations.+struct LLHDInlinerInterface : public DialectInlinerInterface {+  using DialectInlinerInterface::DialectInlinerInterface;++  //===--------------------------------------------------------------------===//+  // Analysis Hooks+  //===--------------------------------------------------------------------===//++  /// All operations within LLHD can be inlined.+  bool isLegalToInline(Operation *, Region *,+                       BlockAndValueMapping &) const final {+    return true;+  }++  bool isLegalToInline(Region *, Region *src,+                       BlockAndValueMapping &) const final {+    // Don't inline processes and entities+    return !isa<llhd::ProcOp>(src->getParentOp()) &&+           !isa<llhd::EntityOp>(src->getParentOp());+  }+};+} // end anonymous namespace++//===----------------------------------------------------------------------===//+// LLHD Dialect+//===----------------------------------------------------------------------===//++LLHDDialect::LLHDDialect(mlir::MLIRContext *context)+    : Dialect(getDialectNamespace(), context) {+  addTypes<SigType, TimeType>();+  addAttributes<TimeAttr>();+  addOperations<+#define GET_OP_LIST+#include "circt/Dialect/LLHD/IR/LLHD.cpp.inc"+      >();+  addInterfaces<LLHDInlinerInterface>();+}++Operation *LLHDDialect::materializeConstant(OpBuilder &builder, Attribute value,+                                            Type type, Location loc) {+  return builder.create<llhd::ConstOp>(loc, type, value);+}++//===----------------------------------------------------------------------===//+// Type parsing+//===----------------------------------------------------------------------===//++/// Parse a signal type.+/// Syntax: sig ::= !llhd.sig<type>+static Type parseSigType(DialectAsmParser &parser) {+  Type underlyingType;+  if (parser.parseLess())+    return Type();++  llvm::SMLoc loc = parser.getCurrentLocation();+  if (parser.parseType(underlyingType)) {+    parser.emitError(loc, "No signal type found. Signal needs an underlying "+                          "type.");+    return nullptr;+  }++  if (parser.parseGreater())+    return Type();+  return SigType::get(underlyingType);+}++Type LLHDDialect::parseType(DialectAsmParser &parser) const {+  llvm::StringRef typeKeyword;+  // parse the type keyword first+  if (parser.parseKeyword(&typeKeyword))+    return Type();+  if (typeKeyword == SigType::getKeyword()) {+    return parseSigType(parser);+  }+  if (typeKeyword == TimeType::getKeyword())+    return TimeType::get(getContext());+  return Type();+}++//===----------------------------------------------------------------------===//+// Type printing+//===----------------------------------------------------------------------===//++/// Print a signal type with custom syntax:+/// type ::= !sig.type<underlying-type>+static void printSigType(SigType sig, DialectAsmPrinter &printer) {+  printer << sig.getKeyword() << "<";+  printer.printType(sig.getUnderlyingType());

nit: You can stream the type directly.

printer << ... << sig.getUnderlyingType() << ">";

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Conversion/LLHDToLLVM/LLHDToLLVM.h"+#include "circt/Dialect/LLHD/IR/LLHDDialect.h"+#include "circt/Dialect/LLHD/IR/LLHDOps.h"++#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h"+#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h"+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Dominance.h"+#include "mlir/Pass/Pass.h"+#include "mlir/Transforms/DialectConversion.h"++namespace mlir {+namespace llhd {+#define GEN_PASS_CLASSES+#include "circt/Conversion/LLHDToLLVM/Passes.h.inc"+} // namespace llhd+} // namespace mlir++using namespace mlir;+using namespace mlir::llhd;++// keep a counter to infer a signal's index in his entity's signal table+static int signalCounter = 0;+// keep a counter to infer the resume index after a wait instruction in a+// process+static int resumeCounter = 0;+//===----------------------------------------------------------------------===//+// Helpers+//===----------------------------------------------------------------------===//++/// Get an existing global string+static Value getGlobalString(Location loc, OpBuilder &builder,+                             LLVMTypeConverter &typeConverter,+                             LLVM::GlobalOp &str) {+  auto i8PtrTy = LLVM::LLVMType::getInt8PtrTy(typeConverter.getDialect());+  auto i32Ty = LLVM::LLVMType::getInt32Ty(typeConverter.getDialect());++  auto addr = builder.create<LLVM::AddressOfOp>(+      loc, str.getType().getPointerTo(), str.getName());+  auto idx = builder.create<LLVM::ConstantOp>(loc, i32Ty,+                                              builder.getI32IntegerAttr(0));+  llvm::SmallVector<Value, 2> idxs({idx, idx});+  auto gep = builder.create<LLVM::GEPOp>(loc, i8PtrTy, addr, idxs);+  return gep;+}++/// Looks up a symbol and inserts a new functino at the beginning of the+/// module's region in case the function does not exists. If+/// insertBodyAndTerminator is set, also adds the entry block and return+/// terminator+static LLVM::LLVMFuncOp+getOrInsertFunction(ModuleOp &module, ConversionPatternRewriter &rewriter,+                    std::string name, LLVM::LLVMType signature,+                    bool insertBodyAndTerminator = false) {+  auto func = module.lookupSymbol<LLVM::LLVMFuncOp>(name);+  if (!func) {+    OpBuilder moduleBuilder(module.getBodyRegion());+    func = moduleBuilder.create<LLVM::LLVMFuncOp>(rewriter.getUnknownLoc(),+                                                  name, signature);+    if (insertBodyAndTerminator) {+      func.addEntryBlock();+      OpBuilder b(func.getBody());+      b.create<LLVM::ReturnOp>(rewriter.getUnknownLoc(), ValueRange());+    }+  }+  return func;+}++/// Insert probe runtime call and extraction of details from the struct. The+/// mlir::Values of the details are returned, in struct-order.+static std::pair<Value, Value>+insertProbeSignal(ModuleOp &module, ConversionPatternRewriter &rewriter,+                  LLVM::LLVMDialect *dialect, Operation *op, Value statePtr,+                  Value signal) {+  auto i8PtrTy = LLVM::LLVMType::getInt8PtrTy(dialect);+  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);+  auto i64Ty = LLVM::LLVMType::getInt64Ty(dialect);+  auto sigTy = LLVM::LLVMType::getStructTy(dialect, {i8PtrTy, i64Ty});++  auto prbSignature = LLVM::LLVMType::getFunctionTy(sigTy.getPointerTo(),+                                                    {i8PtrTy, i32Ty}, false);+  auto prbFunc =+      getOrInsertFunction(module, rewriter, "probe_signal", prbSignature);+  SmallVector<Value, 2> prbArgs({statePtr, signal});+  auto prbCall =+      rewriter+          .create<LLVM::CallOp>(op->getLoc(), sigTy.getPointerTo(),+                                rewriter.getSymbolRefAttr(prbFunc), prbArgs)+          .getResult(0);+  auto zeroC = rewriter.create<LLVM::ConstantOp>(op->getLoc(), i32Ty,+                                                 rewriter.getI32IntegerAttr(0));+  auto oneC = rewriter.create<LLVM::ConstantOp>(op->getLoc(), i32Ty,+                                                rewriter.getI32IntegerAttr(1));++  auto sigPtrPtr =+      rewriter.create<LLVM::GEPOp>(op->getLoc(), i8PtrTy.getPointerTo(),+                                   prbCall, ArrayRef<Value>({zeroC, zeroC}));++  auto offsetPtr =+      rewriter.create<LLVM::GEPOp>(op->getLoc(), i64Ty.getPointerTo(), prbCall,+                                   ArrayRef<Value>({zeroC, oneC}));+  auto sigPtr = rewriter.create<LLVM::LoadOp>(op->getLoc(), i8PtrTy, sigPtrPtr);+  auto offset = rewriter.create<LLVM::LoadOp>(op->getLoc(), i64Ty, offsetPtr);++  return std::pair<Value, Value>(sigPtr, offset);+}++/// Gather the types of values that are used outside of the block they're+/// defined in. An LLVMType structure containing those types, in order of+/// appearance, is returned.+static LLVM::LLVMType getProcPersistenceTy(LLVM::LLVMDialect *dialect,+                                           LLVMTypeConverter &converter,+                                           ProcOp &proc) {+  SmallVector<LLVM::LLVMType, 3> types = SmallVector<LLVM::LLVMType, 3>();++  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);++  proc.walk([&](Operation *op) -> WalkResult {+    if (op->isUsedOutsideOfBlock(op->getBlock())) {+      if (op->getResult(0).getType().isa<IntegerType>())+        types.push_back(converter.convertType(op->getResult(0).getType())+                            .cast<LLVM::LLVMType>());+      else if (op->getResult(0).getType().isa<SigType>())+        types.push_back(i32Ty);+    }+    return WalkResult::advance();+  });+  return LLVM::LLVMType::getStructTy(dialect, types);+}++/// Insert a comparison block that either jumps to the trueDest block, if the+/// resume index mathces the current index, or to falseDest otherwise. If no+/// falseDest is provided, the next block is taken insead.+static void insertComparisonBlock(ConversionPatternRewriter &rewriter,+                                  LLVM::LLVMDialect *dialect, Location loc,+                                  Region *body, Value resumeIdx, int currIdx,+                                  Block *trueDest, Block *falseDest = nullptr) {+  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);+  // redirect entry block to a first comparison block. If it is a fresh start,+  // start from where original entry would have jumped, else the process is in+  // an illegal state and jump to the abort block+  auto secondBlock = ++body->begin();+  auto newBlock = rewriter.createBlock(body, secondBlock);+  auto cmpIdx = rewriter.create<LLVM::ConstantOp>(+      loc, i32Ty, rewriter.getI32IntegerAttr(currIdx));+  auto cmpRes = rewriter.create<LLVM::ICmpOp>(loc, LLVM::ICmpPredicate::eq,+                                              resumeIdx, cmpIdx);++  // default to jump to the next block for false+  if (!falseDest)+    falseDest = &*secondBlock;++  rewriter.create<LLVM::CondBrOp>(loc, cmpRes, trueDest, falseDest);++  // redirect entry block terminator to the new comparison block+  auto entryTer = body->front().getTerminator();+  entryTer->setSuccessor(newBlock, 0);+}++/// Insert the blocks and operations needed to persist values across suspension,+/// as well as ones needed to resume execution at the right spot.+static void insertPersistence(LLVMTypeConverter &converter,+                              ConversionPatternRewriter &rewriter,+                              LLVM::LLVMDialect *dialect, Location loc,+                              ProcOp &proc, LLVM::LLVMType &stateTy,+                              LLVM::LLVMFuncOp &converted) {+  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);+  DominanceInfo dom(converted);++  // load resume index+  rewriter.setInsertionPoint(converted.getBody().front().getTerminator());+  auto zeroC = rewriter.create<LLVM::ConstantOp>(loc, i32Ty,+                                                 rewriter.getI32IntegerAttr(0));+  auto oneC = rewriter.create<LLVM::ConstantOp>(loc, i32Ty,+                                                rewriter.getI32IntegerAttr(1));+  auto gep = rewriter.create<LLVM::GEPOp>(loc, i32Ty.getPointerTo(),+                                          converted.getArgument(1),+                                          ArrayRef<Value>({zeroC, oneC}));++  auto larg = rewriter.create<LLVM::LoadOp>(loc, i32Ty, gep);++  // insert an abort block as the last block+  auto abortBlock =+      rewriter.createBlock(&converted.getBody(), converted.getBody().end());+  rewriter.create<LLVM::ReturnOp>(loc, ValueRange());++  auto body = &converted.getBody();+  assert(body->front().getTerminator()->getNumSuccessors() > 0 &&+         "the process entry block is expected to branch to another block");+  // redirect entry block to a first comparison block. If on a fresh start,+  // start from where original entry would have jumped, else the process is in+  // an illegal state and jump to the abort block+  insertComparisonBlock(rewriter, dialect, loc, body, larg, 0,+                        body->front().getTerminator()->getSuccessor(0),+                        abortBlock);++  // keep track of the index in the presistence table of the operation we+  // are currently processing+  int i = 0;+  // keep track of the current resume index for comparison blocks+  int waitInd = 0;++  // insert persistence keeping for each operation escaping its parent block+  converted.walk([&](Operation *op) -> WalkResult {+    if (op->isUsedOutsideOfBlock(op->getBlock()) &&+        op->getResult(0) != larg.getResult() &&+        !(op->getResult(0).getType().isa<TimeType>())) {+      auto elemTy = stateTy.getStructElementType(3).getStructElementType(i);++      // store the value escaping it's definingn block in the persistence table+      rewriter.setInsertionPointAfter(op);+      auto zeroC0 = rewriter.create<LLVM::ConstantOp>(+          loc, i32Ty, rewriter.getI32IntegerAttr(0));+      auto threeC0 = rewriter.create<LLVM::ConstantOp>(+          loc, i32Ty, rewriter.getI32IntegerAttr(3));+      auto indC0 = rewriter.create<LLVM::ConstantOp>(+          loc, i32Ty, rewriter.getI32IntegerAttr(i));+      auto gep0 = rewriter.create<LLVM::GEPOp>(+          loc, elemTy.getPointerTo(), converted.getArgument(1),+          ArrayRef<Value>({zeroC0, threeC0, indC0}));+      rewriter.create<LLVM::StoreOp>(loc, op->getResult(0), gep0);++      // load the value from the persistence table and substitute the original+      // use with it+      for (auto &use : op->getUses()) {+        if (dom.properlyDominates(op->getBlock(), use.getOwner()->getBlock())) {+          auto user = use.getOwner();+          rewriter.setInsertionPointToStart(user->getBlock());+          auto zeroC1 = rewriter.create<LLVM::ConstantOp>(+              loc, i32Ty, rewriter.getI32IntegerAttr(0));+          auto threeC1 = rewriter.create<LLVM::ConstantOp>(+              loc, i32Ty, rewriter.getI32IntegerAttr(3));+          auto indC1 = rewriter.create<LLVM::ConstantOp>(+              loc, i32Ty, rewriter.getI32IntegerAttr(i));+          auto gep1 = rewriter.create<LLVM::GEPOp>(+              loc, elemTy.getPointerTo(), converted.getArgument(1),+              ArrayRef<Value>({zeroC1, threeC1, indC1}));+          auto load1 = rewriter.create<LLVM::LoadOp>(loc, elemTy, gep1);++          use.set(load1);+        }+      }+      i++;+    }++    // insert a comparison block for wait operations+    if (auto wait = dyn_cast<WaitOp>(op)) {+      insertComparisonBlock(rewriter, dialect, loc, body, larg, ++waitInd,+                            wait.dest());+    }+    return WalkResult::advance();+  });+}++//===----------------------------------------------------------------------===//+// Unit conversions+//===----------------------------------------------------------------------===//++namespace {+/// Convert an `llhd.entity` unit to LLVM. The result is an `llvm.func` which+/// takes a pointer to the state as arguments.+struct EntityOpConversion : public ConvertToLLVMPattern {+  explicit EntityOpConversion(MLIRContext *ctx,+                              LLVMTypeConverter &typeConverter)+      : ConvertToLLVMPattern(llhd::EntityOp::getOperationName(), ctx,+                             typeConverter) {}++  LogicalResult+  matchAndRewrite(Operation *op, ArrayRef<Value> operands,+                  ConversionPatternRewriter &rewriter) const override {+    // reset signal counter+    signalCounter = 0;+    // get adapted operands+    EntityOpAdaptor transformed(operands);+    // get entity operation+    auto entityOp = cast<EntityOp>(op);++    // collect llvm types+    auto voidTy = getVoidType();+    auto i8PtrTy = getVoidPtrType();+    auto i32Ty = LLVM::LLVMType::getInt32Ty(&getDialect());++    // have an intermediate signature conversion to add the arguments for the+    // state, signal table and argument table poitner arguments+    LLVMTypeConverter::SignatureConversion intermediate(+        entityOp.getNumArguments());+    // add state, signal table and arguments table arguments+    intermediate.addInputs(+        ArrayRef<Type>({i8PtrTy, i32Ty.getPointerTo(), i32Ty.getPointerTo()}));+    for (size_t i = 0, e = entityOp.getNumArguments(); i < e; ++i)+      intermediate.addInputs(i, voidTy);+    rewriter.applySignatureConversion(&entityOp.getBody(), intermediate);++    OpBuilder bodyBuilder =+        OpBuilder::atBlockBegin(&entityOp.getBlocks().front());+    LLVMTypeConverter::SignatureConversion final(+        intermediate.getConvertedTypes().size());+    final.addInputs(0, i8PtrTy);+    final.addInputs(1, i32Ty.getPointerTo());+    final.addInputs(2, i32Ty.getPointerTo());++    for (size_t i = 0, e = entityOp.getNumArguments(); i < e; ++i) {+      // create gep and load operations from arguments table for each original+      // argument+      auto index = bodyBuilder.create<LLVM::ConstantOp>(+          rewriter.getUnknownLoc(), i32Ty, rewriter.getI32IntegerAttr(i));+      auto bitcast = bodyBuilder.create<LLVM::GEPOp>(+          rewriter.getUnknownLoc(), i32Ty.getPointerTo(),+          entityOp.getArgument(2), ArrayRef<Value>(index));+      auto load =+          bodyBuilder.create<LLVM::LoadOp>(rewriter.getUnknownLoc(), bitcast);+      // remap i-th original argument to the loaded value+      final.remapInput(i + 3, load.getResult());+    }++    rewriter.applySignatureConversion(&entityOp.getBody(), final);++    // converted entity signature+    auto funcTy = LLVM::LLVMType::getFunctionTy(+        voidTy, {i8PtrTy, i32Ty.getPointerTo(), i32Ty.getPointerTo()}, false);+    // // create the llvm function+    auto llvmFunc = rewriter.create<LLVM::LLVMFuncOp>(+        op->getLoc(), entityOp.getName(), funcTy);++    // inline the entity region in the new llvm function+    rewriter.inlineRegionBefore(entityOp.getBody(), llvmFunc.getBody(),+                                llvmFunc.end());++    // erase original operation+    rewriter.eraseOp(op);++    return success();+  }+};+} // namespace++namespace {+/// Convert an `"llhd.terminator" operation to `llvm.return`.+struct TerminatorOpConversion : public ConvertToLLVMPattern {+  explicit TerminatorOpConversion(MLIRContext *ctx,+                                  LLVMTypeConverter &typeConverter)+      : ConvertToLLVMPattern(llhd::TerminatorOp::getOperationName(), ctx,+                             typeConverter) {}++  LogicalResult+  matchAndRewrite(Operation *op, ArrayRef<Value> operands,+                  ConversionPatternRewriter &rewriter) const override {+    // just replace the original op with return void+    rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, ValueRange());++    return success();+  }+};+} // namespace++namespace {+/// Convert an `llhd.proc` operation to llvm dialect. This inserts the required+/// logic to resume execution after an `llhd.wait` operation, as well as state+/// keeping for values that need to persist across suspension.+struct ProcOpConversion : public ConvertToLLVMPattern {+  explicit ProcOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter)+      : ConvertToLLVMPattern(ProcOp::getOperationName(), ctx, typeConverter) {}++  LogicalResult+  matchAndRewrite(Operation *op, ArrayRef<Value> operands,+                  ConversionPatternRewriter &rewriter) const override {+    auto procOp = cast<ProcOp>(op);++    // get adapted operands+    ProcOpAdaptor transformed(operands);++    // collect llvm types+    auto voidTy = getVoidType();+    auto i8PtrTy = getVoidPtrType();+    auto i1Ty = LLVM::LLVMType::getInt1Ty(&getDialect());+    auto i32Ty = LLVM::LLVMType::getInt32Ty(&getDialect());+    auto senseTableTy =+        LLVM::LLVMType::getArrayTy(i1Ty, procOp.insAttr().getInt())+            .getPointerTo();+    auto stateTy = LLVM::LLVMType::getStructTy(+        /* current instance  */ i8PtrTy, /* resume index */ i32Ty,+        /* sense flags */ senseTableTy, /* persistent types */+        getProcPersistenceTy(&getDialect(), typeConverter, procOp));++    // reset the resume index counter+    resumeCounter = 0;++    // have an intermediate signature conversion to add the arguments for the+    // state, process-specific state and signal arguments table+    LLVMTypeConverter::SignatureConversion intermediate(+        procOp.getNumArguments());+    // add state, process state table and arguments table arguments+    ArrayRef<Type> procSigTys(+        {i8PtrTy, stateTy.getPointerTo(), i32Ty.getPointerTo()});+    intermediate.addInputs(procSigTys);+    for (size_t i = 0, e = procOp.getNumArguments(); i < e; ++i)+      intermediate.addInputs(i, voidTy);+    rewriter.applySignatureConversion(&procOp.getBody(), intermediate);+    OpBuilder bodyBuilder =+        OpBuilder::atBlockBegin(&procOp.getBlocks().front());+    LLVMTypeConverter::SignatureConversion final(+        intermediate.getConvertedTypes().size());+    final.addInputs(0, i8PtrTy);+    final.addInputs(1, stateTy.getPointerTo());+    final.addInputs(2, i32Ty.getPointerTo());++    for (size_t i = 0, e = procOp.getNumArguments(); i < e; ++i) {+      // create gep and load operations from arguments table for each original+      // argument+      auto index = bodyBuilder.create<LLVM::ConstantOp>(+          op->getLoc(), i32Ty, rewriter.getI32IntegerAttr(i));+      auto bitcast = bodyBuilder.create<LLVM::GEPOp>(+          op->getLoc(), i32Ty.getPointerTo(), procOp.getArgument(2),+          ArrayRef<Value>({index}));+      auto load = bodyBuilder.create<LLVM::LoadOp>(op->getLoc(), bitcast);++      // remap i-th original argument to the loaded value+      final.remapInput(i + 3, load.getResult());+    }++    rewriter.applySignatureConversion(&procOp.getBody(), final);++    // converted entity signature+    auto funcTy = LLVM::LLVMType::getFunctionTy(+        voidTy, {i8PtrTy, stateTy.getPointerTo(), i32Ty.getPointerTo()}, false);+    // create the llvm function+    auto llvmFunc = rewriter.create<LLVM::LLVMFuncOp>(op->getLoc(),+                                                      procOp.getName(), funcTy);++    // inline the entity region in the new llvm function+    rewriter.inlineRegionBefore(procOp.getBody(), llvmFunc.getBody(),+                                llvmFunc.end());++    insertPersistence(typeConverter, rewriter, &getDialect(), op->getLoc(),+                      procOp, stateTy, llvmFunc);++    rewriter.eraseOp(op);++    return success();+  }+};+} // namespace++namespace {+/// Convert an `llhd.halt` operation to llvm dialect. This zeroes out all the+/// senses and returns, effectively making the process unable to be invoked+/// again.+struct HaltOpConversion : public ConvertToLLVMPattern {+  explicit HaltOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter)+      : ConvertToLLVMPattern(HaltOp::getOperationName(), ctx, typeConverter) {}++  LogicalResult+  matchAndRewrite(Operation *op, ArrayRef<Value> operands,+                  ConversionPatternRewriter &rewriter) const override {+    auto i1Ty = LLVM::LLVMType::getInt1Ty(&getDialect());+    auto i32Ty = LLVM::LLVMType::getInt32Ty(&getDialect());++    auto llvmFunc = op->getParentOfType<LLVM::LLVMFuncOp>();+    auto procState = llvmFunc.getArgument(1);+    auto senseTableTy = procState.getType()+                            .cast<LLVM::LLVMType>()+                            .getPointerElementTy()+                            .getStructElementType(2)+                            .getPointerElementTy();++    // get senses ptr from process state argument+    auto zeroC = rewriter.create<LLVM::ConstantOp>(+        op->getLoc(), i32Ty, rewriter.getI32IntegerAttr(0));+    auto twoC = rewriter.create<LLVM::ConstantOp>(+        op->getLoc(), i32Ty, rewriter.getI32IntegerAttr(2));+    auto sensePtrGep = rewriter.create<LLVM::GEPOp>(+        op->getLoc(), senseTableTy.getPointerTo().getPointerTo(), procState,+        ArrayRef<Value>({zeroC, twoC}));+    auto sensePtr = rewriter.create<LLVM::LoadOp>(+        op->getLoc(), senseTableTy.getPointerTo(), sensePtrGep);++    // zero out all the senses flags+    for (size_t i = 0, e = senseTableTy.getArrayNumElements(); i < e; ++i) {+      auto indC = rewriter.create<LLVM::ConstantOp>(+          op->getLoc(), i32Ty, rewriter.getI32IntegerAttr(i));+      auto zeroB = rewriter.create<LLVM::ConstantOp>(+          op->getLoc(), i1Ty, rewriter.getI32IntegerAttr(0));+      auto senseElemPtr = rewriter.create<LLVM::GEPOp>(+          op->getLoc(), i1Ty.getPointerTo(), sensePtr,+          ArrayRef<Value>({zeroC, indC}));+      rewriter.create<LLVM::StoreOp>(op->getLoc(), zeroB, senseElemPtr);+    }++    rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, ValueRange());+    return success();+  }+};+} // namespace++namespace {+/// Covnert an `llhd.wait` operation to llvm dialect. This sets the current+/// resume point, sets the observed senses (if present) and schedules the timed+/// wake up (if present).+struct WaitOpConversion : public ConvertToLLVMPattern {+  explicit WaitOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter)+      : ConvertToLLVMPattern(WaitOp::getOperationName(), ctx, typeConverter) {}++  LogicalResult+  matchAndRewrite(Operation *op, ArrayRef<Value> operands,+                  ConversionPatternRewriter &rewriter) const override {+    auto waitOp = cast<WaitOp>(op);+    WaitOpAdaptor transformed(operands, nullptr);+    auto llvmFunc = op->getParentOfType<LLVM::LLVMFuncOp>();++    auto voidTy = getVoidType();+    auto i8PtrTy = getVoidPtrType();+    auto i1Ty = LLVM::LLVMType::getInt1Ty(&getDialect());+    auto i32Ty = LLVM::LLVMType::getInt32Ty(&getDialect());++    // get llhd_suspend runtime function+    auto llhdSuspendTy = LLVM::LLVMType::getFunctionTy(+        voidTy, {i8PtrTy, i8PtrTy, i32Ty, i32Ty, i32Ty}, false);+    auto module = op->getParentOfType<ModuleOp>();+    auto llhdSuspendFunc =+        getOrInsertFunction(module, rewriter, "llhd_suspend", llhdSuspendTy);++    auto statePtr = llvmFunc.getArgument(0);+    auto procState = llvmFunc.getArgument(1);+    auto procStateTy = procState.getType().dyn_cast<LLVM::LLVMType>();+    auto senseTableTy = procStateTy.getPointerElementTy()+                            .getStructElementType(2)+                            .getPointerElementTy();++    // get senses ptr+    auto zeroC = rewriter.create<LLVM::ConstantOp>(+        op->getLoc(), i32Ty, rewriter.getI32IntegerAttr(0));+    auto oneC = rewriter.create<LLVM::ConstantOp>(+        op->getLoc(), i32Ty, rewriter.getI32IntegerAttr(1));+    auto twoC = rewriter.create<LLVM::ConstantOp>(+        op->getLoc(), i32Ty, rewriter.getI32IntegerAttr(2));+    auto sensePtrGep = rewriter.create<LLVM::GEPOp>(+        op->getLoc(), senseTableTy.getPointerTo().getPointerTo(), procState,+        ArrayRef<Value>({zeroC, twoC}));+    auto sensePtr = rewriter.create<LLVM::LoadOp>(+        op->getLoc(), senseTableTy.getPointerTo(), sensePtrGep);++    // set senses flags+    // TODO: actually handle observed signals+    for (size_t i = 0, e = senseTableTy.getArrayNumElements(); i < e; ++i) {+      auto indC = rewriter.create<LLVM::ConstantOp>(+          op->getLoc(), i32Ty, rewriter.getI32IntegerAttr(i));+      auto zeroB = rewriter.create<LLVM::ConstantOp>(+          op->getLoc(), i1Ty, rewriter.getI32IntegerAttr(0));+      auto senseElemPtr = rewriter.create<LLVM::GEPOp>(+          op->getLoc(), i1Ty.getPointerTo(), sensePtr,+          ArrayRef<Value>({zeroC, indC}));+      rewriter.create<LLVM::StoreOp>(op->getLoc(), zeroB, senseElemPtr);+    }++    // get time constats, if present+    Value realTimeConst;+    Value deltaConst;+    Value epsConst;+    if (waitOp.timeMutable().size() > 0) {+      auto timeAttr = cast<llhd::ConstOp>(waitOp.time().getDefiningOp())+                          .valueAttr()+                          .dyn_cast<TimeAttr>();+      // get real time as an attribute+      auto realTimeAttr = rewriter.getI32IntegerAttr(timeAttr.getTime());+      // create new time const operation+      realTimeConst =+          rewriter.create<LLVM::ConstantOp>(op->getLoc(), i32Ty, realTimeAttr);+      // get the delta step as an attribute+      auto deltaAttr = rewriter.getI32IntegerAttr(timeAttr.getDelta());+      // create new delta const operation+      deltaConst =+          rewriter.create<LLVM::ConstantOp>(op->getLoc(), i32Ty, deltaAttr);+      // get the epsilon step as an attribute+      auto epsAttr = rewriter.getI32IntegerAttr(timeAttr.getEps());+      // create new eps const operation+      epsConst =+          rewriter.create<LLVM::ConstantOp>(op->getLoc(), i32Ty, epsAttr);+    }++    auto procStateBC =+        rewriter.create<LLVM::BitcastOp>(op->getLoc(), i8PtrTy, procState);+    auto resumeIdxC = rewriter.create<LLVM::ConstantOp>(+        op->getLoc(), i32Ty, rewriter.getI32IntegerAttr(++resumeCounter));+    auto resumeIdxPtr =+        rewriter.create<LLVM::GEPOp>(op->getLoc(), i32Ty.getPointerTo(),+                                     procState, ArrayRef<Value>({zeroC, oneC}));+    rewriter.create<LLVM::StoreOp>(op->getLoc(), resumeIdxC, resumeIdxPtr);++    SmallVector<Value, 5> args(+        {statePtr, procStateBC, realTimeConst, deltaConst, epsConst});+    rewriter.create<LLVM::CallOp>(+        op->getLoc(), voidTy, rewriter.getSymbolRefAttr(llhdSuspendFunc), args);++    rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, ValueRange());+    return success();+  }+};+} // namespace++namespace {+/// Lower an llhd.inst operation to llvm. This generates malloc calls and+/// alloc_signal calls (to store the pointer into the state) for each signal in+/// the instantiated entity.+struct InstOpConversion : public ConvertToLLVMPattern {+  explicit InstOpConversion(MLIRContext *ctx, LLVMTypeConverter &typeConverter)+      : ConvertToLLVMPattern(InstOp::getOperationName(), ctx, typeConverter) {}++  LogicalResult+  matchAndRewrite(Operation *op, ArrayRef<Value> operands,+                  ConversionPatternRewriter &rewriter) const override {+    // get inst operation+    auto instOp = cast<InstOp>(op);+    // get parent module+    auto module = op->getParentOfType<ModuleOp>();++    auto voidTy = getVoidType();+    auto i8PtrTy = getVoidPtrType();+    auto i1Ty = LLVM::LLVMType::getInt1Ty(&getDialect());+    auto i32Ty = LLVM::LLVMType::getInt32Ty(&getDialect());+    auto i64Ty = LLVM::LLVMType::getInt64Ty(&getDialect());++    // init function signature: (i8* %state) -> void+    auto initFuncTy = LLVM::LLVMType::getFunctionTy(voidTy, {i8PtrTy}, false);+    auto initFunc =+        getOrInsertFunction(module, rewriter, initCall, initFuncTy, true);++    //! get or insert malloc function definition+    // malloc function signature: (i64 %size) -> i8* %pointer+    auto mallocSigFuncTy =+        LLVM::LLVMType::getFunctionTy(i8PtrTy, {i64Ty}, false);+    auto mallFunc =+        getOrInsertFunction(module, rewriter, "malloc", mallocSigFuncTy);++    //! get or insert library call definition+    // alloc_signal function signature: (i8* %state, i8* %sig_name, i8*+    // %sig_owner, i32 %value) -> i32 %sig_index+    auto allocSigFuncTy = LLVM::LLVMType::getFunctionTy(+        i32Ty, {i8PtrTy, i32Ty, i8PtrTy, i8PtrTy, i64Ty}, false);+    auto sigFunc =+        getOrInsertFunction(module, rewriter, allocCall, allocSigFuncTy);++    // get or insert alloc_proc library call definition+    auto allocProcFuncTy = LLVM::LLVMType::getFunctionTy(+        voidTy, {i8PtrTy, i8PtrTy, i8PtrTy}, false);+    auto allocProcFunc =+        getOrInsertFunction(module, rewriter, "alloc_proc", allocProcFuncTy);++    Value initStatePtr = initFunc.getArgument(0);++    // get builder with insertion point before the init function terminator+    OpBuilder initBuilder =+        OpBuilder::atBlockTerminator(&initFunc.getBody().getBlocks().front());++    // use the instance name to retrieve the instance from the state+    auto ownerName = instOp.name();+    //! get or create owner name string+    Value owner;++    auto parentSym =+        module.lookupSymbol<LLVM::GlobalOp>("instance." + ownerName.str());+    if (!parentSym) {+      owner = LLVM::createGlobalString(+          rewriter.getUnknownLoc(), initBuilder, "instance." + ownerName.str(),+          ownerName.str() + '\0', LLVM::Linkage::Internal,+          typeConverter.getDialect());+      parentSym =+          module.lookupSymbol<LLVM::GlobalOp>("instance." + ownerName.str());+    } else {+      owner = getGlobalString(rewriter.getUnknownLoc(), initBuilder,+                              typeConverter, parentSym);+    }++    if (auto child = module.lookupSymbol<EntityOp>(instOp.callee())) {+      // walk over the unit and generate mallocs for each one of its signals+      // index of the signal in the unit's signal table+      int initCounter = 0;+      child.walk([&](Operation *op) -> WalkResult {+        if (auto sigOp = dyn_cast<SigOp>(op)) {+          // get index constant of the signal in the unit's signal table+          auto indexConst = initBuilder.create<LLVM::ConstantOp>(+              rewriter.getUnknownLoc(), i32Ty,+              rewriter.getI32IntegerAttr(initCounter));+          initCounter++;++          //! add signal allocation to the init function+          // clone and insert init's defining operation (assmued to be a+          // constant op)+          auto initDef =+              initBuilder.insert(sigOp.init().getDefiningOp()->clone())+                  ->getResult(0);+          // malloc required space+          int size = llvm::divideCeil(+              sigOp.init().getType().getIntOrFloatBitWidth(), 8);+          auto sizeConst = initBuilder.create<LLVM::ConstantOp>(+              rewriter.getUnknownLoc(), i64Ty,+              rewriter.getI64IntegerAttr(size));+          // malloc an extra byte to avoid segfaulting when loading an offset+          // signal+          auto mallocSize = initBuilder.create<LLVM::ConstantOp>(+              rewriter.getUnknownLoc(), i64Ty,+              rewriter.getI64IntegerAttr(size * 2));+          llvm::SmallVector<Value, 1> margs({mallocSize});+          auto mall = initBuilder+                          .create<LLVM::CallOp>(+                              rewriter.getUnknownLoc(), i8PtrTy,+                              rewriter.getSymbolRefAttr(mallFunc), margs)+                          .getResult(0);+          auto bitcast = initBuilder.create<LLVM::BitcastOp>(+              rewriter.getUnknownLoc(),+              typeConverter.convertType(sigOp.init().getType())+                  .cast<LLVM::LLVMType>()+                  .getPointerTo(),+              mall);+          initBuilder.create<LLVM::StoreOp>(rewriter.getUnknownLoc(), initDef,+                                            bitcast);++          llvm::SmallVector<Value, 5> args(+              {initStatePtr, indexConst, owner, mall, sizeConst});+          initBuilder.create<LLVM::CallOp>(rewriter.getUnknownLoc(), i32Ty,+                                           rewriter.getSymbolRefAttr(sigFunc),+                                           args);+        }+        return WalkResult::advance();+      });+    } else if (auto proc = module.lookupSymbol<ProcOp>(instOp.callee())) {+      auto sensesPtrTy =+          LLVM::LLVMType::getArrayTy(i1Ty, instOp.inputs().size())+              .getPointerTo();+      auto procStatePtrTy =+          LLVM::LLVMType::getStructTy(+              i8PtrTy, i32Ty, sensesPtrTy,+              getProcPersistenceTy(&getDialect(), typeConverter, proc))+              .getPointerTo();++      auto zeroC = initBuilder.create<LLVM::ConstantOp>(+          op->getLoc(), i32Ty, rewriter.getI32IntegerAttr(0));+      auto oneC = initBuilder.create<LLVM::ConstantOp>(+          op->getLoc(), i32Ty, rewriter.getI32IntegerAttr(1));+      auto twoC = initBuilder.create<LLVM::ConstantOp>(+          op->getLoc(), i32Ty, rewriter.getI32IntegerAttr(2));++      // malloc space for proc state+      auto procStateNullPtr =+          initBuilder.create<LLVM::NullOp>(op->getLoc(), procStatePtrTy);+      auto procStateGep = initBuilder.create<LLVM::GEPOp>(+          op->getLoc(), procStatePtrTy, procStateNullPtr,+          ArrayRef<Value>({oneC}));+      auto procStateSize = initBuilder.create<LLVM::PtrToIntOp>(+          op->getLoc(), i64Ty, procStateGep);+      llvm::SmallVector<Value, 1> procStateMArgs({procStateSize});+      auto procStateMall =+          initBuilder+              .create<LLVM::CallOp>(op->getLoc(), i8PtrTy,+                                    rewriter.getSymbolRefAttr(mallFunc),+                                    procStateMArgs)+              .getResult(0);++      auto procStateBC = initBuilder.create<LLVM::BitcastOp>(+          op->getLoc(), procStatePtrTy, procStateMall);++      // malloc space for owner name+      auto strSizeC = initBuilder.create<LLVM::ConstantOp>(+          op->getLoc(), i64Ty, rewriter.getI64IntegerAttr(ownerName.size()));++      llvm::SmallVector<Value, 1> strMallArgs({strSizeC});+      auto strMall = initBuilder+                         .create<LLVM::CallOp>(+                             op->getLoc(), i8PtrTy,+                             rewriter.getSymbolRefAttr(mallFunc), strMallArgs)+                         .getResult(0);+      auto procStateOwnerPtr = initBuilder.create<LLVM::GEPOp>(+          op->getLoc(), i8PtrTy.getPointerTo(), procStateBC,+          ArrayRef<Value>({zeroC, zeroC}));+      initBuilder.create<LLVM::StoreOp>(op->getLoc(), strMall,+                                        procStateOwnerPtr);++      // store initial resume index+      auto resumeGep = initBuilder.create<LLVM::GEPOp>(+          op->getLoc(), i32Ty.getPointerTo(), procStateBC,+          ArrayRef<Value>({zeroC, oneC}));+      initBuilder.create<LLVM::StoreOp>(op->getLoc(), zeroC, resumeGep);++      // malloc space for senses+      auto sensesNullPtr =+          initBuilder.create<LLVM::NullOp>(op->getLoc(), sensesPtrTy);+      auto sensesGep = initBuilder.create<LLVM::GEPOp>(+          op->getLoc(), sensesPtrTy, sensesNullPtr, ArrayRef<Value>({oneC}));+      auto sensesSize =+          initBuilder.create<LLVM::PtrToIntOp>(op->getLoc(), i64Ty, sensesGep);+      SmallVector<Value, 1> senseMArgs({sensesSize});+      auto sensesMall = initBuilder+                            .create<LLVM::CallOp>(+                                op->getLoc(), i8PtrTy,+                                rewriter.getSymbolRefAttr(mallFunc), senseMArgs)+                            .getResult(0);++      auto sensesBC = initBuilder.create<LLVM::BitcastOp>(+          op->getLoc(), sensesPtrTy, sensesMall);++      // set initial senses to 1+      for (size_t i = 0,+                  e = sensesPtrTy.getPointerElementTy().getArrayNumElements();+           i < e; ++i) {+        auto oneB = initBuilder.create<LLVM::ConstantOp>(+            op->getLoc(), i1Ty, rewriter.getBoolAttr(true));+        auto gepInd = initBuilder.create<LLVM::ConstantOp>(+            op->getLoc(), i32Ty, rewriter.getI32IntegerAttr(i));+        auto senseGep = initBuilder.create<LLVM::GEPOp>(+            op->getLoc(), i1Ty.getPointerTo(), sensesBC,+            ArrayRef<Value>({zeroC, gepInd}));+        initBuilder.create<LLVM::StoreOp>(op->getLoc(), oneB, senseGep);+      }++      // store senses ptr in procstate+      auto procStateSensesPtr = initBuilder.create<LLVM::GEPOp>(+          op->getLoc(), sensesPtrTy.getPointerTo(), procStateBC,+          ArrayRef<Value>({zeroC, twoC}));+      initBuilder.create<LLVM::StoreOp>(op->getLoc(), sensesBC,+                                        procStateSensesPtr);++      SmallVector<Value, 4> allocProcArgs({initStatePtr, owner, procStateMall});+      initBuilder.create<LLVM::CallOp>(op->getLoc(), voidTy,+                                       rewriter.getSymbolRefAttr(allocProcFunc),+                                       allocProcArgs);+    }++    rewriter.eraseOp(op);+    return success();+  }++private:+  const std::string allocCall = "alloc_signal";

nit: Use StringRef instead.

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Conversion/LLHDToLLVM/LLHDToLLVM.h"+#include "circt/Dialect/LLHD/IR/LLHDDialect.h"+#include "circt/Dialect/LLHD/IR/LLHDOps.h"++#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h"+#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h"+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Dominance.h"+#include "mlir/Pass/Pass.h"+#include "mlir/Transforms/DialectConversion.h"++namespace mlir {+namespace llhd {+#define GEN_PASS_CLASSES+#include "circt/Conversion/LLHDToLLVM/Passes.h.inc"+} // namespace llhd+} // namespace mlir++using namespace mlir;+using namespace mlir::llhd;++// keep a counter to infer a signal's index in his entity's signal table+static int signalCounter = 0;+// keep a counter to infer the resume index after a wait instruction in a+// process+static int resumeCounter = 0;+//===----------------------------------------------------------------------===//+// Helpers+//===----------------------------------------------------------------------===//++/// Get an existing global string+static Value getGlobalString(Location loc, OpBuilder &builder,+                             LLVMTypeConverter &typeConverter,+                             LLVM::GlobalOp &str) {+  auto i8PtrTy = LLVM::LLVMType::getInt8PtrTy(typeConverter.getDialect());+  auto i32Ty = LLVM::LLVMType::getInt32Ty(typeConverter.getDialect());++  auto addr = builder.create<LLVM::AddressOfOp>(+      loc, str.getType().getPointerTo(), str.getName());+  auto idx = builder.create<LLVM::ConstantOp>(loc, i32Ty,+                                              builder.getI32IntegerAttr(0));+  llvm::SmallVector<Value, 2> idxs({idx, idx});+  auto gep = builder.create<LLVM::GEPOp>(loc, i8PtrTy, addr, idxs);+  return gep;+}++/// Looks up a symbol and inserts a new functino at the beginning of the+/// module's region in case the function does not exists. If+/// insertBodyAndTerminator is set, also adds the entry block and return+/// terminator+static LLVM::LLVMFuncOp+getOrInsertFunction(ModuleOp &module, ConversionPatternRewriter &rewriter,+                    std::string name, LLVM::LLVMType signature,+                    bool insertBodyAndTerminator = false) {+  auto func = module.lookupSymbol<LLVM::LLVMFuncOp>(name);+  if (!func) {+    OpBuilder moduleBuilder(module.getBodyRegion());+    func = moduleBuilder.create<LLVM::LLVMFuncOp>(rewriter.getUnknownLoc(),+                                                  name, signature);+    if (insertBodyAndTerminator) {+      func.addEntryBlock();+      OpBuilder b(func.getBody());+      b.create<LLVM::ReturnOp>(rewriter.getUnknownLoc(), ValueRange());+    }+  }+  return func;+}++/// Insert probe runtime call and extraction of details from the struct. The+/// mlir::Values of the details are returned, in struct-order.+static std::pair<Value, Value>+insertProbeSignal(ModuleOp &module, ConversionPatternRewriter &rewriter,+                  LLVM::LLVMDialect *dialect, Operation *op, Value statePtr,+                  Value signal) {+  auto i8PtrTy = LLVM::LLVMType::getInt8PtrTy(dialect);+  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);+  auto i64Ty = LLVM::LLVMType::getInt64Ty(dialect);+  auto sigTy = LLVM::LLVMType::getStructTy(dialect, {i8PtrTy, i64Ty});++  auto prbSignature = LLVM::LLVMType::getFunctionTy(sigTy.getPointerTo(),+                                                    {i8PtrTy, i32Ty}, false);+  auto prbFunc =+      getOrInsertFunction(module, rewriter, "probe_signal", prbSignature);+  SmallVector<Value, 2> prbArgs({statePtr, signal});+  auto prbCall =+      rewriter+          .create<LLVM::CallOp>(op->getLoc(), sigTy.getPointerTo(),+                                rewriter.getSymbolRefAttr(prbFunc), prbArgs)+          .getResult(0);+  auto zeroC = rewriter.create<LLVM::ConstantOp>(op->getLoc(), i32Ty,+                                                 rewriter.getI32IntegerAttr(0));+  auto oneC = rewriter.create<LLVM::ConstantOp>(op->getLoc(), i32Ty,+                                                rewriter.getI32IntegerAttr(1));++  auto sigPtrPtr =+      rewriter.create<LLVM::GEPOp>(op->getLoc(), i8PtrTy.getPointerTo(),+                                   prbCall, ArrayRef<Value>({zeroC, zeroC}));++  auto offsetPtr =+      rewriter.create<LLVM::GEPOp>(op->getLoc(), i64Ty.getPointerTo(), prbCall,+                                   ArrayRef<Value>({zeroC, oneC}));+  auto sigPtr = rewriter.create<LLVM::LoadOp>(op->getLoc(), i8PtrTy, sigPtrPtr);+  auto offset = rewriter.create<LLVM::LoadOp>(op->getLoc(), i64Ty, offsetPtr);++  return std::pair<Value, Value>(sigPtr, offset);+}++/// Gather the types of values that are used outside of the block they're+/// defined in. An LLVMType structure containing those types, in order of+/// appearance, is returned.+static LLVM::LLVMType getProcPersistenceTy(LLVM::LLVMDialect *dialect,+                                           LLVMTypeConverter &converter,+                                           ProcOp &proc) {+  SmallVector<LLVM::LLVMType, 3> types = SmallVector<LLVM::LLVMType, 3>();++  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);++  proc.walk([&](Operation *op) -> WalkResult {+    if (op->isUsedOutsideOfBlock(op->getBlock())) {+      if (op->getResult(0).getType().isa<IntegerType>())+        types.push_back(converter.convertType(op->getResult(0).getType())+                            .cast<LLVM::LLVMType>());+      else if (op->getResult(0).getType().isa<SigType>())+        types.push_back(i32Ty);+    }+    return WalkResult::advance();+  });+  return LLVM::LLVMType::getStructTy(dialect, types);+}++/// Insert a comparison block that either jumps to the trueDest block, if the+/// resume index mathces the current index, or to falseDest otherwise. If no+/// falseDest is provided, the next block is taken insead.+static void insertComparisonBlock(ConversionPatternRewriter &rewriter,+                                  LLVM::LLVMDialect *dialect, Location loc,+                                  Region *body, Value resumeIdx, int currIdx,+                                  Block *trueDest, Block *falseDest = nullptr) {+  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);+  // redirect entry block to a first comparison block. If it is a fresh start,+  // start from where original entry would have jumped, else the process is in+  // an illegal state and jump to the abort block+  auto secondBlock = ++body->begin();+  auto newBlock = rewriter.createBlock(body, secondBlock);+  auto cmpIdx = rewriter.create<LLVM::ConstantOp>(+      loc, i32Ty, rewriter.getI32IntegerAttr(currIdx));+  auto cmpRes = rewriter.create<LLVM::ICmpOp>(loc, LLVM::ICmpPredicate::eq,+                                              resumeIdx, cmpIdx);++  // default to jump to the next block for false+  if (!falseDest)+    falseDest = &*secondBlock;++  rewriter.create<LLVM::CondBrOp>(loc, cmpRes, trueDest, falseDest);++  // redirect entry block terminator to the new comparison block+  auto entryTer = body->front().getTerminator();+  entryTer->setSuccessor(newBlock, 0);+}++/// Insert the blocks and operations needed to persist values across suspension,+/// as well as ones needed to resume execution at the right spot.+static void insertPersistence(LLVMTypeConverter &converter,+                              ConversionPatternRewriter &rewriter,+                              LLVM::LLVMDialect *dialect, Location loc,+                              ProcOp &proc, LLVM::LLVMType &stateTy,+                              LLVM::LLVMFuncOp &converted) {+  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);+  DominanceInfo dom(converted);++  // load resume index+  rewriter.setInsertionPoint(converted.getBody().front().getTerminator());+  auto zeroC = rewriter.create<LLVM::ConstantOp>(loc, i32Ty,+                                                 rewriter.getI32IntegerAttr(0));+  auto oneC = rewriter.create<LLVM::ConstantOp>(loc, i32Ty,+                                                rewriter.getI32IntegerAttr(1));+  auto gep = rewriter.create<LLVM::GEPOp>(loc, i32Ty.getPointerTo(),+                                          converted.getArgument(1),+                                          ArrayRef<Value>({zeroC, oneC}));++  auto larg = rewriter.create<LLVM::LoadOp>(loc, i32Ty, gep);++  // insert an abort block as the last block+  auto abortBlock =+      rewriter.createBlock(&converted.getBody(), converted.getBody().end());+  rewriter.create<LLVM::ReturnOp>(loc, ValueRange());++  auto body = &converted.getBody();+  assert(body->front().getTerminator()->getNumSuccessors() > 0 &&+         "the process entry block is expected to branch to another block");+  // redirect entry block to a first comparison block. If on a fresh start,+  // start from where original entry would have jumped, else the process is in+  // an illegal state and jump to the abort block+  insertComparisonBlock(rewriter, dialect, loc, body, larg, 0,+                        body->front().getTerminator()->getSuccessor(0),+                        abortBlock);++  // keep track of the index in the presistence table of the operation we+  // are currently processing+  int i = 0;+  // keep track of the current resume index for comparison blocks+  int waitInd = 0;++  // insert persistence keeping for each operation escaping its parent block+  converted.walk([&](Operation *op) -> WalkResult {+    if (op->isUsedOutsideOfBlock(op->getBlock()) &&+        op->getResult(0) != larg.getResult() &&+        !(op->getResult(0).getType().isa<TimeType>())) {+      auto elemTy = stateTy.getStructElementType(3).getStructElementType(i);++      // store the value escaping it's definingn block in the persistence table+      rewriter.setInsertionPointAfter(op);+      auto zeroC0 = rewriter.create<LLVM::ConstantOp>(+          loc, i32Ty, rewriter.getI32IntegerAttr(0));+      auto threeC0 = rewriter.create<LLVM::ConstantOp>(+          loc, i32Ty, rewriter.getI32IntegerAttr(3));+      auto indC0 = rewriter.create<LLVM::ConstantOp>(+          loc, i32Ty, rewriter.getI32IntegerAttr(i));+      auto gep0 = rewriter.create<LLVM::GEPOp>(+          loc, elemTy.getPointerTo(), converted.getArgument(1),+          ArrayRef<Value>({zeroC0, threeC0, indC0}));+      rewriter.create<LLVM::StoreOp>(loc, op->getResult(0), gep0);++      // load the value from the persistence table and substitute the original+      // use with it+      for (auto &use : op->getUses()) {+        if (dom.properlyDominates(op->getBlock(), use.getOwner()->getBlock())) {+          auto user = use.getOwner();+          rewriter.setInsertionPointToStart(user->getBlock());+          auto zeroC1 = rewriter.create<LLVM::ConstantOp>(+              loc, i32Ty, rewriter.getI32IntegerAttr(0));+          auto threeC1 = rewriter.create<LLVM::ConstantOp>(+              loc, i32Ty, rewriter.getI32IntegerAttr(3));+          auto indC1 = rewriter.create<LLVM::ConstantOp>(+              loc, i32Ty, rewriter.getI32IntegerAttr(i));+          auto gep1 = rewriter.create<LLVM::GEPOp>(+              loc, elemTy.getPointerTo(), converted.getArgument(1),+              ArrayRef<Value>({zeroC1, threeC1, indC1}));+          auto load1 = rewriter.create<LLVM::LoadOp>(loc, elemTy, gep1);++          use.set(load1);+        }+      }+      i++;+    }++    // insert a comparison block for wait operations+    if (auto wait = dyn_cast<WaitOp>(op)) {+      insertComparisonBlock(rewriter, dialect, loc, body, larg, ++waitInd,+                            wait.dest());+    }+    return WalkResult::advance();+  });+}++//===----------------------------------------------------------------------===//+// Unit conversions+//===----------------------------------------------------------------------===//++namespace {+/// Convert an `llhd.entity` unit to LLVM. The result is an `llvm.func` which+/// takes a pointer to the state as arguments.+struct EntityOpConversion : public ConvertToLLVMPattern {+  explicit EntityOpConversion(MLIRContext *ctx,+                              LLVMTypeConverter &typeConverter)+      : ConvertToLLVMPattern(llhd::EntityOp::getOperationName(), ctx,+                             typeConverter) {}++  LogicalResult+  matchAndRewrite(Operation *op, ArrayRef<Value> operands,+                  ConversionPatternRewriter &rewriter) const override {+    // reset signal counter+    signalCounter = 0;+    // get adapted operands+    EntityOpAdaptor transformed(operands);+    // get entity operation+    auto entityOp = cast<EntityOp>(op);++    // collect llvm types+    auto voidTy = getVoidType();+    auto i8PtrTy = getVoidPtrType();+    auto i32Ty = LLVM::LLVMType::getInt32Ty(&getDialect());++    // have an intermediate signature conversion to add the arguments for the+    // state, signal table and argument table poitner arguments+    LLVMTypeConverter::SignatureConversion intermediate(+        entityOp.getNumArguments());+    // add state, signal table and arguments table arguments+    intermediate.addInputs(+        ArrayRef<Type>({i8PtrTy, i32Ty.getPointerTo(), i32Ty.getPointerTo()}));+    for (size_t i = 0, e = entityOp.getNumArguments(); i < e; ++i)+      intermediate.addInputs(i, voidTy);+    rewriter.applySignatureConversion(&entityOp.getBody(), intermediate);++    OpBuilder bodyBuilder =+        OpBuilder::atBlockBegin(&entityOp.getBlocks().front());+    LLVMTypeConverter::SignatureConversion final(+        intermediate.getConvertedTypes().size());+    final.addInputs(0, i8PtrTy);+    final.addInputs(1, i32Ty.getPointerTo());+    final.addInputs(2, i32Ty.getPointerTo());++    for (size_t i = 0, e = entityOp.getNumArguments(); i < e; ++i) {+      // create gep and load operations from arguments table for each original+      // argument+      auto index = bodyBuilder.create<LLVM::ConstantOp>(+          rewriter.getUnknownLoc(), i32Ty, rewriter.getI32IntegerAttr(i));+      auto bitcast = bodyBuilder.create<LLVM::GEPOp>(+          rewriter.getUnknownLoc(), i32Ty.getPointerTo(),+          entityOp.getArgument(2), ArrayRef<Value>(index));+      auto load =+          bodyBuilder.create<LLVM::LoadOp>(rewriter.getUnknownLoc(), bitcast);+      // remap i-th original argument to the loaded value+      final.remapInput(i + 3, load.getResult());+    }++    rewriter.applySignatureConversion(&entityOp.getBody(), final);++    // converted entity signature+    auto funcTy = LLVM::LLVMType::getFunctionTy(+        voidTy, {i8PtrTy, i32Ty.getPointerTo(), i32Ty.getPointerTo()}, false);

nit: Please add a parameter comment to the false. i.e. /*paramName=*/false

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Conversion/LLHDToLLVM/LLHDToLLVM.h"+#include "circt/Dialect/LLHD/IR/LLHDDialect.h"+#include "circt/Dialect/LLHD/IR/LLHDOps.h"++#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h"+#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h"+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Dominance.h"+#include "mlir/Pass/Pass.h"+#include "mlir/Transforms/DialectConversion.h"++namespace mlir {+namespace llhd {+#define GEN_PASS_CLASSES+#include "circt/Conversion/LLHDToLLVM/Passes.h.inc"+} // namespace llhd+} // namespace mlir++using namespace mlir;+using namespace mlir::llhd;++// keep a counter to infer a signal's index in his entity's signal table+static int signalCounter = 0;+// keep a counter to infer the resume index after a wait instruction in a+// process+static int resumeCounter = 0;+//===----------------------------------------------------------------------===//+// Helpers+//===----------------------------------------------------------------------===//++/// Get an existing global string+static Value getGlobalString(Location loc, OpBuilder &builder,+                             LLVMTypeConverter &typeConverter,+                             LLVM::GlobalOp &str) {+  auto i8PtrTy = LLVM::LLVMType::getInt8PtrTy(typeConverter.getDialect());+  auto i32Ty = LLVM::LLVMType::getInt32Ty(typeConverter.getDialect());++  auto addr = builder.create<LLVM::AddressOfOp>(+      loc, str.getType().getPointerTo(), str.getName());+  auto idx = builder.create<LLVM::ConstantOp>(loc, i32Ty,+                                              builder.getI32IntegerAttr(0));+  llvm::SmallVector<Value, 2> idxs({idx, idx});+  auto gep = builder.create<LLVM::GEPOp>(loc, i8PtrTy, addr, idxs);+  return gep;+}++/// Looks up a symbol and inserts a new functino at the beginning of the+/// module's region in case the function does not exists. If+/// insertBodyAndTerminator is set, also adds the entry block and return+/// terminator+static LLVM::LLVMFuncOp+getOrInsertFunction(ModuleOp &module, ConversionPatternRewriter &rewriter,+                    std::string name, LLVM::LLVMType signature,+                    bool insertBodyAndTerminator = false) {+  auto func = module.lookupSymbol<LLVM::LLVMFuncOp>(name);+  if (!func) {+    OpBuilder moduleBuilder(module.getBodyRegion());+    func = moduleBuilder.create<LLVM::LLVMFuncOp>(rewriter.getUnknownLoc(),+                                                  name, signature);+    if (insertBodyAndTerminator) {+      func.addEntryBlock();+      OpBuilder b(func.getBody());+      b.create<LLVM::ReturnOp>(rewriter.getUnknownLoc(), ValueRange());+    }+  }+  return func;+}++/// Insert probe runtime call and extraction of details from the struct. The+/// mlir::Values of the details are returned, in struct-order.+static std::pair<Value, Value>+insertProbeSignal(ModuleOp &module, ConversionPatternRewriter &rewriter,+                  LLVM::LLVMDialect *dialect, Operation *op, Value statePtr,+                  Value signal) {+  auto i8PtrTy = LLVM::LLVMType::getInt8PtrTy(dialect);+  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);+  auto i64Ty = LLVM::LLVMType::getInt64Ty(dialect);+  auto sigTy = LLVM::LLVMType::getStructTy(dialect, {i8PtrTy, i64Ty});++  auto prbSignature = LLVM::LLVMType::getFunctionTy(sigTy.getPointerTo(),+                                                    {i8PtrTy, i32Ty}, false);+  auto prbFunc =+      getOrInsertFunction(module, rewriter, "probe_signal", prbSignature);+  SmallVector<Value, 2> prbArgs({statePtr, signal});+  auto prbCall =+      rewriter+          .create<LLVM::CallOp>(op->getLoc(), sigTy.getPointerTo(),+                                rewriter.getSymbolRefAttr(prbFunc), prbArgs)+          .getResult(0);+  auto zeroC = rewriter.create<LLVM::ConstantOp>(op->getLoc(), i32Ty,+                                                 rewriter.getI32IntegerAttr(0));+  auto oneC = rewriter.create<LLVM::ConstantOp>(op->getLoc(), i32Ty,+                                                rewriter.getI32IntegerAttr(1));++  auto sigPtrPtr =+      rewriter.create<LLVM::GEPOp>(op->getLoc(), i8PtrTy.getPointerTo(),+                                   prbCall, ArrayRef<Value>({zeroC, zeroC}));++  auto offsetPtr =+      rewriter.create<LLVM::GEPOp>(op->getLoc(), i64Ty.getPointerTo(), prbCall,+                                   ArrayRef<Value>({zeroC, oneC}));+  auto sigPtr = rewriter.create<LLVM::LoadOp>(op->getLoc(), i8PtrTy, sigPtrPtr);+  auto offset = rewriter.create<LLVM::LoadOp>(op->getLoc(), i64Ty, offsetPtr);++  return std::pair<Value, Value>(sigPtr, offset);+}++/// Gather the types of values that are used outside of the block they're+/// defined in. An LLVMType structure containing those types, in order of+/// appearance, is returned.+static LLVM::LLVMType getProcPersistenceTy(LLVM::LLVMDialect *dialect,+                                           LLVMTypeConverter &converter,+                                           ProcOp &proc) {+  SmallVector<LLVM::LLVMType, 3> types = SmallVector<LLVM::LLVMType, 3>();++  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);++  proc.walk([&](Operation *op) -> WalkResult {+    if (op->isUsedOutsideOfBlock(op->getBlock())) {+      if (op->getResult(0).getType().isa<IntegerType>())+        types.push_back(converter.convertType(op->getResult(0).getType())+                            .cast<LLVM::LLVMType>());+      else if (op->getResult(0).getType().isa<SigType>())+        types.push_back(i32Ty);+    }+    return WalkResult::advance();+  });+  return LLVM::LLVMType::getStructTy(dialect, types);+}++/// Insert a comparison block that either jumps to the trueDest block, if the+/// resume index mathces the current index, or to falseDest otherwise. If no+/// falseDest is provided, the next block is taken insead.+static void insertComparisonBlock(ConversionPatternRewriter &rewriter,+                                  LLVM::LLVMDialect *dialect, Location loc,+                                  Region *body, Value resumeIdx, int currIdx,+                                  Block *trueDest, Block *falseDest = nullptr) {+  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);+  // redirect entry block to a first comparison block. If it is a fresh start,+  // start from where original entry would have jumped, else the process is in+  // an illegal state and jump to the abort block+  auto secondBlock = ++body->begin();+  auto newBlock = rewriter.createBlock(body, secondBlock);+  auto cmpIdx = rewriter.create<LLVM::ConstantOp>(+      loc, i32Ty, rewriter.getI32IntegerAttr(currIdx));+  auto cmpRes = rewriter.create<LLVM::ICmpOp>(loc, LLVM::ICmpPredicate::eq,+                                              resumeIdx, cmpIdx);++  // default to jump to the next block for false+  if (!falseDest)+    falseDest = &*secondBlock;++  rewriter.create<LLVM::CondBrOp>(loc, cmpRes, trueDest, falseDest);++  // redirect entry block terminator to the new comparison block+  auto entryTer = body->front().getTerminator();+  entryTer->setSuccessor(newBlock, 0);+}++/// Insert the blocks and operations needed to persist values across suspension,+/// as well as ones needed to resume execution at the right spot.+static void insertPersistence(LLVMTypeConverter &converter,+                              ConversionPatternRewriter &rewriter,+                              LLVM::LLVMDialect *dialect, Location loc,+                              ProcOp &proc, LLVM::LLVMType &stateTy,+                              LLVM::LLVMFuncOp &converted) {+  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);+  DominanceInfo dom(converted);++  // load resume index+  rewriter.setInsertionPoint(converted.getBody().front().getTerminator());+  auto zeroC = rewriter.create<LLVM::ConstantOp>(loc, i32Ty,+                                                 rewriter.getI32IntegerAttr(0));+  auto oneC = rewriter.create<LLVM::ConstantOp>(loc, i32Ty,+                                                rewriter.getI32IntegerAttr(1));+  auto gep = rewriter.create<LLVM::GEPOp>(loc, i32Ty.getPointerTo(),+                                          converted.getArgument(1),+                                          ArrayRef<Value>({zeroC, oneC}));++  auto larg = rewriter.create<LLVM::LoadOp>(loc, i32Ty, gep);++  // insert an abort block as the last block+  auto abortBlock =+      rewriter.createBlock(&converted.getBody(), converted.getBody().end());+  rewriter.create<LLVM::ReturnOp>(loc, ValueRange());++  auto body = &converted.getBody();+  assert(body->front().getTerminator()->getNumSuccessors() > 0 &&+         "the process entry block is expected to branch to another block");+  // redirect entry block to a first comparison block. If on a fresh start,+  // start from where original entry would have jumped, else the process is in+  // an illegal state and jump to the abort block+  insertComparisonBlock(rewriter, dialect, loc, body, larg, 0,+                        body->front().getTerminator()->getSuccessor(0),+                        abortBlock);++  // keep track of the index in the presistence table of the operation we+  // are currently processing+  int i = 0;+  // keep track of the current resume index for comparison blocks+  int waitInd = 0;++  // insert persistence keeping for each operation escaping its parent block+  converted.walk([&](Operation *op) -> WalkResult {+    if (op->isUsedOutsideOfBlock(op->getBlock()) &&+        op->getResult(0) != larg.getResult() &&+        !(op->getResult(0).getType().isa<TimeType>())) {+      auto elemTy = stateTy.getStructElementType(3).getStructElementType(i);++      // store the value escaping it's definingn block in the persistence table+      rewriter.setInsertionPointAfter(op);+      auto zeroC0 = rewriter.create<LLVM::ConstantOp>(+          loc, i32Ty, rewriter.getI32IntegerAttr(0));+      auto threeC0 = rewriter.create<LLVM::ConstantOp>(+          loc, i32Ty, rewriter.getI32IntegerAttr(3));+      auto indC0 = rewriter.create<LLVM::ConstantOp>(+          loc, i32Ty, rewriter.getI32IntegerAttr(i));+      auto gep0 = rewriter.create<LLVM::GEPOp>(+          loc, elemTy.getPointerTo(), converted.getArgument(1),+          ArrayRef<Value>({zeroC0, threeC0, indC0}));+      rewriter.create<LLVM::StoreOp>(loc, op->getResult(0), gep0);++      // load the value from the persistence table and substitute the original+      // use with it+      for (auto &use : op->getUses()) {+        if (dom.properlyDominates(op->getBlock(), use.getOwner()->getBlock())) {+          auto user = use.getOwner();+          rewriter.setInsertionPointToStart(user->getBlock());+          auto zeroC1 = rewriter.create<LLVM::ConstantOp>(+              loc, i32Ty, rewriter.getI32IntegerAttr(0));+          auto threeC1 = rewriter.create<LLVM::ConstantOp>(+              loc, i32Ty, rewriter.getI32IntegerAttr(3));+          auto indC1 = rewriter.create<LLVM::ConstantOp>(+              loc, i32Ty, rewriter.getI32IntegerAttr(i));+          auto gep1 = rewriter.create<LLVM::GEPOp>(+              loc, elemTy.getPointerTo(), converted.getArgument(1),+              ArrayRef<Value>({zeroC1, threeC1, indC1}));+          auto load1 = rewriter.create<LLVM::LoadOp>(loc, elemTy, gep1);++          use.set(load1);+        }+      }+      i++;+    }++    // insert a comparison block for wait operations+    if (auto wait = dyn_cast<WaitOp>(op)) {+      insertComparisonBlock(rewriter, dialect, loc, body, larg, ++waitInd,+                            wait.dest());+    }+    return WalkResult::advance();

Same comment as the previous walk.

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Conversion/LLHDToLLVM/LLHDToLLVM.h"+#include "circt/Dialect/LLHD/IR/LLHDDialect.h"+#include "circt/Dialect/LLHD/IR/LLHDOps.h"++#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h"+#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h"+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Dominance.h"+#include "mlir/Pass/Pass.h"+#include "mlir/Transforms/DialectConversion.h"++namespace mlir {+namespace llhd {+#define GEN_PASS_CLASSES+#include "circt/Conversion/LLHDToLLVM/Passes.h.inc"+} // namespace llhd+} // namespace mlir++using namespace mlir;+using namespace mlir::llhd;++// keep a counter to infer a signal's index in his entity's signal table+static int signalCounter = 0;+// keep a counter to infer the resume index after a wait instruction in a+// process+static int resumeCounter = 0;+//===----------------------------------------------------------------------===//+// Helpers+//===----------------------------------------------------------------------===//++/// Get an existing global string+static Value getGlobalString(Location loc, OpBuilder &builder,+                             LLVMTypeConverter &typeConverter,+                             LLVM::GlobalOp &str) {+  auto i8PtrTy = LLVM::LLVMType::getInt8PtrTy(typeConverter.getDialect());+  auto i32Ty = LLVM::LLVMType::getInt32Ty(typeConverter.getDialect());++  auto addr = builder.create<LLVM::AddressOfOp>(+      loc, str.getType().getPointerTo(), str.getName());+  auto idx = builder.create<LLVM::ConstantOp>(loc, i32Ty,+                                              builder.getI32IntegerAttr(0));+  llvm::SmallVector<Value, 2> idxs({idx, idx});+  auto gep = builder.create<LLVM::GEPOp>(loc, i8PtrTy, addr, idxs);+  return gep;+}++/// Looks up a symbol and inserts a new functino at the beginning of the+/// module's region in case the function does not exists. If+/// insertBodyAndTerminator is set, also adds the entry block and return+/// terminator+static LLVM::LLVMFuncOp+getOrInsertFunction(ModuleOp &module, ConversionPatternRewriter &rewriter,+                    std::string name, LLVM::LLVMType signature,+                    bool insertBodyAndTerminator = false) {+  auto func = module.lookupSymbol<LLVM::LLVMFuncOp>(name);+  if (!func) {+    OpBuilder moduleBuilder(module.getBodyRegion());+    func = moduleBuilder.create<LLVM::LLVMFuncOp>(rewriter.getUnknownLoc(),+                                                  name, signature);+    if (insertBodyAndTerminator) {+      func.addEntryBlock();+      OpBuilder b(func.getBody());+      b.create<LLVM::ReturnOp>(rewriter.getUnknownLoc(), ValueRange());+    }+  }+  return func;+}++/// Insert probe runtime call and extraction of details from the struct. The+/// mlir::Values of the details are returned, in struct-order.+static std::pair<Value, Value>+insertProbeSignal(ModuleOp &module, ConversionPatternRewriter &rewriter,+                  LLVM::LLVMDialect *dialect, Operation *op, Value statePtr,+                  Value signal) {+  auto i8PtrTy = LLVM::LLVMType::getInt8PtrTy(dialect);+  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);+  auto i64Ty = LLVM::LLVMType::getInt64Ty(dialect);+  auto sigTy = LLVM::LLVMType::getStructTy(dialect, {i8PtrTy, i64Ty});++  auto prbSignature = LLVM::LLVMType::getFunctionTy(sigTy.getPointerTo(),+                                                    {i8PtrTy, i32Ty}, false);+  auto prbFunc =+      getOrInsertFunction(module, rewriter, "probe_signal", prbSignature);+  SmallVector<Value, 2> prbArgs({statePtr, signal});+  auto prbCall =+      rewriter+          .create<LLVM::CallOp>(op->getLoc(), sigTy.getPointerTo(),+                                rewriter.getSymbolRefAttr(prbFunc), prbArgs)+          .getResult(0);+  auto zeroC = rewriter.create<LLVM::ConstantOp>(op->getLoc(), i32Ty,+                                                 rewriter.getI32IntegerAttr(0));+  auto oneC = rewriter.create<LLVM::ConstantOp>(op->getLoc(), i32Ty,+                                                rewriter.getI32IntegerAttr(1));++  auto sigPtrPtr =+      rewriter.create<LLVM::GEPOp>(op->getLoc(), i8PtrTy.getPointerTo(),+                                   prbCall, ArrayRef<Value>({zeroC, zeroC}));++  auto offsetPtr =+      rewriter.create<LLVM::GEPOp>(op->getLoc(), i64Ty.getPointerTo(), prbCall,+                                   ArrayRef<Value>({zeroC, oneC}));+  auto sigPtr = rewriter.create<LLVM::LoadOp>(op->getLoc(), i8PtrTy, sigPtrPtr);+  auto offset = rewriter.create<LLVM::LoadOp>(op->getLoc(), i64Ty, offsetPtr);++  return std::pair<Value, Value>(sigPtr, offset);+}++/// Gather the types of values that are used outside of the block they're+/// defined in. An LLVMType structure containing those types, in order of+/// appearance, is returned.+static LLVM::LLVMType getProcPersistenceTy(LLVM::LLVMDialect *dialect,+                                           LLVMTypeConverter &converter,+                                           ProcOp &proc) {+  SmallVector<LLVM::LLVMType, 3> types = SmallVector<LLVM::LLVMType, 3>();++  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);++  proc.walk([&](Operation *op) -> WalkResult {+    if (op->isUsedOutsideOfBlock(op->getBlock())) {+      if (op->getResult(0).getType().isa<IntegerType>())+        types.push_back(converter.convertType(op->getResult(0).getType())+                            .cast<LLVM::LLVMType>());+      else if (op->getResult(0).getType().isa<SigType>())+        types.push_back(i32Ty);+    }+    return WalkResult::advance();+  });+  return LLVM::LLVMType::getStructTy(dialect, types);+}++/// Insert a comparison block that either jumps to the trueDest block, if the+/// resume index mathces the current index, or to falseDest otherwise. If no+/// falseDest is provided, the next block is taken insead.+static void insertComparisonBlock(ConversionPatternRewriter &rewriter,+                                  LLVM::LLVMDialect *dialect, Location loc,+                                  Region *body, Value resumeIdx, int currIdx,+                                  Block *trueDest, Block *falseDest = nullptr) {+  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);+  // redirect entry block to a first comparison block. If it is a fresh start,+  // start from where original entry would have jumped, else the process is in+  // an illegal state and jump to the abort block+  auto secondBlock = ++body->begin();+  auto newBlock = rewriter.createBlock(body, secondBlock);+  auto cmpIdx = rewriter.create<LLVM::ConstantOp>(+      loc, i32Ty, rewriter.getI32IntegerAttr(currIdx));+  auto cmpRes = rewriter.create<LLVM::ICmpOp>(loc, LLVM::ICmpPredicate::eq,+                                              resumeIdx, cmpIdx);++  // default to jump to the next block for false+  if (!falseDest)+    falseDest = &*secondBlock;++  rewriter.create<LLVM::CondBrOp>(loc, cmpRes, trueDest, falseDest);++  // redirect entry block terminator to the new comparison block+  auto entryTer = body->front().getTerminator();+  entryTer->setSuccessor(newBlock, 0);+}++/// Insert the blocks and operations needed to persist values across suspension,+/// as well as ones needed to resume execution at the right spot.+static void insertPersistence(LLVMTypeConverter &converter,+                              ConversionPatternRewriter &rewriter,+                              LLVM::LLVMDialect *dialect, Location loc,+                              ProcOp &proc, LLVM::LLVMType &stateTy,+                              LLVM::LLVMFuncOp &converted) {+  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);+  DominanceInfo dom(converted);++  // load resume index+  rewriter.setInsertionPoint(converted.getBody().front().getTerminator());+  auto zeroC = rewriter.create<LLVM::ConstantOp>(loc, i32Ty,+                                                 rewriter.getI32IntegerAttr(0));+  auto oneC = rewriter.create<LLVM::ConstantOp>(loc, i32Ty,+                                                rewriter.getI32IntegerAttr(1));+  auto gep = rewriter.create<LLVM::GEPOp>(loc, i32Ty.getPointerTo(),+                                          converted.getArgument(1),+                                          ArrayRef<Value>({zeroC, oneC}));++  auto larg = rewriter.create<LLVM::LoadOp>(loc, i32Ty, gep);++  // insert an abort block as the last block+  auto abortBlock =+      rewriter.createBlock(&converted.getBody(), converted.getBody().end());+  rewriter.create<LLVM::ReturnOp>(loc, ValueRange());++  auto body = &converted.getBody();+  assert(body->front().getTerminator()->getNumSuccessors() > 0 &&+         "the process entry block is expected to branch to another block");+  // redirect entry block to a first comparison block. If on a fresh start,+  // start from where original entry would have jumped, else the process is in+  // an illegal state and jump to the abort block+  insertComparisonBlock(rewriter, dialect, loc, body, larg, 0,+                        body->front().getTerminator()->getSuccessor(0),+                        abortBlock);++  // keep track of the index in the presistence table of the operation we+  // are currently processing+  int i = 0;+  // keep track of the current resume index for comparison blocks+  int waitInd = 0;++  // insert persistence keeping for each operation escaping its parent block+  converted.walk([&](Operation *op) -> WalkResult {+    if (op->isUsedOutsideOfBlock(op->getBlock()) &&+        op->getResult(0) != larg.getResult() &&+        !(op->getResult(0).getType().isa<TimeType>())) {+      auto elemTy = stateTy.getStructElementType(3).getStructElementType(i);++      // store the value escaping it's definingn block in the persistence table+      rewriter.setInsertionPointAfter(op);+      auto zeroC0 = rewriter.create<LLVM::ConstantOp>(+          loc, i32Ty, rewriter.getI32IntegerAttr(0));+      auto threeC0 = rewriter.create<LLVM::ConstantOp>(+          loc, i32Ty, rewriter.getI32IntegerAttr(3));+      auto indC0 = rewriter.create<LLVM::ConstantOp>(+          loc, i32Ty, rewriter.getI32IntegerAttr(i));+      auto gep0 = rewriter.create<LLVM::GEPOp>(+          loc, elemTy.getPointerTo(), converted.getArgument(1),+          ArrayRef<Value>({zeroC0, threeC0, indC0}));+      rewriter.create<LLVM::StoreOp>(loc, op->getResult(0), gep0);++      // load the value from the persistence table and substitute the original+      // use with it+      for (auto &use : op->getUses()) {+        if (dom.properlyDominates(op->getBlock(), use.getOwner()->getBlock())) {+          auto user = use.getOwner();+          rewriter.setInsertionPointToStart(user->getBlock());+          auto zeroC1 = rewriter.create<LLVM::ConstantOp>(+              loc, i32Ty, rewriter.getI32IntegerAttr(0));+          auto threeC1 = rewriter.create<LLVM::ConstantOp>(+              loc, i32Ty, rewriter.getI32IntegerAttr(3));+          auto indC1 = rewriter.create<LLVM::ConstantOp>(+              loc, i32Ty, rewriter.getI32IntegerAttr(i));+          auto gep1 = rewriter.create<LLVM::GEPOp>(+              loc, elemTy.getPointerTo(), converted.getArgument(1),+              ArrayRef<Value>({zeroC1, threeC1, indC1}));+          auto load1 = rewriter.create<LLVM::LoadOp>(loc, elemTy, gep1);++          use.set(load1);+        }+      }+      i++;+    }++    // insert a comparison block for wait operations+    if (auto wait = dyn_cast<WaitOp>(op)) {+      insertComparisonBlock(rewriter, dialect, loc, body, larg, ++waitInd,+                            wait.dest());+    }+    return WalkResult::advance();+  });+}++//===----------------------------------------------------------------------===//+// Unit conversions+//===----------------------------------------------------------------------===//++namespace {+/// Convert an `llhd.entity` unit to LLVM. The result is an `llvm.func` which+/// takes a pointer to the state as arguments.+struct EntityOpConversion : public ConvertToLLVMPattern {+  explicit EntityOpConversion(MLIRContext *ctx,+                              LLVMTypeConverter &typeConverter)+      : ConvertToLLVMPattern(llhd::EntityOp::getOperationName(), ctx,+                             typeConverter) {}++  LogicalResult+  matchAndRewrite(Operation *op, ArrayRef<Value> operands,+                  ConversionPatternRewriter &rewriter) const override {+    // reset signal counter+    signalCounter = 0;+    // get adapted operands+    EntityOpAdaptor transformed(operands);+    // get entity operation+    auto entityOp = cast<EntityOp>(op);++    // collect llvm types+    auto voidTy = getVoidType();+    auto i8PtrTy = getVoidPtrType();+    auto i32Ty = LLVM::LLVMType::getInt32Ty(&getDialect());++    // have an intermediate signature conversion to add the arguments for the+    // state, signal table and argument table poitner arguments+    LLVMTypeConverter::SignatureConversion intermediate(+        entityOp.getNumArguments());+    // add state, signal table and arguments table arguments+    intermediate.addInputs(+        ArrayRef<Type>({i8PtrTy, i32Ty.getPointerTo(), i32Ty.getPointerTo()}));+    for (size_t i = 0, e = entityOp.getNumArguments(); i < e; ++i)+      intermediate.addInputs(i, voidTy);+    rewriter.applySignatureConversion(&entityOp.getBody(), intermediate);++    OpBuilder bodyBuilder =+        OpBuilder::atBlockBegin(&entityOp.getBlocks().front());+    LLVMTypeConverter::SignatureConversion final(+        intermediate.getConvertedTypes().size());+    final.addInputs(0, i8PtrTy);+    final.addInputs(1, i32Ty.getPointerTo());+    final.addInputs(2, i32Ty.getPointerTo());++    for (size_t i = 0, e = entityOp.getNumArguments(); i < e; ++i) {+      // create gep and load operations from arguments table for each original+      // argument+      auto index = bodyBuilder.create<LLVM::ConstantOp>(+          rewriter.getUnknownLoc(), i32Ty, rewriter.getI32IntegerAttr(i));+      auto bitcast = bodyBuilder.create<LLVM::GEPOp>(+          rewriter.getUnknownLoc(), i32Ty.getPointerTo(),+          entityOp.getArgument(2), ArrayRef<Value>(index));+      auto load =+          bodyBuilder.create<LLVM::LoadOp>(rewriter.getUnknownLoc(), bitcast);+      // remap i-th original argument to the loaded value+      final.remapInput(i + 3, load.getResult());+    }++    rewriter.applySignatureConversion(&entityOp.getBody(), final);++    // converted entity signature+    auto funcTy = LLVM::LLVMType::getFunctionTy(+        voidTy, {i8PtrTy, i32Ty.getPointerTo(), i32Ty.getPointerTo()}, false);+    // // create the llvm function

nit: Can you fix this comment? Also, please use complete sentences and punctuation in comments.

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Conversion/LLHDToLLVM/LLHDToLLVM.h"+#include "circt/Dialect/LLHD/IR/LLHDDialect.h"+#include "circt/Dialect/LLHD/IR/LLHDOps.h"++#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h"+#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h"+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Dominance.h"+#include "mlir/Pass/Pass.h"+#include "mlir/Transforms/DialectConversion.h"++namespace mlir {+namespace llhd {+#define GEN_PASS_CLASSES+#include "circt/Conversion/LLHDToLLVM/Passes.h.inc"+} // namespace llhd+} // namespace mlir++using namespace mlir;+using namespace mlir::llhd;++// keep a counter to infer a signal's index in his entity's signal table+static int signalCounter = 0;+// keep a counter to infer the resume index after a wait instruction in a+// process+static int resumeCounter = 0;+//===----------------------------------------------------------------------===//+// Helpers+//===----------------------------------------------------------------------===//++/// Get an existing global string+static Value getGlobalString(Location loc, OpBuilder &builder,+                             LLVMTypeConverter &typeConverter,+                             LLVM::GlobalOp &str) {+  auto i8PtrTy = LLVM::LLVMType::getInt8PtrTy(typeConverter.getDialect());+  auto i32Ty = LLVM::LLVMType::getInt32Ty(typeConverter.getDialect());++  auto addr = builder.create<LLVM::AddressOfOp>(+      loc, str.getType().getPointerTo(), str.getName());+  auto idx = builder.create<LLVM::ConstantOp>(loc, i32Ty,+                                              builder.getI32IntegerAttr(0));+  llvm::SmallVector<Value, 2> idxs({idx, idx});+  auto gep = builder.create<LLVM::GEPOp>(loc, i8PtrTy, addr, idxs);+  return gep;+}++/// Looks up a symbol and inserts a new functino at the beginning of the+/// module's region in case the function does not exists. If+/// insertBodyAndTerminator is set, also adds the entry block and return+/// terminator+static LLVM::LLVMFuncOp+getOrInsertFunction(ModuleOp &module, ConversionPatternRewriter &rewriter,+                    std::string name, LLVM::LLVMType signature,+                    bool insertBodyAndTerminator = false) {+  auto func = module.lookupSymbol<LLVM::LLVMFuncOp>(name);+  if (!func) {+    OpBuilder moduleBuilder(module.getBodyRegion());+    func = moduleBuilder.create<LLVM::LLVMFuncOp>(rewriter.getUnknownLoc(),+                                                  name, signature);+    if (insertBodyAndTerminator) {+      func.addEntryBlock();+      OpBuilder b(func.getBody());+      b.create<LLVM::ReturnOp>(rewriter.getUnknownLoc(), ValueRange());+    }+  }+  return func;+}++/// Insert probe runtime call and extraction of details from the struct. The+/// mlir::Values of the details are returned, in struct-order.+static std::pair<Value, Value>+insertProbeSignal(ModuleOp &module, ConversionPatternRewriter &rewriter,+                  LLVM::LLVMDialect *dialect, Operation *op, Value statePtr,+                  Value signal) {+  auto i8PtrTy = LLVM::LLVMType::getInt8PtrTy(dialect);+  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);+  auto i64Ty = LLVM::LLVMType::getInt64Ty(dialect);+  auto sigTy = LLVM::LLVMType::getStructTy(dialect, {i8PtrTy, i64Ty});++  auto prbSignature = LLVM::LLVMType::getFunctionTy(sigTy.getPointerTo(),+                                                    {i8PtrTy, i32Ty}, false);+  auto prbFunc =+      getOrInsertFunction(module, rewriter, "probe_signal", prbSignature);+  SmallVector<Value, 2> prbArgs({statePtr, signal});+  auto prbCall =+      rewriter+          .create<LLVM::CallOp>(op->getLoc(), sigTy.getPointerTo(),+                                rewriter.getSymbolRefAttr(prbFunc), prbArgs)+          .getResult(0);+  auto zeroC = rewriter.create<LLVM::ConstantOp>(op->getLoc(), i32Ty,+                                                 rewriter.getI32IntegerAttr(0));+  auto oneC = rewriter.create<LLVM::ConstantOp>(op->getLoc(), i32Ty,+                                                rewriter.getI32IntegerAttr(1));++  auto sigPtrPtr =+      rewriter.create<LLVM::GEPOp>(op->getLoc(), i8PtrTy.getPointerTo(),+                                   prbCall, ArrayRef<Value>({zeroC, zeroC}));++  auto offsetPtr =+      rewriter.create<LLVM::GEPOp>(op->getLoc(), i64Ty.getPointerTo(), prbCall,+                                   ArrayRef<Value>({zeroC, oneC}));+  auto sigPtr = rewriter.create<LLVM::LoadOp>(op->getLoc(), i8PtrTy, sigPtrPtr);+  auto offset = rewriter.create<LLVM::LoadOp>(op->getLoc(), i64Ty, offsetPtr);++  return std::pair<Value, Value>(sigPtr, offset);+}++/// Gather the types of values that are used outside of the block they're+/// defined in. An LLVMType structure containing those types, in order of+/// appearance, is returned.+static LLVM::LLVMType getProcPersistenceTy(LLVM::LLVMDialect *dialect,+                                           LLVMTypeConverter &converter,+                                           ProcOp &proc) {+  SmallVector<LLVM::LLVMType, 3> types = SmallVector<LLVM::LLVMType, 3>();++  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);++  proc.walk([&](Operation *op) -> WalkResult {+    if (op->isUsedOutsideOfBlock(op->getBlock())) {+      if (op->getResult(0).getType().isa<IntegerType>())+        types.push_back(converter.convertType(op->getResult(0).getType())+                            .cast<LLVM::LLVMType>());+      else if (op->getResult(0).getType().isa<SigType>())+        types.push_back(i32Ty);+    }+    return WalkResult::advance();+  });+  return LLVM::LLVMType::getStructTy(dialect, types);+}++/// Insert a comparison block that either jumps to the trueDest block, if the+/// resume index mathces the current index, or to falseDest otherwise. If no+/// falseDest is provided, the next block is taken insead.+static void insertComparisonBlock(ConversionPatternRewriter &rewriter,+                                  LLVM::LLVMDialect *dialect, Location loc,+                                  Region *body, Value resumeIdx, int currIdx,+                                  Block *trueDest, Block *falseDest = nullptr) {+  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);+  // redirect entry block to a first comparison block. If it is a fresh start,+  // start from where original entry would have jumped, else the process is in+  // an illegal state and jump to the abort block+  auto secondBlock = ++body->begin();+  auto newBlock = rewriter.createBlock(body, secondBlock);+  auto cmpIdx = rewriter.create<LLVM::ConstantOp>(+      loc, i32Ty, rewriter.getI32IntegerAttr(currIdx));+  auto cmpRes = rewriter.create<LLVM::ICmpOp>(loc, LLVM::ICmpPredicate::eq,+                                              resumeIdx, cmpIdx);++  // default to jump to the next block for false+  if (!falseDest)+    falseDest = &*secondBlock;++  rewriter.create<LLVM::CondBrOp>(loc, cmpRes, trueDest, falseDest);++  // redirect entry block terminator to the new comparison block+  auto entryTer = body->front().getTerminator();+  entryTer->setSuccessor(newBlock, 0);+}++/// Insert the blocks and operations needed to persist values across suspension,+/// as well as ones needed to resume execution at the right spot.+static void insertPersistence(LLVMTypeConverter &converter,+                              ConversionPatternRewriter &rewriter,+                              LLVM::LLVMDialect *dialect, Location loc,+                              ProcOp &proc, LLVM::LLVMType &stateTy,+                              LLVM::LLVMFuncOp &converted) {+  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);+  DominanceInfo dom(converted);++  // load resume index+  rewriter.setInsertionPoint(converted.getBody().front().getTerminator());+  auto zeroC = rewriter.create<LLVM::ConstantOp>(loc, i32Ty,+                                                 rewriter.getI32IntegerAttr(0));+  auto oneC = rewriter.create<LLVM::ConstantOp>(loc, i32Ty,+                                                rewriter.getI32IntegerAttr(1));+  auto gep = rewriter.create<LLVM::GEPOp>(loc, i32Ty.getPointerTo(),+                                          converted.getArgument(1),+                                          ArrayRef<Value>({zeroC, oneC}));++  auto larg = rewriter.create<LLVM::LoadOp>(loc, i32Ty, gep);++  // insert an abort block as the last block+  auto abortBlock =+      rewriter.createBlock(&converted.getBody(), converted.getBody().end());+  rewriter.create<LLVM::ReturnOp>(loc, ValueRange());++  auto body = &converted.getBody();+  assert(body->front().getTerminator()->getNumSuccessors() > 0 &&

nit: I don't think the getTerminator()-> part of this is necessary.

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Conversion/LLHDToLLVM/LLHDToLLVM.h"+#include "circt/Dialect/LLHD/IR/LLHDDialect.h"+#include "circt/Dialect/LLHD/IR/LLHDOps.h"++#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h"+#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h"+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Dominance.h"+#include "mlir/Pass/Pass.h"+#include "mlir/Transforms/DialectConversion.h"++namespace mlir {+namespace llhd {+#define GEN_PASS_CLASSES+#include "circt/Conversion/LLHDToLLVM/Passes.h.inc"+} // namespace llhd+} // namespace mlir++using namespace mlir;+using namespace mlir::llhd;++// keep a counter to infer a signal's index in his entity's signal table+static int signalCounter = 0;+// keep a counter to infer the resume index after a wait instruction in a+// process+static int resumeCounter = 0;+//===----------------------------------------------------------------------===//+// Helpers+//===----------------------------------------------------------------------===//++/// Get an existing global string+static Value getGlobalString(Location loc, OpBuilder &builder,+                             LLVMTypeConverter &typeConverter,+                             LLVM::GlobalOp &str) {+  auto i8PtrTy = LLVM::LLVMType::getInt8PtrTy(typeConverter.getDialect());+  auto i32Ty = LLVM::LLVMType::getInt32Ty(typeConverter.getDialect());++  auto addr = builder.create<LLVM::AddressOfOp>(+      loc, str.getType().getPointerTo(), str.getName());+  auto idx = builder.create<LLVM::ConstantOp>(loc, i32Ty,+                                              builder.getI32IntegerAttr(0));+  llvm::SmallVector<Value, 2> idxs({idx, idx});+  auto gep = builder.create<LLVM::GEPOp>(loc, i8PtrTy, addr, idxs);+  return gep;+}++/// Looks up a symbol and inserts a new functino at the beginning of the+/// module's region in case the function does not exists. If+/// insertBodyAndTerminator is set, also adds the entry block and return+/// terminator+static LLVM::LLVMFuncOp+getOrInsertFunction(ModuleOp &module, ConversionPatternRewriter &rewriter,+                    std::string name, LLVM::LLVMType signature,+                    bool insertBodyAndTerminator = false) {+  auto func = module.lookupSymbol<LLVM::LLVMFuncOp>(name);+  if (!func) {+    OpBuilder moduleBuilder(module.getBodyRegion());+    func = moduleBuilder.create<LLVM::LLVMFuncOp>(rewriter.getUnknownLoc(),+                                                  name, signature);+    if (insertBodyAndTerminator) {+      func.addEntryBlock();+      OpBuilder b(func.getBody());+      b.create<LLVM::ReturnOp>(rewriter.getUnknownLoc(), ValueRange());+    }+  }+  return func;+}++/// Insert probe runtime call and extraction of details from the struct. The+/// mlir::Values of the details are returned, in struct-order.+static std::pair<Value, Value>+insertProbeSignal(ModuleOp &module, ConversionPatternRewriter &rewriter,+                  LLVM::LLVMDialect *dialect, Operation *op, Value statePtr,+                  Value signal) {+  auto i8PtrTy = LLVM::LLVMType::getInt8PtrTy(dialect);+  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);+  auto i64Ty = LLVM::LLVMType::getInt64Ty(dialect);+  auto sigTy = LLVM::LLVMType::getStructTy(dialect, {i8PtrTy, i64Ty});++  auto prbSignature = LLVM::LLVMType::getFunctionTy(sigTy.getPointerTo(),+                                                    {i8PtrTy, i32Ty}, false);+  auto prbFunc =+      getOrInsertFunction(module, rewriter, "probe_signal", prbSignature);+  SmallVector<Value, 2> prbArgs({statePtr, signal});+  auto prbCall =+      rewriter+          .create<LLVM::CallOp>(op->getLoc(), sigTy.getPointerTo(),+                                rewriter.getSymbolRefAttr(prbFunc), prbArgs)+          .getResult(0);+  auto zeroC = rewriter.create<LLVM::ConstantOp>(op->getLoc(), i32Ty,+                                                 rewriter.getI32IntegerAttr(0));+  auto oneC = rewriter.create<LLVM::ConstantOp>(op->getLoc(), i32Ty,+                                                rewriter.getI32IntegerAttr(1));++  auto sigPtrPtr =+      rewriter.create<LLVM::GEPOp>(op->getLoc(), i8PtrTy.getPointerTo(),+                                   prbCall, ArrayRef<Value>({zeroC, zeroC}));++  auto offsetPtr =+      rewriter.create<LLVM::GEPOp>(op->getLoc(), i64Ty.getPointerTo(), prbCall,+                                   ArrayRef<Value>({zeroC, oneC}));+  auto sigPtr = rewriter.create<LLVM::LoadOp>(op->getLoc(), i8PtrTy, sigPtrPtr);+  auto offset = rewriter.create<LLVM::LoadOp>(op->getLoc(), i64Ty, offsetPtr);++  return std::pair<Value, Value>(sigPtr, offset);+}++/// Gather the types of values that are used outside of the block they're+/// defined in. An LLVMType structure containing those types, in order of+/// appearance, is returned.+static LLVM::LLVMType getProcPersistenceTy(LLVM::LLVMDialect *dialect,+                                           LLVMTypeConverter &converter,+                                           ProcOp &proc) {+  SmallVector<LLVM::LLVMType, 3> types = SmallVector<LLVM::LLVMType, 3>();++  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);++  proc.walk([&](Operation *op) -> WalkResult {

There are different types of walk methods. If you aren't using interrupt, you don't need WalkResult. You can just change this lambda to return void instead.

maerhart

comment created time in 3 months

Pull request review commentcirct/circt

Merge LLHD project into CIRCT

+#include "circt/Conversion/LLHDToLLVM/LLHDToLLVM.h"+#include "circt/Dialect/LLHD/IR/LLHDDialect.h"+#include "circt/Dialect/LLHD/IR/LLHDOps.h"++#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h"+#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h"+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"+#include "mlir/Dialect/StandardOps/IR/Ops.h"+#include "mlir/IR/BlockAndValueMapping.h"+#include "mlir/IR/Dominance.h"+#include "mlir/Pass/Pass.h"+#include "mlir/Transforms/DialectConversion.h"++namespace mlir {+namespace llhd {+#define GEN_PASS_CLASSES+#include "circt/Conversion/LLHDToLLVM/Passes.h.inc"+} // namespace llhd+} // namespace mlir++using namespace mlir;+using namespace mlir::llhd;++// keep a counter to infer a signal's index in his entity's signal table+static int signalCounter = 0;+// keep a counter to infer the resume index after a wait instruction in a+// process+static int resumeCounter = 0;+//===----------------------------------------------------------------------===//+// Helpers+//===----------------------------------------------------------------------===//++/// Get an existing global string+static Value getGlobalString(Location loc, OpBuilder &builder,+                             LLVMTypeConverter &typeConverter,+                             LLVM::GlobalOp &str) {+  auto i8PtrTy = LLVM::LLVMType::getInt8PtrTy(typeConverter.getDialect());+  auto i32Ty = LLVM::LLVMType::getInt32Ty(typeConverter.getDialect());++  auto addr = builder.create<LLVM::AddressOfOp>(+      loc, str.getType().getPointerTo(), str.getName());+  auto idx = builder.create<LLVM::ConstantOp>(loc, i32Ty,+                                              builder.getI32IntegerAttr(0));+  llvm::SmallVector<Value, 2> idxs({idx, idx});+  auto gep = builder.create<LLVM::GEPOp>(loc, i8PtrTy, addr, idxs);+  return gep;+}++/// Looks up a symbol and inserts a new functino at the beginning of the+/// module's region in case the function does not exists. If+/// insertBodyAndTerminator is set, also adds the entry block and return+/// terminator+static LLVM::LLVMFuncOp+getOrInsertFunction(ModuleOp &module, ConversionPatternRewriter &rewriter,+                    std::string name, LLVM::LLVMType signature,+                    bool insertBodyAndTerminator = false) {+  auto func = module.lookupSymbol<LLVM::LLVMFuncOp>(name);+  if (!func) {+    OpBuilder moduleBuilder(module.getBodyRegion());+    func = moduleBuilder.create<LLVM::LLVMFuncOp>(rewriter.getUnknownLoc(),+                                                  name, signature);+    if (insertBodyAndTerminator) {+      func.addEntryBlock();+      OpBuilder b(func.getBody());+      b.create<LLVM::ReturnOp>(rewriter.getUnknownLoc(), ValueRange());+    }+  }+  return func;+}++/// Insert probe runtime call and extraction of details from the struct. The+/// mlir::Values of the details are returned, in struct-order.+static std::pair<Value, Value>+insertProbeSignal(ModuleOp &module, ConversionPatternRewriter &rewriter,+                  LLVM::LLVMDialect *dialect, Operation *op, Value statePtr,+                  Value signal) {+  auto i8PtrTy = LLVM::LLVMType::getInt8PtrTy(dialect);+  auto i32Ty = LLVM::LLVMType::getInt32Ty(dialect);+  auto i64Ty = LLVM::LLVMType::getInt64Ty(dialect);+  auto sigTy = LLVM::LLVMType::getStructTy(dialect, {i8PtrTy, i64Ty});++  auto prbSignature = LLVM::LLVMType::getFunctionTy(sigTy.getPointerTo(),+                                                    {i8PtrTy, i32Ty}, false);+  auto prbFunc =+      getOrInsertFunction(module, rewriter, "probe_signal", prbSignature);+  SmallVector<Value, 2> prbArgs({statePtr, signal});

nit: If you don't need resizing prefer arrays/std::array instead of SmallVector.

maerhart

comment created time in 3 months

more