mirror of https://github.com/llvm/circt.git
[SV] Add sv.func, sv.return and sv.func.call operations (#7003)
This PR adds function call constructs (sv.func, sv.return and sv.func.call) to SV dialect. * `sv.func` -- this op is between hw.module and func.func. This op implement interfaces `PortList`, `HWModuleLike` and `FunctionOpInterface`. Similar to hw.module, we can preserve argument names and mix the order of input and output arguments. Following func.func empty body indicates that the op is a declaration. Currently sv/func is defined as "automatic" function in SV. * `sv.return` -- terminator for sv.func. We cannot reuse neither hw.output nor func.func since they have constraints on parent op(and I think it's not reasonable to loose them). * `sv.func.call` and `sv.func.call.procedural`. Two operations are added because output arguments cannot be used non-procedural regions.
This commit is contained in:
parent
b64af242f2
commit
bd259e788a
|
@ -17,6 +17,9 @@
|
|||
#include "mlir/Pass/Pass.h"
|
||||
|
||||
namespace circt {
|
||||
namespace hw {
|
||||
class HWModuleLike;
|
||||
} // namespace hw
|
||||
|
||||
std::unique_ptr<mlir::Pass>
|
||||
createTestApplyLoweringOptionPass(llvm::StringRef options);
|
||||
|
|
|
@ -50,7 +50,16 @@ void printModuleSignature(OpAsmPrinter &p, Operation *op,
|
|||
ParseResult parseModuleSignature(OpAsmParser &parser,
|
||||
SmallVectorImpl<PortParse> &args,
|
||||
TypeAttr &modType);
|
||||
|
||||
void printModuleSignatureNew(OpAsmPrinter &p, Region &body,
|
||||
hw::ModuleType modType,
|
||||
ArrayRef<Attribute> portAttrs,
|
||||
ArrayRef<Location> locAttrs);
|
||||
void printModuleSignatureNew(OpAsmPrinter &p, HWModuleLike op);
|
||||
void getAsmBlockArgumentNamesImpl(mlir::Region ®ion,
|
||||
OpAsmSetValueNameFn setNameFn);
|
||||
|
||||
SmallVector<Location> getAllPortLocsImpl(hw::ModuleType modType);
|
||||
|
||||
} // namespace module_like_impl
|
||||
} // namespace hw
|
||||
|
|
|
@ -22,7 +22,7 @@ def SVDialect : Dialect {
|
|||
This dialect defines the `sv` dialect, which represents various
|
||||
SystemVerilog-specific constructs in an AST-like representation.
|
||||
}];
|
||||
let dependentDialects = ["circt::comb::CombDialect"];
|
||||
let dependentDialects = ["circt::comb::CombDialect", "circt::hw::HWDialect"];
|
||||
|
||||
let useDefaultTypePrinterParser = 1;
|
||||
let useDefaultAttributePrinterParser = 1;
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
|
||||
include "mlir/IR/EnumAttr.td"
|
||||
include "mlir/IR/OpAsmInterface.td"
|
||||
include "mlir/Interfaces/FunctionInterfaces.td"
|
||||
include "mlir/Interfaces/ControlFlowInterfaces.td"
|
||||
include "circt/Dialect/Emit/EmitOpInterfaces.td"
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Control flow like-operations
|
||||
|
@ -881,6 +884,169 @@ def MacroDefOp : SVOp<"macro.def",
|
|||
}
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Function/Call
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def FuncOp : SVOp<"func",
|
||||
[IsolatedFromAbove, Symbol, OpAsmOpInterface, ProceduralRegion,
|
||||
DeclareOpInterfaceMethods<HWModuleLike>,
|
||||
DeclareOpInterfaceMethods<PortList>,
|
||||
FunctionOpInterface, HasParent<"mlir::ModuleOp">]> {
|
||||
let summary = "A SystemVerilog function";
|
||||
let description = [{
|
||||
`sv.func` represents SystemVerilog function in IEEE 1800-2017 section 13.4
|
||||
"Functions". Similar to HW module, it's allowed to mix the order of input
|
||||
and output arguments. `sv.func` can be used for both function
|
||||
declaration and definition, i.e. a function without a body
|
||||
region is a declaration.
|
||||
|
||||
In SV there are two representations for function results,
|
||||
"output argument" and "return value". Currently an output argument
|
||||
is considered as as a return value if it's is the last argument
|
||||
and has a special attribute `sv.func.explicitly_returned`.
|
||||
}];
|
||||
|
||||
let arguments = (ins
|
||||
SymbolNameAttr:$sym_name,
|
||||
TypeAttrOf<ModuleType>:$module_type,
|
||||
OptionalAttr<DictArrayAttr>:$per_argument_attrs,
|
||||
OptionalAttr<LocationArrayAttr>:$input_locs, // Null for actual definition.
|
||||
OptionalAttr<LocationArrayAttr>:$result_locs,
|
||||
OptionalAttr<StrAttr>:$verilogName
|
||||
);
|
||||
|
||||
let results = (outs);
|
||||
let regions = (region AnyRegion:$body);
|
||||
|
||||
let hasCustomAssemblyFormat = 1;
|
||||
let extraClassDeclaration = [{
|
||||
static mlir::StringRef getExplicitlyReturnedAttrName() {
|
||||
return "sv.func.explicitly_returned";
|
||||
}
|
||||
|
||||
mlir::FunctionType getFunctionType() {
|
||||
return getModuleType().getFuncType();
|
||||
}
|
||||
|
||||
void setFunctionTypeAttr(mlir::TypeAttr mlirType) {
|
||||
setModuleType(cast<hw::ModuleType>(mlirType.getValue()));
|
||||
}
|
||||
|
||||
/// Returns the argument types of this function.
|
||||
ArrayRef<Type> getArgumentTypes() { return getFunctionType().getInputs(); }
|
||||
|
||||
/// Returns the result types of this function.
|
||||
ArrayRef<Type> getResultTypes() { return getFunctionType().getResults(); }
|
||||
|
||||
Type getExplicitlyReturnedType();
|
||||
|
||||
size_t getNumOutputs() {
|
||||
return getResultTypes().size();
|
||||
}
|
||||
|
||||
size_t getNumInputs() {
|
||||
return getArgumentTypes().size();
|
||||
}
|
||||
|
||||
::mlir::Region *getCallableRegion() { return isExternal() ? nullptr : &getBody(); }
|
||||
bool isDeclaration() { return isExternal(); }
|
||||
|
||||
/// OpAsmInterface
|
||||
void getAsmBlockArgumentNames(mlir::Region ®ion,
|
||||
mlir::OpAsmSetValueNameFn setNameFn);
|
||||
SmallVector<hw::PortInfo> getPortList(bool excludeExplicitReturn);
|
||||
}];
|
||||
let extraClassDefinition = [{
|
||||
hw::ModuleType $cppClass::getHWModuleType() {
|
||||
return getModuleType();
|
||||
}
|
||||
void $cppClass::setHWModuleType(hw::ModuleType type) {
|
||||
return setModuleType(type);
|
||||
}
|
||||
void $cppClass::setAllPortNames(llvm::ArrayRef<mlir::Attribute>) {
|
||||
}
|
||||
|
||||
size_t $cppClass::getNumPorts() {
|
||||
auto modty = getHWModuleType();
|
||||
return modty.getNumPorts();
|
||||
}
|
||||
|
||||
size_t $cppClass::getNumInputPorts() {
|
||||
auto modty = getHWModuleType();
|
||||
return modty.getNumInputs();
|
||||
}
|
||||
|
||||
size_t $cppClass::getNumOutputPorts() {
|
||||
auto modty = getHWModuleType();
|
||||
return modty.getNumOutputs();
|
||||
}
|
||||
|
||||
size_t $cppClass::getPortIdForInputId(size_t idx) {
|
||||
auto modty = getHWModuleType();
|
||||
return modty.getPortIdForInputId(idx);
|
||||
}
|
||||
|
||||
size_t $cppClass::getPortIdForOutputId(size_t idx) {
|
||||
auto modty = getHWModuleType();
|
||||
return modty.getPortIdForOutputId(idx);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
class SVFuncCallBase<string mnemonic, list<Trait> traits = []>: SVOp<mnemonic,
|
||||
traits # [CallOpInterface, DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
|
||||
|
||||
let arguments = (ins FlatSymbolRefAttr:$callee, Variadic<AnyType>:$inputs);
|
||||
let results = (outs Variadic<AnyType>);
|
||||
|
||||
let assemblyFormat =
|
||||
"$callee `(` $inputs `)` attr-dict `:` functional-type($inputs, results)";
|
||||
|
||||
let extraClassDeclaration = [{
|
||||
Value getExplicitlyReturnedValue(sv::FuncOp op);
|
||||
|
||||
operand_range getArgOperands() {
|
||||
return getInputs();
|
||||
}
|
||||
MutableOperandRange getArgOperandsMutable() {
|
||||
return getInputsMutable();
|
||||
}
|
||||
mlir::CallInterfaceCallable getCallableForCallee() {
|
||||
return (*this)->getAttrOfType<mlir::SymbolRefAttr>("callee");
|
||||
}
|
||||
|
||||
/// Set the callee for this operation.
|
||||
void setCalleeFromCallable(mlir::CallInterfaceCallable callee) {
|
||||
(*this)->setAttr(getCalleeAttrName(), callee.get<mlir::SymbolRefAttr>());
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
def FuncCallProceduralOp : SVFuncCallBase<"func.call.procedural", [ProceduralOp]> {
|
||||
let summary = "Function call in a procedural region";
|
||||
}
|
||||
|
||||
def FuncCallOp : SVFuncCallBase<"func.call", [NonProceduralOp]> {
|
||||
let summary = "Function call in a non-procedural region";
|
||||
let description = [{
|
||||
This op represents a function call in a non-procedural region.
|
||||
A function call in a non-procedural region must have a return
|
||||
value and no output argument.
|
||||
}];
|
||||
}
|
||||
|
||||
def ReturnOp : SVOp<"return",
|
||||
[Pure, ReturnLike, Terminator, HasParent<"FuncOp">]> {
|
||||
let summary = "Function return operation";
|
||||
|
||||
let arguments = (ins Variadic<AnyType>:$operands);
|
||||
|
||||
let hasCustomAssemblyFormat = 1;
|
||||
let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?";
|
||||
let hasVerifier = 1;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// SV output control.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -39,11 +39,12 @@ public:
|
|||
AlwaysCombOp, AlwaysFFOp, InitialOp, CaseOp,
|
||||
// Other Statements.
|
||||
AssignOp, BPAssignOp, PAssignOp, ForceOp, ReleaseOp, AliasOp,
|
||||
FWriteOp, SystemFunctionOp, VerbatimOp,
|
||||
FWriteOp, SystemFunctionOp, VerbatimOp, FuncCallOp,
|
||||
FuncCallProceduralOp, ReturnOp,
|
||||
// Type declarations.
|
||||
InterfaceOp, InterfaceSignalOp, InterfaceModportOp,
|
||||
InterfaceInstanceOp, GetModportOp, AssignInterfaceSignalOp,
|
||||
ReadInterfaceSignalOp, MacroDeclOp, MacroDefOp,
|
||||
ReadInterfaceSignalOp, MacroDeclOp, MacroDefOp, FuncOp,
|
||||
// Verification statements.
|
||||
AssertOp, AssumeOp, CoverOp, AssertConcurrentOp, AssumeConcurrentOp,
|
||||
CoverConcurrentOp,
|
||||
|
@ -127,6 +128,9 @@ public:
|
|||
HANDLE(AliasOp, Unhandled);
|
||||
HANDLE(FWriteOp, Unhandled);
|
||||
HANDLE(SystemFunctionOp, Unhandled);
|
||||
HANDLE(FuncCallProceduralOp, Unhandled);
|
||||
HANDLE(FuncCallOp, Unhandled);
|
||||
HANDLE(ReturnOp, Unhandled);
|
||||
HANDLE(VerbatimOp, Unhandled);
|
||||
|
||||
// Type declarations.
|
||||
|
@ -139,6 +143,7 @@ public:
|
|||
HANDLE(ReadInterfaceSignalOp, Unhandled);
|
||||
HANDLE(MacroDefOp, Unhandled);
|
||||
HANDLE(MacroDeclOp, Unhandled);
|
||||
HANDLE(FuncOp, Unhandled);
|
||||
|
||||
// Verification statements.
|
||||
HANDLE(AssertOp, Unhandled);
|
||||
|
|
|
@ -391,20 +391,21 @@ static const char *directionAsString(ModulePort::Direction dir) {
|
|||
abort();
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
void module_like_impl::printModuleSignatureNew(OpAsmPrinter &p,
|
||||
HWModuleLike op) {
|
||||
hw::HWModuleLike op) {
|
||||
module_like_impl::printModuleSignatureNew(
|
||||
p, op.getModuleBody(), op.getHWModuleType(), op.getAllPortAttrs(),
|
||||
op.getAllPortLocs());
|
||||
}
|
||||
|
||||
Region &body = op.getModuleBody();
|
||||
void module_like_impl::printModuleSignatureNew(OpAsmPrinter &p, Region &body,
|
||||
hw::ModuleType modType,
|
||||
ArrayRef<Attribute> portAttrs,
|
||||
ArrayRef<Location> locAttrs) {
|
||||
bool isExternal = body.empty();
|
||||
SmallString<32> resultNameStr;
|
||||
mlir::OpPrintingFlags flags;
|
||||
unsigned curArg = 0;
|
||||
|
||||
auto modType = op.getHWModuleType();
|
||||
auto portAttrs = op.getAllPortAttrs();
|
||||
auto locAttrs = op.getAllPortLocs();
|
||||
|
||||
p << '(';
|
||||
for (auto [i, port] : llvm::enumerate(modType.getPorts())) {
|
||||
if (i > 0)
|
||||
|
@ -449,3 +450,20 @@ void module_like_impl::printModuleSignatureNew(OpAsmPrinter &p,
|
|||
|
||||
p << ')';
|
||||
}
|
||||
|
||||
/// Get a special name to use when printing the entry block arguments of the
|
||||
/// region contained by an operation in this dialect.
|
||||
void module_like_impl::getAsmBlockArgumentNamesImpl(
|
||||
mlir::Region ®ion, OpAsmSetValueNameFn setNameFn) {
|
||||
if (region.empty())
|
||||
return;
|
||||
// Assign port names to the bbargs.
|
||||
auto module = cast<HWModuleOp>(region.getParentOp());
|
||||
|
||||
auto *block = ®ion.front();
|
||||
for (size_t i = 0, e = block->getNumArguments(); i != e; ++i) {
|
||||
auto name = module.getInputName(i);
|
||||
// Let mlir deterministically convert names to valid identifiers
|
||||
setNameFn(block->getArgument(i), name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,12 +16,14 @@
|
|||
#include "circt/Dialect/HW/HWOps.h"
|
||||
#include "circt/Dialect/HW/HWSymCache.h"
|
||||
#include "circt/Dialect/HW/HWTypes.h"
|
||||
#include "circt/Dialect/HW/ModuleImplementation.h"
|
||||
#include "circt/Dialect/SV/SVAttributes.h"
|
||||
#include "circt/Support/CustomDirectiveImpl.h"
|
||||
#include "mlir/IR/Builders.h"
|
||||
#include "mlir/IR/BuiltinTypes.h"
|
||||
#include "mlir/IR/Matchers.h"
|
||||
#include "mlir/IR/PatternMatch.h"
|
||||
#include "mlir/Interfaces/FunctionImplementation.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/ADT/TypeSwitch.h"
|
||||
|
@ -2106,6 +2108,313 @@ ModportStructAttr ModportStructAttr::get(MLIRContext *context,
|
|||
return get(context, ModportDirectionAttr::get(context, direction), signal);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// FuncOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
ParseResult FuncOp::parse(OpAsmParser &parser, OperationState &result) {
|
||||
auto builder = parser.getBuilder();
|
||||
// Parse visibility.
|
||||
(void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
|
||||
|
||||
// Parse the name as a symbol.
|
||||
StringAttr nameAttr;
|
||||
if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(),
|
||||
result.attributes))
|
||||
return failure();
|
||||
|
||||
SmallVector<hw::module_like_impl::PortParse> ports;
|
||||
TypeAttr modType;
|
||||
if (failed(
|
||||
hw::module_like_impl::parseModuleSignature(parser, ports, modType)))
|
||||
return failure();
|
||||
|
||||
result.addAttribute(FuncOp::getModuleTypeAttrName(result.name), modType);
|
||||
|
||||
// Convert the specified array of dictionary attrs (which may have null
|
||||
// entries) to an ArrayAttr of dictionaries.
|
||||
auto unknownLoc = builder.getUnknownLoc();
|
||||
SmallVector<Attribute> attrs, inputLocs, outputLocs;
|
||||
auto nonEmptyLocsFn = [unknownLoc](Attribute attr) {
|
||||
return attr && cast<Location>(attr) != unknownLoc;
|
||||
};
|
||||
|
||||
for (auto &port : ports) {
|
||||
attrs.push_back(port.attrs ? port.attrs : builder.getDictionaryAttr({}));
|
||||
auto loc = port.sourceLoc ? Location(*port.sourceLoc) : unknownLoc;
|
||||
(port.direction == hw::PortInfo::Direction::Output ? outputLocs : inputLocs)
|
||||
.push_back(loc);
|
||||
}
|
||||
|
||||
result.addAttribute(FuncOp::getPerArgumentAttrsAttrName(result.name),
|
||||
builder.getArrayAttr(attrs));
|
||||
|
||||
if (llvm::any_of(outputLocs, nonEmptyLocsFn))
|
||||
result.addAttribute(FuncOp::getResultLocsAttrName(result.name),
|
||||
builder.getArrayAttr(outputLocs));
|
||||
// Parse the attribute dict.
|
||||
if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
|
||||
return failure();
|
||||
|
||||
// Add the entry block arguments.
|
||||
SmallVector<OpAsmParser::Argument, 4> entryArgs;
|
||||
for (auto &port : ports)
|
||||
if (port.direction != hw::ModulePort::Direction::Output)
|
||||
entryArgs.push_back(port);
|
||||
|
||||
// Parse the optional function body. The printer will not print the body if
|
||||
// its empty, so disallow parsing of empty body in the parser.
|
||||
auto *body = result.addRegion();
|
||||
llvm::SMLoc loc = parser.getCurrentLocation();
|
||||
|
||||
mlir::OptionalParseResult parseResult =
|
||||
parser.parseOptionalRegion(*body, entryArgs,
|
||||
/*enableNameShadowing=*/false);
|
||||
if (parseResult.has_value()) {
|
||||
if (failed(*parseResult))
|
||||
return failure();
|
||||
// Function body was parsed, make sure its not empty.
|
||||
if (body->empty())
|
||||
return parser.emitError(loc, "expected non-empty function body");
|
||||
} else {
|
||||
if (llvm::any_of(inputLocs, nonEmptyLocsFn))
|
||||
result.addAttribute(FuncOp::getInputLocsAttrName(result.name),
|
||||
builder.getArrayAttr(inputLocs));
|
||||
}
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
void FuncOp::getAsmBlockArgumentNames(mlir::Region ®ion,
|
||||
mlir::OpAsmSetValueNameFn setNameFn) {
|
||||
if (region.empty())
|
||||
return;
|
||||
// Assign port names to the bbargs.
|
||||
auto func = cast<FuncOp>(region.getParentOp());
|
||||
|
||||
auto *block = ®ion.front();
|
||||
|
||||
auto names = func.getModuleType().getInputNames();
|
||||
for (size_t i = 0, e = block->getNumArguments(); i != e; ++i) {
|
||||
// Let mlir deterministically convert names to valid identifiers
|
||||
setNameFn(block->getArgument(i), cast<StringAttr>(names[i]));
|
||||
}
|
||||
}
|
||||
|
||||
Type FuncOp::getExplicitlyReturnedType() {
|
||||
if (!getPerArgumentAttrs() || getNumOutputs() == 0)
|
||||
return {};
|
||||
|
||||
// Check if the last port is used as an explicit return.
|
||||
auto lastArgument = getModuleType().getPorts().back();
|
||||
auto lastArgumentAttr = dyn_cast<DictionaryAttr>(
|
||||
getPerArgumentAttrsAttr()[getPerArgumentAttrsAttr().size() - 1]);
|
||||
|
||||
if (lastArgument.dir == hw::ModulePort::Output && lastArgumentAttr &&
|
||||
lastArgumentAttr.getAs<UnitAttr>(getExplicitlyReturnedAttrName()))
|
||||
return lastArgument.type;
|
||||
return {};
|
||||
}
|
||||
|
||||
ArrayRef<Attribute> FuncOp::getAllPortAttrs() {
|
||||
if (getPerArgumentAttrs())
|
||||
return getPerArgumentAttrs()->getValue();
|
||||
return {};
|
||||
}
|
||||
|
||||
void FuncOp::setAllPortAttrs(ArrayRef<Attribute> attrs) {
|
||||
setPerArgumentAttrsAttr(ArrayAttr::get(getContext(), attrs));
|
||||
}
|
||||
|
||||
void FuncOp::removeAllPortAttrs() { setPerArgumentAttrsAttr({}); }
|
||||
SmallVector<Location> FuncOp::getAllPortLocs() {
|
||||
SmallVector<Location> portLocs;
|
||||
portLocs.reserve(getNumPorts());
|
||||
auto resultLocs = getResultLocsAttr();
|
||||
unsigned inputCount = 0;
|
||||
auto modType = getModuleType();
|
||||
auto unknownLoc = UnknownLoc::get(getContext());
|
||||
auto *body = getBodyBlock();
|
||||
auto inputLocs = getInputLocsAttr();
|
||||
for (unsigned i = 0, e = getNumPorts(); i < e; ++i) {
|
||||
if (modType.isOutput(i)) {
|
||||
auto loc = resultLocs
|
||||
? cast<Location>(
|
||||
resultLocs.getValue()[portLocs.size() - inputCount])
|
||||
: unknownLoc;
|
||||
portLocs.push_back(loc);
|
||||
} else {
|
||||
auto loc = body ? body->getArgument(inputCount).getLoc()
|
||||
: (inputLocs ? cast<Location>(inputLocs[inputCount])
|
||||
: unknownLoc);
|
||||
portLocs.push_back(loc);
|
||||
++inputCount;
|
||||
}
|
||||
}
|
||||
return portLocs;
|
||||
}
|
||||
|
||||
void FuncOp::setAllPortLocsAttrs(llvm::ArrayRef<mlir::Attribute> locs) {
|
||||
SmallVector<Attribute> resultLocs, inputLocs;
|
||||
unsigned inputCount = 0;
|
||||
auto modType = getModuleType();
|
||||
auto *body = getBodyBlock();
|
||||
for (unsigned i = 0, e = getNumPorts(); i < e; ++i) {
|
||||
if (modType.isOutput(i))
|
||||
resultLocs.push_back(locs[i]);
|
||||
else if (body)
|
||||
body->getArgument(inputCount++).setLoc(cast<Location>(locs[i]));
|
||||
else // Need to store locations in an attribute if declaration.
|
||||
inputLocs.push_back(locs[i]);
|
||||
}
|
||||
setResultLocsAttr(ArrayAttr::get(getContext(), resultLocs));
|
||||
if (!body)
|
||||
setInputLocsAttr(ArrayAttr::get(getContext(), inputLocs));
|
||||
}
|
||||
|
||||
SmallVector<hw::PortInfo> FuncOp::getPortList() { return getPortList(false); }
|
||||
|
||||
hw::PortInfo FuncOp::getPort(size_t idx) {
|
||||
auto modTy = getHWModuleType();
|
||||
auto emptyDict = DictionaryAttr::get(getContext());
|
||||
LocationAttr loc = getPortLoc(idx);
|
||||
DictionaryAttr attrs = dyn_cast_or_null<DictionaryAttr>(getPortAttrs(idx));
|
||||
if (!attrs)
|
||||
attrs = emptyDict;
|
||||
return {modTy.getPorts()[idx],
|
||||
modTy.isOutput(idx) ? modTy.getOutputIdForPortId(idx)
|
||||
: modTy.getInputIdForPortId(idx),
|
||||
attrs, loc};
|
||||
}
|
||||
|
||||
SmallVector<hw::PortInfo> FuncOp::getPortList(bool excludeExplicitReturn) {
|
||||
auto modTy = getModuleType();
|
||||
auto emptyDict = DictionaryAttr::get(getContext());
|
||||
auto skipLastArgument = getExplicitlyReturnedType() && excludeExplicitReturn;
|
||||
SmallVector<hw::PortInfo> retval;
|
||||
auto portAttr = getAllPortLocs();
|
||||
for (unsigned i = 0, e = skipLastArgument ? modTy.getNumPorts() - 1
|
||||
: modTy.getNumPorts();
|
||||
i < e; ++i) {
|
||||
DictionaryAttr attrs = emptyDict;
|
||||
if (auto perArgumentAttr = getPerArgumentAttrs())
|
||||
if (auto argumentAttr =
|
||||
dyn_cast_or_null<DictionaryAttr>((*perArgumentAttr)[i]))
|
||||
attrs = argumentAttr;
|
||||
|
||||
retval.push_back({modTy.getPorts()[i],
|
||||
modTy.isOutput(i) ? modTy.getOutputIdForPortId(i)
|
||||
: modTy.getInputIdForPortId(i),
|
||||
attrs, portAttr[i]});
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
void FuncOp::print(OpAsmPrinter &p) {
|
||||
FuncOp op = *this;
|
||||
// Print the operation and the function name.
|
||||
auto funcName =
|
||||
op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName())
|
||||
.getValue();
|
||||
p << ' ';
|
||||
|
||||
StringRef visibilityAttrName = SymbolTable::getVisibilityAttrName();
|
||||
if (auto visibility = op->getAttrOfType<StringAttr>(visibilityAttrName))
|
||||
p << visibility.getValue() << ' ';
|
||||
p.printSymbolName(funcName);
|
||||
hw::module_like_impl::printModuleSignatureNew(
|
||||
p, op.getBody(), op.getModuleType(),
|
||||
op.getPerArgumentAttrsAttr()
|
||||
? ArrayRef<Attribute>(op.getPerArgumentAttrsAttr().getValue())
|
||||
: ArrayRef<Attribute>{},
|
||||
getAllPortLocs());
|
||||
|
||||
mlir::function_interface_impl::printFunctionAttributes(
|
||||
p, op,
|
||||
{visibilityAttrName, getModuleTypeAttrName(),
|
||||
getPerArgumentAttrsAttrName(), getInputLocsAttrName(),
|
||||
getResultLocsAttrName()});
|
||||
// Print the body if this is not an external function.
|
||||
Region &body = op->getRegion(0);
|
||||
if (!body.empty()) {
|
||||
p << ' ';
|
||||
p.printRegion(body, /*printEntryBlockArgs=*/false,
|
||||
/*printBlockTerminators=*/true);
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ReturnOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
LogicalResult ReturnOp::verify() {
|
||||
auto func = getParentOp<sv::FuncOp>();
|
||||
auto funcResults = func.getResultTypes();
|
||||
auto returnedValues = getOperands();
|
||||
if (funcResults.size() != returnedValues.size())
|
||||
return emitOpError("must have same number of operands as region results.");
|
||||
// Check that the types of our operands and the region's results match.
|
||||
for (size_t i = 0, e = funcResults.size(); i < e; ++i) {
|
||||
if (funcResults[i] != returnedValues[i].getType()) {
|
||||
emitOpError("output types must match function. In "
|
||||
"operand ")
|
||||
<< i << ", expected " << funcResults[i] << ", but got "
|
||||
<< returnedValues[i].getType() << ".";
|
||||
return failure();
|
||||
}
|
||||
}
|
||||
return success();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Call Ops
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
static Value
|
||||
getExplicitlyReturnedValueImpl(sv::FuncOp op,
|
||||
mlir::Operation::result_range results) {
|
||||
if (!op.getExplicitlyReturnedType())
|
||||
return {};
|
||||
return results.back();
|
||||
}
|
||||
|
||||
Value FuncCallOp::getExplicitlyReturnedValue(sv::FuncOp op) {
|
||||
return getExplicitlyReturnedValueImpl(op, getResults());
|
||||
}
|
||||
|
||||
Value FuncCallProceduralOp::getExplicitlyReturnedValue(sv::FuncOp op) {
|
||||
return getExplicitlyReturnedValueImpl(op, getResults());
|
||||
}
|
||||
|
||||
LogicalResult
|
||||
FuncCallProceduralOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
|
||||
auto referencedOp = dyn_cast_or_null<sv::FuncOp>(
|
||||
symbolTable.lookupNearestSymbolFrom(*this, getCalleeAttr()));
|
||||
if (!referencedOp)
|
||||
return emitError("cannot find function declaration '")
|
||||
<< getCallee() << "'";
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult FuncCallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
|
||||
auto referencedOp = dyn_cast_or_null<sv::FuncOp>(
|
||||
symbolTable.lookupNearestSymbolFrom(*this, getCalleeAttr()));
|
||||
if (!referencedOp)
|
||||
return emitError("cannot find function declaration '")
|
||||
<< getCallee() << "'";
|
||||
|
||||
// Non-procedural call cannot have output arguments.
|
||||
if (referencedOp.getNumOutputs() != 1 ||
|
||||
!referencedOp.getExplicitlyReturnedType()) {
|
||||
auto diag = emitError()
|
||||
<< "function called in a non-procedural region must "
|
||||
"return a single result";
|
||||
diag.attachNote(referencedOp.getLoc()) << "doesn't satisfy the constraint";
|
||||
return failure();
|
||||
}
|
||||
return success();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// TableGen generated logic.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -12,6 +12,14 @@ hw.module.extern @test4(in %input: i7, out output: i7)
|
|||
hw.module.extern @test5(in %input: i7 {hw.arg = "arg"}, out output: i7 {hw.res = "res"})
|
||||
hw.module.extern @test6(in %input: i7 loc("arg"), out output: i7 loc("res"))
|
||||
hw.module.extern @test7(in %input: i7 {hw.arg = "arg"} loc("arg"), out output: i7 {hw.res = "res"} loc("res"))
|
||||
sv.func @test8(in %input: i7, out output: i7) { sv.return %input : i7 }
|
||||
sv.func @test9(in %input: i7 {hw.arg = "arg"}, out output: i7 {hw.res = "res"}) { sv.return %input : i7 }
|
||||
sv.func @test10(in %input: i7 loc("arg"), out output: i7 loc("res")) { sv.return %input : i7 }
|
||||
sv.func @test11(in %input: i7 {hw.arg = "arg"} loc("arg"), out output: i7 {hw.res = "res"} loc("res")) { sv.return %input : i7 }
|
||||
sv.func private @test12(in %input: i7, out output: i7)
|
||||
sv.func private @test13(in %input: i7 {hw.arg = "arg"}, out output: i7 {hw.res = "res"})
|
||||
sv.func private @test14(in %input: i7 loc("arg"), out output: i7 loc("res"))
|
||||
sv.func private @test15(in %input: i7 {hw.arg = "arg"} loc("arg"), out output: i7 {hw.res = "res"} loc("res"))
|
||||
|
||||
// BASIC: hw.module @test0(in %input : i7, out output : i7)
|
||||
// BASIC: hw.module @test1(in %input : i7 {hw.arg = "arg"}, out output : i7 {hw.res = "res"})
|
||||
|
@ -21,6 +29,14 @@ hw.module.extern @test7(in %input: i7 {hw.arg = "arg"} loc("arg"), out output: i
|
|||
// BASIC: hw.module.extern @test5(in %input : i7 {hw.arg = "arg"}, out output : i7 {hw.res = "res"})
|
||||
// BASIC: hw.module.extern @test6(in %input : i7, out output : i7)
|
||||
// BASIC: hw.module.extern @test7(in %input : i7 {hw.arg = "arg"}, out output : i7 {hw.res = "res"})
|
||||
// BASIC: sv.func @test8(in %input : i7, out output : i7)
|
||||
// BASIC: sv.func @test9(in %input : i7 {hw.arg = "arg"}, out output : i7 {hw.res = "res"})
|
||||
// BASIC: sv.func @test10(in %input : i7, out output : i7)
|
||||
// BASIC: sv.func @test11(in %input : i7 {hw.arg = "arg"}, out output : i7 {hw.res = "res"})
|
||||
// BASIC: sv.func private @test12(in %input : i7, out output : i7)
|
||||
// BASIC: sv.func private @test13(in %input : i7 {hw.arg = "arg"}, out output : i7 {hw.res = "res"})
|
||||
// BASIC: sv.func private @test14(in %input : i7, out output : i7)
|
||||
// BASIC: sv.func private @test15(in %input : i7 {hw.arg = "arg"}, out output : i7 {hw.res = "res"})
|
||||
|
||||
// DEBUG: hw.module @test0(in %input : i7 loc({{.+}}), out output : i7 loc({{.+}}))
|
||||
// DEBUG: hw.module @test1(in %input : i7 {hw.arg = "arg"} loc({{.+}}), out output : i7 {hw.res = "res"} loc({{.+}}))
|
||||
|
@ -30,3 +46,11 @@ hw.module.extern @test7(in %input: i7 {hw.arg = "arg"} loc("arg"), out output: i
|
|||
// DEBUG: hw.module.extern @test5(in %input : i7 {hw.arg = "arg"} loc({{.+}}), out output : i7 {hw.res = "res"} loc({{.+}}))
|
||||
// DEBUG: hw.module.extern @test6(in %input : i7 loc("arg"), out output : i7 loc("res"))
|
||||
// DEBUG: hw.module.extern @test7(in %input : i7 {hw.arg = "arg"} loc("arg"), out output : i7 {hw.res = "res"} loc("res"))
|
||||
// DEBUG: sv.func @test8(in %input : i7 loc({{.+}}), out output : i7 loc({{.+}}))
|
||||
// DEBUG: sv.func @test9(in %input : i7 {hw.arg = "arg"} loc({{.+}}), out output : i7 {hw.res = "res"} loc({{.+}}))
|
||||
// DEBUG: sv.func @test10(in %input : i7 loc("arg"), out output : i7 loc("res"))
|
||||
// DEBUG: sv.func @test11(in %input : i7 {hw.arg = "arg"} loc("arg"), out output : i7 {hw.res = "res"} loc("res"))
|
||||
// DEBUG: sv.func private @test12(in %input : i7 loc({{.+}}), out output : i7 loc({{.+}}))
|
||||
// DEBUG: sv.func private @test13(in %input : i7 {hw.arg = "arg"} loc({{.+}}), out output : i7 {hw.res = "res"} loc({{.+}}))
|
||||
// DEBUG: sv.func private @test14(in %input : i7 loc("arg"), out output : i7 loc("res"))
|
||||
// DEBUG: sv.func private @test15(in %input : i7 {hw.arg = "arg"} loc("arg"), out output : i7 {hw.res = "res"} loc("res"))
|
||||
|
|
|
@ -389,3 +389,22 @@ hw.module @XMRRefOp() {
|
|||
// CHECK: %1 = sv.xmr.ref @ref2 ".x.y.z[42]" : !hw.inout<i8>
|
||||
%1 = sv.xmr.ref @ref2 ".x.y.z[42]" : !hw.inout<i8>
|
||||
}
|
||||
|
||||
// Functions.
|
||||
// CHECK-LABEL: sv.func private @function_declare(in %in_0 : i2, in %in_1 : i2, out out_0 : i1, in %in_2 : !hw.array<2xi2>)
|
||||
sv.func private @function_declare(in %in_0 : i2, in %in_1 : i2, out out_0 : i1, in %in_2 : !hw.array<2xi2>)
|
||||
|
||||
// CHECK-LABEL: sv.func private @function_define(in %in_0 : i2, in %in_1 : i2, out out_0 : i1, in %in_2 : !hw.array<2xi2>)
|
||||
sv.func private @function_define(in %in_0 : i2, in %in_1 : i2, out out_0 : i1, in %in_2 : !hw.array<2xi2>) attributes {test = "foo"} {
|
||||
%0 = comb.icmp eq %in_0, %in_1: i2
|
||||
// CHECK: sv.return %{{.+}} : i1
|
||||
sv.return %0 : i1
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sv.func @recurse(in %n : i32, out out : i32) {
|
||||
// CHECK: %0 = sv.func.call.procedural @recurse(%n) : (i32) -> i32
|
||||
// CHECK-NEXT: sv.return %0
|
||||
sv.func @recurse(in %n : i32, out out : i32) {
|
||||
%v = sv.func.call.procedural @recurse(%n) : (i32) -> i32
|
||||
sv.return %v : i32
|
||||
}
|
||||
|
|
|
@ -238,3 +238,37 @@ hw.module @NoMessage(in %clock: i1, in %value : i4) {
|
|||
"sv.assert"(%clock, %value) { defer = 0 : i32 } : (i1, i4) -> ()
|
||||
}
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
sv.func private @function() {
|
||||
%0 = hw.constant true
|
||||
// expected-error @below {{'sv.return' op must have same number of operands as region results}}
|
||||
sv.return %0 : i1
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
sv.func private @function(out out: i2) {
|
||||
%0 = hw.constant true
|
||||
// expected-error @below {{'sv.return' op output types must match function. In operand 0, expected 'i2', but got 'i1'}}
|
||||
sv.return %0 : i1
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
hw.module private @module(out out: i2) {
|
||||
%0 = hw.constant true
|
||||
// expected-error @below {{'sv.return' op expects parent op 'sv.func'}}
|
||||
sv.return %0 : i1
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
// expected-note @below {{doesn't satisfy the constraint}}
|
||||
sv.func private @func(out out: i1)
|
||||
hw.module private @call(){
|
||||
// expected-error @below {{function called in a non-procedural region must return a single result}}
|
||||
%0 = sv.func.call @func() : () -> (i1)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue