diff --git a/include/circt/Dialect/CMakeLists.txt b/include/circt/Dialect/CMakeLists.txt index 798409471773..ce5f1cf54134 100644 --- a/include/circt/Dialect/CMakeLists.txt +++ b/include/circt/Dialect/CMakeLists.txt @@ -26,6 +26,7 @@ add_subdirectory(LTL) add_subdirectory(Moore) add_subdirectory(MSFT) add_subdirectory(OM) +add_subdirectory(OpLib) add_subdirectory(Pipeline) add_subdirectory(Ibis) add_subdirectory(Seq) diff --git a/include/circt/Dialect/Calyx/CalyxPrimitives.td b/include/circt/Dialect/Calyx/CalyxPrimitives.td index 1a9012b5796d..5d020d7c0e81 100644 --- a/include/circt/Dialect/Calyx/CalyxPrimitives.td +++ b/include/circt/Dialect/Calyx/CalyxPrimitives.td @@ -220,7 +220,7 @@ def SeqMemoryOp : CalyxPrimitive<"seq_mem", []> { ); let results = (outs - Variadic:$results + Variadic:$results ); let assemblyFormat = [{ diff --git a/include/circt/Dialect/OpLib/CMakeLists.txt b/include/circt/Dialect/OpLib/CMakeLists.txt new file mode 100644 index 000000000000..c98ed5653c6c --- /dev/null +++ b/include/circt/Dialect/OpLib/CMakeLists.txt @@ -0,0 +1,9 @@ +add_circt_dialect(OpLib oplib) +add_circt_dialect_doc(OpLib oplib) + +set(LLVM_TARGET_DEFINITIONS OpLib.td) + +mlir_tablegen(OpLibAttributes.h.inc -gen-attrdef-decls) +mlir_tablegen(OpLibAttributes.cpp.inc -gen-attrdef-defs) +add_public_tablegen_target(CIRCTOpLibAttributesIncGen) +add_dependencies(circt-headers CIRCTOpLibAttributesIncGen) diff --git a/include/circt/Dialect/OpLib/OpLib.td b/include/circt/Dialect/OpLib/OpLib.td new file mode 100644 index 000000000000..6e5111901c60 --- /dev/null +++ b/include/circt/Dialect/OpLib/OpLib.td @@ -0,0 +1,30 @@ +//===- OpLib.td - OpLib Definitions ------------------*- tablegen -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_OPLIB_DIALECT_TD +#define CIRCT_OPLIB_DIALECT_TD + +include "mlir/IR/DialectBase.td" + +def OpLib_Dialect : Dialect { + let name = "oplib"; + let cppNamespace = "::circt::oplib"; + let summary = "Operator library dialect"; + + let useDefaultAttributePrinterParser = true; + + let extraClassDeclaration = [{ + /// Register all SSP attributes. + void registerAttributes(); + }]; +} + +include "circt/Dialect/OpLib/OpLibAttributes.td" +include "circt/Dialect/OpLib/OpLibOps.td" + +#endif // CIRCT_OPLIB_DIALECT_TD diff --git a/include/circt/Dialect/OpLib/OpLibAttributes.h b/include/circt/Dialect/OpLib/OpLibAttributes.h new file mode 100644 index 000000000000..1ce4d8a19045 --- /dev/null +++ b/include/circt/Dialect/OpLib/OpLibAttributes.h @@ -0,0 +1,44 @@ +//===- OpLibAttributes.h - OpLib attribute definitions ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the OpLib dialect attributes. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_OPLIB_OPLIBATTRIBUTES_H +#define CIRCT_DIALECT_OPLIB_OPLIBATTRIBUTES_H + +#include "circt/Dialect/OpLib/OpLibDialect.h" +#include "circt/Scheduling/Problems.h" +#include "circt/Support/LLVM.h" + +#include "mlir/IR/OpImplementation.h" + +#define GET_ATTRDEF_CLASSES +#include "circt/Dialect/OpLib/OpLibAttributes.h.inc" + +namespace circt { +namespace oplib { + +/// Parse an array of attributes while recognizing the properties of the +/// OpLib dialect even without a `#oplib.` prefix. Any attributes +/// supplied in \p alreadyParsed are prepended to the parsed ones. +mlir::OptionalParseResult +parseOptionalPropertyArray(ArrayAttr &attr, AsmParser &parser, + ArrayRef alreadyParsed = {}); + +/// Print an array attribute, suppressing the `#oplib.` prefix for properties +/// defined in the OpLib dialect. Attributes mentioned in \p alreadyPrinted +/// are skipped. +void printPropertyArray(ArrayAttr attr, AsmPrinter &p, + ArrayRef alreadyPrinted = {}); + +} // namespace oplib +} // namespace circt + +#endif // CIRCT_DIALECT_OPLIB_OPLIBATTRIBUTES_H diff --git a/include/circt/Dialect/OpLib/OpLibAttributes.td b/include/circt/Dialect/OpLib/OpLibAttributes.td new file mode 100644 index 000000000000..70caba137b35 --- /dev/null +++ b/include/circt/Dialect/OpLib/OpLibAttributes.td @@ -0,0 +1,59 @@ +//===- OpLibAttributes.td - OpLib attributes ---------*- tablegen -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the OpLib dialect attributes. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Dialect attributes +//===----------------------------------------------------------------------===// + +include "mlir/IR/AttrTypeBase.td" + +class OperatorProperty traits = []> + : AttrDef { + + string propertyName = name; + string propertyType = type; + + // Unwrap the data in this attribute's `$value` parameter in order to pass it + // to the corresponding setter on the problem class. + // code unwrapValue = [{ getValue() }]; + // Wrap the `value` returned by the getter on the problem class in order to + // store it in this attribute's `$value` parameter. + // code wrapValue = [{ value }]; + + let summary = "Models the `" # propertyName # "` property for a OpLib operator."; + + let parameters = (ins propertyType:$value); + let mnemonic = propertyName; + let assemblyFormat = [{ `<` $value `>` }]; +} + +def IncomingDelayProp : OperatorProperty { + let mnemonic = "incDelay"; +} + +def OutgoingDelayProp : OperatorProperty { + let mnemonic = "outDelay"; +} + +def LimitProp : OperatorProperty { + let mnemonic = "limit"; +} + +def LatencyProp : OperatorProperty { + let mnemonic = "latency"; +} diff --git a/include/circt/Dialect/OpLib/OpLibDialect.h b/include/circt/Dialect/OpLib/OpLibDialect.h new file mode 100644 index 000000000000..13400f4cdb82 --- /dev/null +++ b/include/circt/Dialect/OpLib/OpLibDialect.h @@ -0,0 +1,22 @@ +//===- OpLibDialect.h - OpLib Definitions -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the OpLib CIRCT dialect. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_OPLIB_OPLIBDIALECT_H +#define CIRCT_DIALECT_OPLIB_OPLIBDIALECT_H + +#include "circt/Support/LLVM.h" +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/Dialect.h" + +#include "circt/Dialect/OpLib/OpLibDialect.h.inc" + +#endif // CIRCT_DIALECT_OPLIB_OPLIBDIALECT_H diff --git a/include/circt/Dialect/OpLib/OpLibOps.h b/include/circt/Dialect/OpLib/OpLibOps.h new file mode 100644 index 000000000000..fa963cacb9b2 --- /dev/null +++ b/include/circt/Dialect/OpLib/OpLibOps.h @@ -0,0 +1,31 @@ +//===- OpLibOps.h - OpLib Op Definitions ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_OPLIB_OPLIBOPS_H +#define CIRCT_DIALECT_OPLIB_OPLIBOPS_H + +#include "circt/Support/LLVM.h" +#include "mlir/IR/Attributes.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/BuiltinTypes.h" +#include "mlir/IR/Dialect.h" +#include "mlir/IR/OpDefinition.h" +#include "mlir/IR/OpImplementation.h" +#include "mlir/IR/Operation.h" +#include "mlir/Interfaces/ControlFlowInterfaces.h" +#include "mlir/Interfaces/FunctionInterfaces.h" +#include "mlir/Interfaces/InferTypeOpInterface.h" +#include "mlir/Interfaces/SideEffectInterfaces.h" + +#include "circt/Dialect/OpLib/OpLibDialect.h" + +#define GET_OP_CLASSES +#include "circt/Dialect/OpLib/OpLib.h.inc" + +#endif // CIRCT_DIALECT_OPLIB_OPLIBOPS_H diff --git a/include/circt/Dialect/OpLib/OpLibOps.td b/include/circt/Dialect/OpLib/OpLibOps.td new file mode 100644 index 000000000000..91993c9e6ca9 --- /dev/null +++ b/include/circt/Dialect/OpLib/OpLibOps.td @@ -0,0 +1,190 @@ +//===- OpLibOps.td - OpLib Op Definitions ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This describes the MLIR ops for OpLib. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_OPLIB_OPS_TD +#define CIRCT_DIALECT_OPLIB_OPS_TD + +include "circt/Dialect/OpLib/OpLib.td" +include "mlir/IR/OpBase.td" +include "mlir/Interfaces/ControlFlowInterfaces.td" +include "mlir/Interfaces/FunctionInterfaces.td" +include "mlir/IR/SymbolInterfaces.td" + +class OpLibOp traits = []> : + Op; + +def LibraryOp : OpLibOp<"library", [IsolatedFromAbove, SingleBlock, + Symbol, SymbolTable, NoTerminator, + NoRegionArguments, + HasParent<"mlir::ModuleOp">]> { + let summary = "OpLib dialect Library operation"; + let description = [{ + The `oplib.library` operation represents an operator library to be + used by passes that lower to one of a few hardware design dialects. + A library is made up of a single block and contains only + `oplib.operator` ops. + }]; + + let arguments = (ins SymbolNameAttr:$sym_name); + let results = (outs); + let regions = (region SizedRegion<1>:$bodyRegion); + let assemblyFormat = [{ + $sym_name attr-dict-with-keyword $bodyRegion + }]; + + let hasVerifier = 1; +} + +def OperatorOp : OpLibOp<"operator", [IsolatedFromAbove, + SingleBlock, + NoRegionArguments, + Symbol, SymbolTable, + HasParent<"LibraryOp">]> { + let summary = "OpLib dialect Operator operation"; + let description = [{ + The `oplib.operator` operation represents an operator in the + operator library. + }]; + + let arguments = (ins SymbolNameAttr:$sym_name, + I32Attr:$latency, + OptionalAttr:$incDelay, + OptionalAttr:$outDelay); + let results = (outs); + let regions = (region SizedRegion<1>:$bodyRegion); + let assemblyFormat = [{ + $sym_name `latency` `<` $latency `>` (`,` `incDelay` `<` $incDelay^ `>`)? + (`,` `outDelay` `<` $outDelay^ `>`)? attr-dict-with-keyword $bodyRegion + }]; + + let hasVerifier = 1; +} + +def TargetOp : OpLibOp<"target", [Symbol, + FunctionOpInterface, + SingleBlockImplicitTerminator<"OutputOp">, + HasParent<"OperatorOp">]> { + let summary = "OpLib dialect Target operation"; + let description = [{ + }]; + + let arguments = (ins SymbolNameAttr:$sym_name, + TypeAttrOf:$function_type, + OptionalAttr:$arg_attrs, + OptionalAttr:$res_attrs); + + let results = (outs); + let regions = (region SizedRegion<1>:$bodyRegion); + let hasCustomAssemblyFormat = 1; + + let extraClassDeclaration = [{ + /// Returns the argument types of this function. + ArrayRef getArgumentTypes() { return getFunctionType().getInputs(); } + + /// Returns the result types of this function. + ArrayRef getResultTypes() { return getFunctionType().getResults(); } + + /// Returns the region on the current operation that is callable. This may + /// return null in the case of an external callable object, e.g. an external + /// function. + ::mlir::Region *getCallableRegion() { + return &getBodyRegion(); + } + + /// Verify the type attribute of this function. Returns failure and emits + /// an error if the attribute is invalid. + LogicalResult verifyType() { + auto type = getFunctionTypeAttr().getValue(); + if (!llvm::isa(type)) + return emitOpError("requires '" + getFunctionTypeAttrName().getValue() + + "' attribute of function type"); + return success(); + } + }]; + // let assemblyFormat = [{ + // $sym_name custom($function_type) attr-dict-with-keyword $bodyRegion + // }]; + // let hasVerifier = 1; +} + +def OperationOp : OpLibOp<"operation", [HasParent<"TargetOp">]> { + let summary = "OpLib dialect Operation operation"; + let description = [{ + }]; + let arguments = (ins Variadic:$operands, + StrAttr:$opName, + StrAttr:$dialectName, + OptionalAttr:$opDict); + let results = (outs Variadic:$results); + let assemblyFormat = [{ + $opName `in` $dialectName (`with` $opDict^)? `(` ($operands^ `:` qualified(type($operands)))? `)` + attr-dict (`:` qualified(type($results))^)? + }]; +} + +def CalyxMatchOp : OpLibOp<"calyx_match", + [Terminator, SymbolTable, + HasParent<"OperatorOp">, + NoRegionArguments, + SingleBlock, + DeclareOpInterfaceMethods]> { + let summary = "OpLib dialect Target operation"; + let description = [{ + }]; + + let arguments = (ins FlatSymbolRefAttr:$target, + TypeAttrOf:$targetType); + + let results = (outs); + let regions = (region SizedRegion<1>:$bodyRegion); + + let assemblyFormat = [{ + `(` $target `:` $targetType `)` `produce` attr-dict-with-keyword $bodyRegion + }]; + // let hasVerifier = 1; +} + +def OutputOp : OpLibOp<"output", [Terminator, + ReturnLike, ParentOneOf<["TargetOp"]>]> { + let summary = "OpLib dialect Output operation"; + let description = [{ + The `oplib.output` operation. + }]; + let arguments = (ins Variadic:$outputs); + let assemblyFormat = [{ + attr-dict ($outputs^ `:` qualified(type($outputs)))? + }]; +} + +def YieldOp : OpLibOp<"yield", [Terminator, + ReturnLike, + AttrSizedOperandSegments, + ParentOneOf<["CalyxMatchOp"]>]> { + let summary = "OpLib dialect Yield operation"; + let description = [{ + The `oplib.output` operation. + }]; + let arguments = (ins Optional:$clock, + Optional:$clockEnable, + Optional:$reset, + Variadic:$inputs, + Variadic:$outputs); + let assemblyFormat = [{ + attr-dict (`clk` `(` $clock^ `:` qualified(type($clock)) `)` `,` )? + (`ce` `(` $clockEnable^ `:` qualified(type($clockEnable)) `)` `,` )? + (`reset` `(` $reset^ `:` qualified(type($reset)) `)` `,` )? + (`ins` `(` $inputs^ `:` qualified(type($inputs)) `)` `,` )? + (`outs` `(` $outputs^ `:` qualified(type($outputs)) `)`)? + }]; +} + +#endif // CIRCT_DIALECT_OPLIB_OPS_TD diff --git a/include/circt/InitAllDialects.h b/include/circt/InitAllDialects.h index 3212a01c154d..ad14a3b8f857 100644 --- a/include/circt/InitAllDialects.h +++ b/include/circt/InitAllDialects.h @@ -36,6 +36,7 @@ #include "circt/Dialect/MSFT/MSFTDialect.h" #include "circt/Dialect/Moore/MooreDialect.h" #include "circt/Dialect/OM/OMDialect.h" +#include "circt/Dialect/OpLib/OpLibDialect.h" #include "circt/Dialect/Pipeline/PipelineDialect.h" #include "circt/Dialect/SMT/SMTDialect.h" #include "circt/Dialect/SSP/SSPDialect.h" @@ -55,6 +56,7 @@ inline void registerAllDialects(mlir::DialectRegistry ®istry) { aig::AIGDialect, arc::ArcDialect, calyx::CalyxDialect, + oplib::OpLibDialect, chirrtl::CHIRRTLDialect, comb::CombDialect, dc::DCDialect, diff --git a/lib/Dialect/CMakeLists.txt b/lib/Dialect/CMakeLists.txt index eb30485abf79..ae655d32e593 100644 --- a/lib/Dialect/CMakeLists.txt +++ b/lib/Dialect/CMakeLists.txt @@ -30,6 +30,7 @@ add_subdirectory(LTL) add_subdirectory(Moore) add_subdirectory(MSFT) add_subdirectory(OM) +add_subdirectory(OpLib) add_subdirectory(Pipeline) add_subdirectory(Seq) add_subdirectory(Sim) diff --git a/lib/Dialect/Calyx/CalyxOps.cpp b/lib/Dialect/Calyx/CalyxOps.cpp index c4f673bd78ff..35d5dcaace77 100644 --- a/lib/Dialect/Calyx/CalyxOps.cpp +++ b/lib/Dialect/Calyx/CalyxOps.cpp @@ -209,10 +209,7 @@ LogicalResult calyx::verifyComponent(Operation *op) { } LogicalResult calyx::verifyCell(Operation *op) { - auto opParent = op->getParentOp(); - if (!isa(opParent)) - return op->emitOpError() - << "has parent: " << opParent << ", expected ComponentInterface."; + // auto opParent = op->getParentOp(); return success(); } @@ -1674,12 +1671,14 @@ static LogicalResult verifyPrimitiveOpType(PrimitiveOp instance, hw::HWModuleExternOp referencedPrimitive) { auto module = instance->getParentOfType(); - StringRef entryPointName = - module->getAttrOfType("calyx.entrypoint"); - if (instance.getPrimitiveName() == entryPointName) - return instance.emitOpError() - << "cannot reference the entry-point component: '" << entryPointName - << "'."; + if (module->hasAttrOfType("calyx.entrypoint")) { + StringRef entryPointName = + module->getAttrOfType("calyx.entrypoint"); + if (instance.getPrimitiveName() == entryPointName) + return instance.emitOpError() + << "cannot reference the entry-point component: '" + << entryPointName << "'."; + } // Verify the instance result ports with those of its referenced component. auto primitivePorts = referencedPrimitive.getPortList(); diff --git a/lib/Dialect/OpLib/CMakeLists.txt b/lib/Dialect/OpLib/CMakeLists.txt new file mode 100644 index 000000000000..ea0805358d2c --- /dev/null +++ b/lib/Dialect/OpLib/CMakeLists.txt @@ -0,0 +1,18 @@ +add_circt_dialect_library(CIRCTOpLib + OpLibDialect.cpp + OpLibOps.cpp + OpLibAttributes.cpp + + ADDITIONAL_HEADER_DIRS + ${CIRCT_MAIN_INCLUDE_DIR}/circt/Dialect/OpLib + + LINK_LIBS PUBLIC + MLIRIR + CIRCTSupport + + DEPENDS + CIRCTSupport + MLIROpLibIncGen + CIRCTOpLibAttributesIncGen + CIRCTCalyx +) diff --git a/lib/Dialect/OpLib/OpLibAttributes.cpp b/lib/Dialect/OpLib/OpLibAttributes.cpp new file mode 100644 index 000000000000..6869a28ae179 --- /dev/null +++ b/lib/Dialect/OpLib/OpLibAttributes.cpp @@ -0,0 +1,108 @@ +//===- OpLibAttributes.cpp - OpLib attribute implementation +//-------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the OpLib (static scheduling problem) dialect +// attributes. +// +//===----------------------------------------------------------------------===// + +#include "circt/Dialect/OpLib/OpLibAttributes.h" +#include "circt/Dialect/OpLib/OpLibDialect.h" + +#include "mlir/IR/Builders.h" +#include "mlir/IR/DialectImplementation.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/TypeSwitch.h" + +using namespace circt; +using namespace oplib; + +#define GET_ATTRDEF_CLASSES +#include "circt/Dialect/OpLib/OpLibAttributes.cpp.inc" + +void OpLibDialect::registerAttributes() { + addAttributes< +#define GET_ATTRDEF_LIST +#include "circt/Dialect/OpLib/OpLibAttributes.cpp.inc" + >(); +} + +mlir::OptionalParseResult +oplib::parseOptionalPropertyArray(ArrayAttr &attr, AsmParser &parser, + ArrayRef alreadyParsed) { + auto &builder = parser.getBuilder(); + + if (parser.parseOptionalLSquare()) { + if (!alreadyParsed.empty()) { + attr = builder.getArrayAttr(alreadyParsed); + return success(); + } + return {}; + } + + SmallVector elements; + elements.append(alreadyParsed.begin(), alreadyParsed.end()); + + auto parseListResult = parser.parseCommaSeparatedList([&]() -> ParseResult { + Attribute elem; + + // Try to parse a generic attribute. + auto parseGenericAttrResult = parser.parseOptionalAttribute(elem); + if (parseGenericAttrResult.has_value()) { + if (failed(*parseGenericAttrResult)) + return failure(); + + elements.push_back(elem); + return success(); + } + + // Try to parse one of the built-in OpLib property attributes. + StringRef mnemonic; + auto parseShortformAttrResult = + generatedAttributeParser(parser, &mnemonic, Type(), elem); + + if (!parseShortformAttrResult.has_value()) { + return parser.emitError(parser.getCurrentLocation(), + "carries unknown shortform property: ") + << mnemonic; + } + + if (failed(*parseShortformAttrResult)) + return failure(); + + elements.push_back(elem); + return success(); + }); + + if (parseListResult || parser.parseRSquare()) + return failure(); + + attr = builder.getArrayAttr(elements); + return success(); +} + +void oplib::printPropertyArray(ArrayAttr attr, AsmPrinter &p, + ArrayRef alreadyPrinted) { + auto elementsToPrint = + llvm::make_filter_range(attr.getAsRange(), [&](Attribute a) { + return !llvm::is_contained(alreadyPrinted, a); + }); + if (elementsToPrint.empty()) + return; + + p << '['; + llvm::interleaveComma(elementsToPrint, p, [&](Attribute elem) { + // Try to emit the shortform for the built-in OpLib property attributes, and + // if that fails, fall back to the generic form. + if (failed(generatedAttributePrinter(elem, p))) + p.printAttribute(attr); + }); + p << ']'; +} diff --git a/lib/Dialect/OpLib/OpLibDialect.cpp b/lib/Dialect/OpLib/OpLibDialect.cpp new file mode 100644 index 000000000000..91a3b07449b3 --- /dev/null +++ b/lib/Dialect/OpLib/OpLibDialect.cpp @@ -0,0 +1,35 @@ +//===- OpLibDialect.cpp - Implement the OpLib dialect ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the OpLib dialect. +// +//===----------------------------------------------------------------------===// + +#include "circt/Dialect/OpLib/OpLibDialect.h" +#include "circt/Dialect/OpLib/OpLibOps.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/BuiltinTypes.h" +#include "mlir/IR/DialectImplementation.h" + +using namespace circt; +using namespace oplib; + +//===----------------------------------------------------------------------===// +// Dialect specification. +//===----------------------------------------------------------------------===// + +void OpLibDialect::initialize() { + // Register operations. + addOperations< +#define GET_OP_LIST +#include "circt/Dialect/OpLib/OpLib.cpp.inc" + >(); + registerAttributes(); +} + +#include "circt/Dialect/OpLib/OpLibDialect.cpp.inc" diff --git a/lib/Dialect/OpLib/OpLibOps.cpp b/lib/Dialect/OpLib/OpLibOps.cpp new file mode 100644 index 000000000000..0731e97f2758 --- /dev/null +++ b/lib/Dialect/OpLib/OpLibOps.cpp @@ -0,0 +1,205 @@ +//===- OpLibOps.cpp - OpLib Dialect Operations ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implement the OpLib ops. +// +//===----------------------------------------------------------------------===// + +#include "circt/Dialect/OpLib/OpLibOps.h" +#include "circt/Dialect/HW/HWOps.h" +#include "circt/Dialect/OpLib/OpLibAttributes.h" +#include "mlir/IR/Attributes.h" +#include "mlir/IR/OpImplementation.h" +#include "mlir/IR/PatternMatch.h" +#include "mlir/Interfaces/FunctionImplementation.h" +#include "llvm/Support/LogicalResult.h" + +using namespace mlir; +using namespace circt; +using namespace circt::oplib; + +//===----------------------------------------------------------------------===// +// LibraryOp +//===----------------------------------------------------------------------===// + +LogicalResult LibraryOp::verify() { + for (auto &op : this->getBodyRegion().getOps()) { + if (!isa(op)) + return emitOpError("can only contain OperatorOps"); + } + + return success(); +} + +//===----------------------------------------------------------------------===// +// OperatorOp +//===----------------------------------------------------------------------===// + +LogicalResult OperatorOp::verify() { + auto *term = getBodyRegion().front().getTerminator(); + if (!isa(term)) { + return emitOpError("region terminator must be supported match op"); + } + + if (getIncDelay().has_value() != getOutDelay().has_value()) { + return emitOpError( + "must have either both incDelay and outDelay or neither"); + } + + if (getLatency() == 0 && + (getIncDelay().has_value() || getOutDelay().has_value())) { + if (getIncDelay() != getOutDelay()) { + return emitError( + "incDelay and outDelay of combinational operators must be the same"); + } + } + + return success(); +} + +//===----------------------------------------------------------------------===// +// TargetOp +//===----------------------------------------------------------------------===// + +ParseResult TargetOp::parse(OpAsmParser &parser, OperationState &result) { + auto buildFuncType = + [](Builder &builder, ArrayRef argTypes, ArrayRef results, + function_interface_impl::VariadicFlag, + std::string &) { return builder.getFunctionType(argTypes, results); }; + + return function_interface_impl::parseFunctionOp( + parser, result, /*allowVariadic=*/false, + getFunctionTypeAttrName(result.name), buildFuncType, + getArgAttrsAttrName(result.name), getResAttrsAttrName(result.name)); +} + +void TargetOp::print(OpAsmPrinter &p) { + function_interface_impl::printFunctionOp( + p, *this, /*isVariadic=*/false, getFunctionTypeAttrName(), + getArgAttrsAttrName(), getResAttrsAttrName()); +} + +//===----------------------------------------------------------------------===// +// CalyxMatchOp +//===----------------------------------------------------------------------===// + +LogicalResult +CalyxMatchOp::verifySymbolUses(SymbolTableCollection &symbolTable) { + StringRef targetName = getTarget(); + Type type = getTargetType(); + + // Try to find the referenced target. + auto fn = symbolTable.lookupSymbolIn( + this->getParentOp(), + StringAttr::get(getContext(), targetName)); + if (!fn) + return emitOpError() << "reference to undefined target '" << targetName + << "'"; + + // Check that the referenced function has the correct type. + if (fn.getFunctionType() != type) + return emitOpError("reference to target with mismatched type"); + + return success(); +} + +//===----------------------------------------------------------------------===// +// Wrappers for the `custom` ODS directive. +//===----------------------------------------------------------------------===// + +static ParseResult parseOpLibProperties(OpAsmParser &parser, ArrayAttr &attr) { + auto result = parseOptionalPropertyArray(attr, parser); + if (!result.has_value() || succeeded(*result)) + return success(); + return failure(); +} + +static void printOpLibProperties(OpAsmPrinter &p, Operation *op, + ArrayAttr attr) { + if (!attr) + return; + printPropertyArray(attr, p); +} + +static ParseResult parseTargetTypes(OpAsmParser &parser, TypeAttr &attr) { + auto &builder = parser.getBuilder(); + SmallVector arguments; + + auto result = parser.parseCommaSeparatedList( + OpAsmParser::Delimiter::Paren, [&]() -> ParseResult { + // Parse argument name if present. + OpAsmParser::Argument argument; + auto argPresent = parser.parseOptionalArgument( + argument, /*allowType=*/true, /*allowAttrs=*/true); + if (argPresent.has_value()) { + if (failed(argPresent.value())) + return failure(); // Present but malformed. + + // Reject this if the preceding argument was missing a name. + if (!arguments.empty() && arguments.back().ssaName.name.empty()) + return parser.emitError(argument.ssaName.location, + "expected type instead of SSA identifier"); + + } else { + return failure(); + } + arguments.push_back(argument); + return success(); + }); + if (failed(result)) + return failure(); + + // Parse the function signature. + SMLoc signatureLocation = parser.getCurrentLocation(); + std::string errorMessage; + SmallVector argTypes; + SmallVector resultTypes; + argTypes.reserve(arguments.size()); + for (auto &arg : arguments) + argTypes.push_back(arg.type); + Type type = builder.getFunctionType(argTypes, resultTypes); + if (!type) { + return parser.emitError(signatureLocation) + << "failed to construct function type" + << (errorMessage.empty() ? "" : ": ") << errorMessage; + } + attr = TypeAttr::get(type); + + return success(); +} + +static void printTargetTypes(OpAsmPrinter &p, Operation *op, TypeAttr attr) { + if (!attr) + return; + + Region &body = op->getRegion(1); + + auto funcType = llvm::dyn_cast_or_null(attr.getValue()); + + if (funcType == nullptr) + return; + + ArrayRef argTypes = funcType.getInputs(); + p << '('; + for (unsigned i = 0, e = argTypes.size(); i < e; ++i) { + if (i > 0) + p << ", "; + + ArrayRef attrs; + p.printRegionArgument(body.getArgument(i), attrs); + } + + p << ')'; +} + +//===----------------------------------------------------------------------===// +// TableGen'ed code +//===----------------------------------------------------------------------===// + +#define GET_OP_CLASSES +#include "circt/Dialect/OpLib/OpLib.cpp.inc" diff --git a/test/Dialect/OpLib/errors.mlir b/test/Dialect/OpLib/errors.mlir new file mode 100644 index 000000000000..1d016fd829b9 --- /dev/null +++ b/test/Dialect/OpLib/errors.mlir @@ -0,0 +1,68 @@ +// RUN: circt-opt %s -split-input-file -verify-diagnostics + +oplib.library @lib0 { + oplib.operator @addi latency<0>, incDelay<0.2>, outDelay<0.2> { + oplib.target @target0(%l: i32, %r: i32) -> i32 { + %o = oplib.operation "addi" in "arith"(%l, %r : i32, i32) : i32 + oplib.output %o : i32 + } + oplib.calyx_match(@target0 : (i32, i32) -> i32) produce { + %left, %right, %out = calyx.std_add @add : i32, i32, i32 + oplib.yield ins(%left, %right : i32, i32), outs(%out : i32) + } + } +} + +// ----- + +// expected-error @+1 {{can only contain OperatorOps}} +oplib.library @lib0 { + func.func @main() { + func.return + } +} + +// ----- + +oplib.library @lib0 { + // expected-error @+1 {{region terminator must be supported match op}} + oplib.operator @addi latency<0>, incDelay<0.2>, outDelay<0.2> { + oplib.target @target0(%l: i32, %r: i32) -> i32 { + %o = oplib.operation "addi" in "arith"(%l, %r : i32, i32) : i32 + oplib.output %o : i32 + } + func.return + } +} + +// ----- + +oplib.library @lib0 { + // expected-error @+1 {{must have either both incDelay and outDelay or neither}} + oplib.operator @addi latency<0>, incDelay<0.2> { + oplib.target @target0(%l: i32, %r: i32) -> i32 { + %o = oplib.operation "addi" in "arith"(%l, %r : i32, i32) : i32 + oplib.output %o : i32 + } + oplib.calyx_match(@target0 : (i32, i32) -> i32) produce { + %left, %right, %out = calyx.std_add @add : i32, i32, i32 + oplib.yield ins(%left, %right : i32, i32), outs(%out : i32) + } + } +} + +// ----- + +oplib.library @lib0 { + // expected-error @+1 {{incDelay and outDelay of combinational operators must be the same}} + oplib.operator @addi latency<0>, incDelay<0.2>, outDelay<0.5> { + oplib.target @target0(%l: i32, %r: i32) -> i32 { + %o = oplib.operation "addi" in "arith"(%l, %r : i32, i32) : i32 + oplib.output %o : i32 + } + oplib.calyx_match(@target0 : (i32, i32) -> i32) produce { + %left, %right, %out = calyx.std_add @add : i32, i32, i32 + oplib.yield ins(%left, %right : i32, i32), outs(%out : i32) + } + } +} diff --git a/test/Dialect/OpLib/round-trip.mlir b/test/Dialect/OpLib/round-trip.mlir new file mode 100644 index 000000000000..7653ae30430a --- /dev/null +++ b/test/Dialect/OpLib/round-trip.mlir @@ -0,0 +1,66 @@ +// RUN: circt-opt %s | circt-opt | FileCheck %s + +hw.module.extern @ext_fmult(in %clk : i1 {calyx.clk}, in %left : i32 {calyx.data}, in %right : i32 {calyx.data}, in %ce : i1, out result : i32 {calyx.stable, calyx.data}) attributes {filename = "fmult.sv", verilogName = "fmult"} + +// CHECK-LABEL: @lib0 + +// CHECK-LABEL: oplib.operator @fmult latency<4>, incDelay<5.000000e-01>, outDelay<5.000000e-01> +// CHECK-NEXT: oplib.target @target0(%[[VAL_0:.*]]: f32, %[[VAL_1:.*]]: f32) -> f32 +// CHECK-NEXT: %[[VAL_2:.*]] = oplib.operation "mulf" in "arith"(%[[VAL_0]], %[[VAL_1]] : f32, f32) : f32 +// CHECK-NEXT: oplib.output %[[VAL_2]] : f32 +// CHECK: oplib.calyx_match(@target0 : (f32, f32) -> f32) produce +// CHECK-NEXT: %[[VAL_3:.*]], %[[VAL_4:.*]], %[[VAL_5:.*]], %[[VAL_6:.*]], %[[VAL_7:.*]] = calyx.primitive @fmult of @ext_fmult : i1, i32, i32, i1, i32 +// CHECK-NEXT: oplib.yield clk(%[[VAL_3]] : i1), ce(%[[VAL_6]] : i1), ins(%[[VAL_4]], %[[VAL_5]] : i32, i32), outs(%[[VAL_7]] : i32) + +// CHECK-LABEL: oplib.operator @addi latency<0>, incDelay<2.000000e-01>, outDelay<2.000000e-01> +// CHECK-NEXT: oplib.target @target0(%[[VAL_8:.*]]: i32, %[[VAL_9:.*]]: i32) -> i32 +// CHECK-NEXT: %[[VAL_10:.*]] = oplib.operation "addi" in "arith"(%[[VAL_8]], %[[VAL_9]] : i32, i32) : i32 +// CHECK-NEXT: oplib.output %[[VAL_10]] : i32 +// CHECK: oplib.calyx_match(@target0 : (i32, i32) -> i32) produce +// CHECK-NEXT: %[[VAL_11:.*]], %[[VAL_12:.*]], %[[VAL_13:.*]] = calyx.std_add @add : i32, i32, i32 +// CHECK-NEXT: oplib.yield ins(%[[VAL_11]], %[[VAL_12]] : i32, i32), outs(%[[VAL_13]] : i32) + +// CHECK-LABEL: oplib.operator @trunci latency<0> + +oplib.library @lib0 { + oplib.operator @fmult latency<4>, incDelay<0.5>, outDelay<0.5> { + oplib.target @target0(%l: f32, %r: f32) -> f32 { + %o = oplib.operation "mulf" in "arith"(%l, %r : f32, f32) : f32 + oplib.output %o : f32 + } + oplib.calyx_match(@target0 : (f32, f32) -> f32) produce { + %clk, %left, %right, %ce, %result = calyx.primitive @fmult of @ext_fmult : i1, i32, i32, i1, i32 + oplib.yield clk(%clk : i1), ce(%ce : i1), ins(%left, %right : i32, i32), outs(%result : i32) + } + } + oplib.operator @addi latency<0>, incDelay<0.2>, outDelay<0.2> { + oplib.target @target0(%l: i32, %r: i32) -> i32 { + %o = oplib.operation "addi" in "arith"(%l, %r : i32, i32) : i32 + oplib.output %o : i32 + } + oplib.calyx_match(@target0 : (i32, i32) -> i32) produce { + %left, %right, %out = calyx.std_add @add : i32, i32, i32 + oplib.yield ins(%left, %right : i32, i32), outs(%out : i32) + } + } + oplib.operator @trunci latency<0> { + oplib.target @target0(%in: i5) -> i4 { + %o = oplib.operation "trunci" in "arith"(%in : i5) : i4 + oplib.output %o : i4 + } + oplib.calyx_match(@target0 : (i5) -> i4) produce { + %slice.in, %slice.out = calyx.std_slice @slice : i5, i4 + oplib.yield ins(%slice.in : i5), outs(%slice.out : i4) + } + } + oplib.operator @cmpi latency<0> { + oplib.target @target0(%l: i32, %r: i32) -> i1 { + %o = oplib.operation "cmpi" in "arith" with {predicate = 0 : i64}(%l, %r : i32, i32) : i1 + oplib.output %o : i1 + } + oplib.calyx_match(@target0 : (i32, i32) -> i1) produce { + %eq.left, %eq.right, %eq.out = calyx.std_eq @eq : i32, i32, i1 + oplib.yield ins(%eq.left, %eq.right : i32, i32), outs(%eq.out : i1) + } + } +}