CIRCT 24.0.0git
Loading...
Searching...
No Matches
SystemCOps.cpp
Go to the documentation of this file.
1//===- SystemCOps.cpp - Implement the SystemC operations ------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements the SystemC ops.
10//
11//===----------------------------------------------------------------------===//
12
18#include "mlir/IR/IRMapping.h"
19#include "mlir/IR/PatternMatch.h"
20#include "mlir/Interfaces/FunctionImplementation.h"
21#include "llvm/ADT/MapVector.h"
22#include "llvm/ADT/TypeSwitch.h"
23
24using namespace circt;
25using namespace circt::systemc;
26
27//===----------------------------------------------------------------------===//
28// Helpers
29//===----------------------------------------------------------------------===//
30
31static LogicalResult verifyUniqueNamesInRegion(
32 Operation *operation, ArrayAttr argNames,
33 std::function<void(mlir::InFlightDiagnostic &)> attachNote) {
34 DenseMap<StringRef, BlockArgument> portNames;
35 DenseMap<StringRef, Operation *> memberNames;
36 DenseMap<StringRef, Operation *> localNames;
37
38 if (operation->getNumRegions() != 1)
39 return operation->emitError("required to have exactly one region");
40
41 bool portsVerified = true;
42
43 for (auto arg : llvm::zip(argNames, operation->getRegion(0).getArguments())) {
44 StringRef argName = cast<StringAttr>(std::get<0>(arg)).getValue();
45 BlockArgument argValue = std::get<1>(arg);
46
47 if (portNames.count(argName)) {
48 auto diag = mlir::emitError(argValue.getLoc(), "redefines name '")
49 << argName << "'";
50 diag.attachNote(portNames[argName].getLoc())
51 << "'" << argName << "' first defined here";
52 attachNote(diag);
53 portsVerified = false;
54 continue;
55 }
56
57 portNames.insert({argName, argValue});
58 }
59
60 WalkResult result =
61 operation->walk<mlir::WalkOrder::PreOrder>([&](Operation *op) {
62 if (isa<SCModuleOp>(op->getParentOp()))
63 localNames.clear();
64
65 if (auto nameDeclOp = dyn_cast<SystemCNameDeclOpInterface>(op)) {
66 StringRef name = nameDeclOp.getName();
67
68 auto reportNameRedefinition = [&](Location firstLoc) -> WalkResult {
69 auto diag = mlir::emitError(op->getLoc(), "redefines name '")
70 << name << "'";
71 diag.attachNote(firstLoc) << "'" << name << "' first defined here";
72 attachNote(diag);
73 return WalkResult::interrupt();
74 };
75
76 if (portNames.count(name))
77 return reportNameRedefinition(portNames[name].getLoc());
78 if (memberNames.count(name))
79 return reportNameRedefinition(memberNames[name]->getLoc());
80 if (localNames.count(name))
81 return reportNameRedefinition(localNames[name]->getLoc());
82
83 if (isa<SCModuleOp>(op->getParentOp()))
84 memberNames.insert({name, op});
85 else
86 localNames.insert({name, op});
87 }
88
89 return WalkResult::advance();
90 });
91
92 if (result.wasInterrupted() || !portsVerified)
93 return failure();
94
95 return success();
96}
97
98//===----------------------------------------------------------------------===//
99// SCModuleOp
100//===----------------------------------------------------------------------===//
101
103 return TypeSwitch<Type, hw::ModulePort::Direction>(type)
104 .Case<InOutType>([](auto ty) { return hw::ModulePort::Direction::InOut; })
105 .Case<InputType>([](auto ty) { return hw::ModulePort::Direction::Input; })
106 .Case<OutputType>(
107 [](auto ty) { return hw::ModulePort::Direction::Output; });
108}
109
110SCModuleOp::PortDirectionRange
111SCModuleOp::getPortsOfDirection(hw::ModulePort::Direction direction) {
112 std::function<bool(const BlockArgument &)> predicateFn =
113 [&](const BlockArgument &arg) -> bool {
114 return getDirection(arg.getType()) == direction;
115 };
116 return llvm::make_filter_range(getArguments(), predicateFn);
117}
118
119SmallVector<::circt::hw::PortInfo> SCModuleOp::getPortList() {
120 SmallVector<hw::PortInfo> ports;
121 size_t inputIdx = 0, outputIdx = 0;
122 for (int i = 0, e = getNumArguments(); i < e; ++i) {
124 auto argType = getArgument(i).getType();
125 info.name = cast<StringAttr>(getPortNames()[i]);
126 info.type = getSignalBaseType(argType);
127 info.dir = getDirection(argType);
128 info.argNum = info.dir == hw::ModulePort::Direction::Output ? outputIdx++
129 : inputIdx++;
130 ports.push_back(info);
131 }
132 return ports;
133}
134
135mlir::Region *SCModuleOp::getCallableRegion() { return &getBody(); }
136
137StringRef SCModuleOp::getModuleName() { return getSymName(); }
138
139ParseResult SCModuleOp::parse(OpAsmParser &parser, OperationState &result) {
140
141 // Parse the visibility attribute.
142 (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
143
144 // Parse the name as a symbol.
145 StringAttr moduleName;
146 if (parser.parseSymbolName(moduleName,
147 SCModuleOp::getSymNameAttrName(result.name),
148 result.attributes))
149 return failure();
150
151 // Parse the function signature.
152 bool isVariadic = false;
153 SmallVector<OpAsmParser::Argument, 4> entryArgs;
154 SmallVector<Attribute> argNames;
155 SmallVector<Attribute> argLocs;
156 SmallVector<Attribute> resultNames;
157 SmallVector<DictionaryAttr> resultAttrs;
158 SmallVector<Attribute> resultLocs;
159 TypeAttr functionType;
161 parser, isVariadic, entryArgs, argNames, argLocs, resultNames,
162 resultAttrs, resultLocs, functionType)))
163 return failure();
164
165 // Parse the attribute dict.
166 if (parser.parseOptionalAttrDictWithKeyword(result.attributes))
167 return failure();
168
169 result.addAttribute("portNames",
170 ArrayAttr::get(parser.getContext(), argNames));
171
172 result.addAttribute(SCModuleOp::getFunctionTypeAttrName(result.name),
173 functionType);
174
175 mlir::call_interface_impl::addArgAndResultAttrs(
176 parser.getBuilder(), result, entryArgs, resultAttrs,
177 SCModuleOp::getArgAttrsAttrName(result.name),
178 SCModuleOp::getResAttrsAttrName(result.name));
179
180 auto &body = *result.addRegion();
181 if (parser.parseRegion(body, entryArgs))
182 return failure();
183 if (body.empty())
184 body.push_back(std::make_unique<Block>().release());
185
186 return success();
187}
188
189void SCModuleOp::print(OpAsmPrinter &p) {
190 p << ' ';
191
192 // Print the visibility of the module.
193 StringRef visibilityAttrName =
194 mlir::SymbolOpInterface::getDefaultVisibilityAttrName();
195 if (auto visibility =
196 getOperation()->getAttrOfType<StringAttr>(visibilityAttrName))
197 p << visibility.getValue() << ' ';
198
199 p.printSymbolName(SymbolTable::getSymbolName(*this).getValue());
200 p << ' ';
201
202 bool needArgNamesAttr = false;
204 p, *this, getFunctionType().getInputs(), false, {}, needArgNamesAttr);
205 mlir::function_interface_impl::printFunctionAttributes(
206 p, *this,
207 {"portNames", getFunctionTypeAttrName(), getArgAttrsAttrName(),
208 getResAttrsAttrName()});
209
210 p << ' ';
211 p.printRegion(getBody(), false, false);
212}
213
214/// Returns the argument types of this function.
215ArrayRef<Type> SCModuleOp::getArgumentTypes() {
216 return getFunctionType().getInputs();
217}
218
219/// Returns the result types of this function.
220ArrayRef<Type> SCModuleOp::getResultTypes() {
221 return getFunctionType().getResults();
222}
223
224static Type wrapPortType(Type type, hw::ModulePort::Direction direction) {
225 if (auto inoutTy = dyn_cast<hw::InOutType>(type))
226 type = inoutTy.getElementType();
227
228 switch (direction) {
229 case hw::ModulePort::Direction::InOut:
230 return InOutType::get(type);
231 case hw::ModulePort::Direction::Input:
232 return InputType::get(type);
233 case hw::ModulePort::Direction::Output:
234 return OutputType::get(type);
235 }
236 llvm_unreachable("Impossible port direction");
237}
238
239void SCModuleOp::build(OpBuilder &odsBuilder, OperationState &odsState,
240 StringAttr name, ArrayAttr portNames,
241 ArrayRef<Type> portTypes,
242 ArrayRef<NamedAttribute> attributes) {
243 odsState.addAttribute(getPortNamesAttrName(odsState.name), portNames);
244 Region *region = odsState.addRegion();
245
246 auto moduleType = odsBuilder.getFunctionType(portTypes, {});
247 odsState.addAttribute(getFunctionTypeAttrName(odsState.name),
248 TypeAttr::get(moduleType));
249
250 odsState.addAttribute(SCModuleOp::getSymNameAttrName(odsState.name), name);
251 region->push_back(new Block);
252 region->addArguments(
253 portTypes,
254 SmallVector<Location>(portTypes.size(), odsBuilder.getUnknownLoc()));
255 odsState.addAttributes(attributes);
256}
257
258void SCModuleOp::build(OpBuilder &odsBuilder, OperationState &odsState,
259 StringAttr name, ArrayRef<hw::PortInfo> ports,
260 ArrayRef<NamedAttribute> attributes) {
261 MLIRContext *ctxt = odsBuilder.getContext();
262 SmallVector<Attribute> portNames;
263 SmallVector<Type> portTypes;
264 for (auto port : ports) {
265 portNames.push_back(StringAttr::get(ctxt, port.getName()));
266 portTypes.push_back(wrapPortType(port.type, port.dir));
267 }
268 build(odsBuilder, odsState, name, ArrayAttr::get(ctxt, portNames), portTypes);
269}
270
271void SCModuleOp::build(OpBuilder &odsBuilder, OperationState &odsState,
272 StringAttr name, const hw::ModulePortInfo &ports,
273 ArrayRef<NamedAttribute> attributes) {
274 MLIRContext *ctxt = odsBuilder.getContext();
275 SmallVector<Attribute> portNames;
276 SmallVector<Type> portTypes;
277 for (auto port : ports) {
278 portNames.push_back(StringAttr::get(ctxt, port.getName()));
279 portTypes.push_back(wrapPortType(port.type, port.dir));
280 }
281 build(odsBuilder, odsState, name, ArrayAttr::get(ctxt, portNames), portTypes);
282}
283
284void SCModuleOp::getAsmBlockArgumentNames(mlir::Region &region,
285 mlir::OpAsmSetValueNameFn setNameFn) {
286 if (region.empty())
287 return;
288
289 ArrayAttr portNames = getPortNames();
290 for (size_t i = 0, e = getNumArguments(); i != e; ++i) {
291 auto str = cast<StringAttr>(portNames[i]).getValue();
292 setNameFn(getArgument(i), str);
293 }
294}
295
296LogicalResult SCModuleOp::verify() {
297 if (getFunctionType().getNumResults() != 0)
298 return emitOpError(
299 "incorrect number of function results (always has to be 0)");
300 if (getPortNames().size() != getFunctionType().getNumInputs())
301 return emitOpError("incorrect number of port names");
302
303 for (auto arg : getArguments()) {
304 if (!hw::type_isa<InputType, OutputType, InOutType>(arg.getType()))
305 return mlir::emitError(
306 arg.getLoc(),
307 "module port must be of type 'sc_in', 'sc_out', or 'sc_inout'");
308 }
309
310 for (auto portName : getPortNames()) {
311 if (cast<StringAttr>(portName).getValue().empty())
312 return emitOpError("port name must not be empty");
313 }
314
315 return success();
316}
317
318LogicalResult SCModuleOp::verifyRegions() {
319 auto attachNote = [&](mlir::InFlightDiagnostic &diag) {
320 diag.attachNote(getLoc()) << "in module '@" << getModuleName() << "'";
321 };
322 return verifyUniqueNamesInRegion(getOperation(), getPortNames(), attachNote);
323}
324
325CtorOp SCModuleOp::getOrCreateCtor(OpBuilder &builder) {
326 CtorOp ctor;
327 getBody().walk([&](Operation *op) {
328 if ((ctor = dyn_cast<CtorOp>(op)))
329 return WalkResult::interrupt();
330
331 return WalkResult::skip();
332 });
333
334 if (ctor)
335 return ctor;
336
337 OpBuilder::InsertionGuard guard(builder);
338 builder.setInsertionPoint(getBodyBlock(), getBodyBlock()->begin());
339 return CtorOp::create(builder, getLoc());
340}
341
342DestructorOp SCModuleOp::getOrCreateDestructor() {
343 DestructorOp destructor;
344 getBody().walk([&](Operation *op) {
345 if ((destructor = dyn_cast<DestructorOp>(op)))
346 return WalkResult::interrupt();
347
348 return WalkResult::skip();
349 });
350
351 if (destructor)
352 return destructor;
353
354 auto builder = OpBuilder::atBlockEnd(getBodyBlock());
355 return DestructorOp::create(builder, getLoc());
356}
357
358//===----------------------------------------------------------------------===//
359// SignalOp
360//===----------------------------------------------------------------------===//
361
362void SignalOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
363 setNameFn(getSignal(), getName());
364}
365
366//===----------------------------------------------------------------------===//
367// ConvertOp
368//===----------------------------------------------------------------------===//
369
370OpFoldResult ConvertOp::fold(FoldAdaptor) {
371 if (getInput().getType() == getResult().getType())
372 return getInput();
373
374 if (auto other = getInput().getDefiningOp<ConvertOp>()) {
375 Type inputType = other.getInput().getType();
376 Type intermediateType = getInput().getType();
377
378 if (inputType != getResult().getType())
379 return {};
380
381 // Either both the input and intermediate types are signed or both are
382 // unsigned.
383 bool inputSigned = isa<SignedType, IntBaseType>(inputType);
384 bool intermediateSigned = isa<SignedType, IntBaseType>(intermediateType);
385 if (inputSigned ^ intermediateSigned)
386 return {};
387
388 // Converting 4-valued to 2-valued and back may lose information.
389 if (isa<LogicVectorBaseType, LogicType>(inputType) &&
390 !isa<LogicVectorBaseType, LogicType>(intermediateType))
391 return {};
392
393 auto inputBw = getBitWidth(inputType);
394 auto intermediateBw = getBitWidth(intermediateType);
395
396 if (!inputBw && intermediateBw) {
397 if (isa<IntBaseType, UIntBaseType>(inputType) && *intermediateBw >= 64)
398 return other.getInput();
399 // We cannot support input types of signed, unsigned, and vector types
400 // since they have no upper bound for the bit-width.
401 }
402
403 if (!intermediateBw) {
404 if (isa<BitVectorBaseType, LogicVectorBaseType>(intermediateType))
405 return other.getInput();
406
407 if (!inputBw && isa<IntBaseType, UIntBaseType>(inputType) &&
408 isa<SignedType, UnsignedType>(intermediateType))
409 return other.getInput();
410
411 if (inputBw && *inputBw <= 64 &&
412 isa<IntBaseType, UIntBaseType, SignedType, UnsignedType>(
413 intermediateType))
414 return other.getInput();
415
416 // We have to be careful with the signed and unsigned types as they often
417 // have a max bit-width defined (that can be customized) and thus folding
418 // here could change the behavior.
419 }
420
421 if (inputBw && intermediateBw && *inputBw <= *intermediateBw)
422 return other.getInput();
423 }
424
425 return {};
426}
427
428//===----------------------------------------------------------------------===//
429// CtorOp
430//===----------------------------------------------------------------------===//
431
432LogicalResult CtorOp::verify() {
433 if (getBody().getNumArguments() != 0)
434 return emitOpError("must not have any arguments");
435
436 return success();
437}
438
439//===----------------------------------------------------------------------===//
440// SCFuncOp
441//===----------------------------------------------------------------------===//
442
443void SCFuncOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
444 setNameFn(getHandle(), getName());
445}
446
447LogicalResult SCFuncOp::verify() {
448 if (getBody().getNumArguments() != 0)
449 return emitOpError("must not have any arguments");
450
451 return success();
452}
453
454//===----------------------------------------------------------------------===//
455// InstanceDeclOp
456//===----------------------------------------------------------------------===//
457
458void InstanceDeclOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
459 setNameFn(getInstanceHandle(), getName());
460}
461
462StringRef InstanceDeclOp::getInstanceName() { return getName(); }
463StringAttr InstanceDeclOp::getInstanceNameAttr() { return getNameAttr(); }
464
465Operation *
466InstanceDeclOp::getReferencedModuleCached(const hw::HWSymbolCache *cache) {
467 if (cache)
468 if (auto *result = cache->getDefinition(getModuleNameAttr()))
469 return result;
470
471 auto topLevelModuleOp = (*this)->getParentOfType<ModuleOp>();
472 return topLevelModuleOp.lookupSymbol(getModuleName());
473}
474
475LogicalResult
476InstanceDeclOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
477 auto *module =
478 symbolTable.lookupNearestSymbolFrom(*this, getModuleNameAttr());
479 if (module == nullptr)
480 return emitError("cannot find module definition '")
481 << getModuleName() << "'";
482
483 auto emitError = [&](const std::function<void(InFlightDiagnostic & diag)> &fn)
484 -> LogicalResult {
485 auto diag = emitOpError();
486 fn(diag);
487 diag.attachNote(module->getLoc()) << "module declared here";
488 return failure();
489 };
490
491 // It must be a systemc module.
492 if (!isa<SCModuleOp>(module))
493 return emitError([&](auto &diag) {
494 diag << "symbol reference '" << getModuleName()
495 << "' isn't a systemc module";
496 });
497
498 auto scModule = cast<SCModuleOp>(module);
499
500 // Check that the module name of the symbol and instance type match.
501 if (scModule.getModuleName() != getInstanceType().getModuleName())
502 return emitError([&](auto &diag) {
503 diag << "module names must match; expected '" << scModule.getModuleName()
504 << "' but got '" << getInstanceType().getModuleName().getValue()
505 << "'";
506 });
507
508 // Check that port types and names are consistent with the referenced module.
509 ArrayRef<ModuleType::PortInfo> ports = getInstanceType().getPorts();
510 ArrayAttr modArgNames = scModule.getPortNames();
511 auto numPorts = ports.size();
512 auto expectedPortTypes = scModule.getArgumentTypes();
513
514 if (expectedPortTypes.size() != numPorts)
515 return emitError([&](auto &diag) {
516 diag << "has a wrong number of ports; expected "
517 << expectedPortTypes.size() << " but got " << numPorts;
518 });
519
520 for (size_t i = 0; i != numPorts; ++i) {
521 if (ports[i].type != expectedPortTypes[i]) {
522 return emitError([&](auto &diag) {
523 diag << "port type #" << i << " must be " << expectedPortTypes[i]
524 << ", but got " << ports[i].type;
525 });
526 }
527
528 if (ports[i].name != modArgNames[i])
529 return emitError([&](auto &diag) {
530 diag << "port name #" << i << " must be " << modArgNames[i]
531 << ", but got " << ports[i].name;
532 });
533 }
534
535 return success();
536}
537
538SmallVector<hw::PortInfo> InstanceDeclOp::getPortList() {
539 return cast<hw::PortList>(SymbolTable::lookupNearestSymbolFrom(
540 getOperation(), getReferencedModuleNameAttr()))
541 .getPortList();
542}
543
544//===----------------------------------------------------------------------===//
545// DestructorOp
546//===----------------------------------------------------------------------===//
547
548LogicalResult DestructorOp::verify() {
549 if (getBody().getNumArguments() != 0)
550 return emitOpError("must not have any arguments");
551
552 return success();
553}
554
555//===----------------------------------------------------------------------===//
556// BindPortOp
557//===----------------------------------------------------------------------===//
558
559ParseResult BindPortOp::parse(OpAsmParser &parser, OperationState &result) {
560 OpAsmParser::UnresolvedOperand instance, channel;
561 std::string portName;
562 if (parser.parseOperand(instance) || parser.parseLSquare() ||
563 parser.parseString(&portName))
564 return failure();
565
566 auto portNameLoc = parser.getCurrentLocation();
567
568 if (parser.parseRSquare() || parser.parseKeyword("to") ||
569 parser.parseOperand(channel))
570 return failure();
571
572 if (parser.parseOptionalAttrDict(result.attributes))
573 return failure();
574
575 auto typeListLoc = parser.getCurrentLocation();
576 SmallVector<Type> types;
577 if (parser.parseColonTypeList(types))
578 return failure();
579
580 if (types.size() != 2)
581 return parser.emitError(typeListLoc,
582 "expected a list of exactly 2 types, but got ")
583 << types.size();
584
585 if (parser.resolveOperand(instance, types[0], result.operands))
586 return failure();
587 if (parser.resolveOperand(channel, types[1], result.operands))
588 return failure();
589
590 if (auto moduleType = dyn_cast<ModuleType>(types[0])) {
591 auto ports = moduleType.getPorts();
592 uint64_t index = 0;
593 for (auto port : ports) {
594 if (port.name == portName)
595 break;
596 index++;
597 }
598 if (index >= ports.size())
599 return parser.emitError(portNameLoc, "port name \"")
600 << portName << "\" not found in module";
601
602 result.addAttribute("portId", parser.getBuilder().getIndexAttr(index));
603
604 return success();
605 }
606
607 return failure();
608}
609
610void BindPortOp::print(OpAsmPrinter &p) {
611 p << " " << getInstance() << "["
612 << cast<ModuleType>(getInstance().getType())
613 .getPorts()[getPortId().getZExtValue()]
614 .name
615 << "] to " << getChannel();
616 p.printOptionalAttrDict((*this)->getAttrs(), {"portId"});
617 p << " : " << getInstance().getType() << ", " << getChannel().getType();
618}
619
620LogicalResult BindPortOp::verify() {
621 auto ports = cast<ModuleType>(getInstance().getType()).getPorts();
622 if (getPortId().getZExtValue() >= ports.size())
623 return emitOpError("port #")
624 << getPortId().getZExtValue() << " does not exist, there are only "
625 << ports.size() << " ports";
626
627 // Verify that the base types match.
628 Type portType = ports[getPortId().getZExtValue()].type;
629 Type channelType = getChannel().getType();
630 if (getSignalBaseType(portType) != getSignalBaseType(channelType))
631 return emitOpError() << portType << " port cannot be bound to "
632 << channelType << " channel due to base type mismatch";
633
634 // Verify that the port/channel directions are valid.
635 if ((isa<InputType>(portType) && isa<OutputType>(channelType)) ||
636 (isa<OutputType>(portType) && isa<InputType>(channelType)))
637 return emitOpError() << portType << " port cannot be bound to "
638 << channelType
639 << " channel due to port direction mismatch";
640
641 return success();
642}
643
644StringRef BindPortOp::getPortName() {
645 return cast<ModuleType>(getInstance().getType())
646 .getPorts()[getPortId().getZExtValue()]
647 .name.getValue();
648}
649
650//===----------------------------------------------------------------------===//
651// SensitiveOp
652//===----------------------------------------------------------------------===//
653
654LogicalResult SensitiveOp::canonicalize(SensitiveOp op,
655 PatternRewriter &rewriter) {
656 if (op.getSensitivities().empty()) {
657 rewriter.eraseOp(op);
658 return success();
659 }
660
661 return failure();
662}
663
664//===----------------------------------------------------------------------===//
665// VariableOp
666//===----------------------------------------------------------------------===//
667
668void VariableOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
669 setNameFn(getVariable(), getName());
670}
671
672ParseResult VariableOp::parse(OpAsmParser &parser, OperationState &result) {
673 StringAttr nameAttr;
674 if (parseImplicitSSAName(parser, nameAttr))
675 return failure();
676 result.addAttribute("name", nameAttr);
677
678 OpAsmParser::UnresolvedOperand init;
679 auto initResult = parser.parseOptionalOperand(init);
680
681 if (parser.parseOptionalAttrDict(result.attributes))
682 return failure();
683
684 Type variableType;
685 if (parser.parseColonType(variableType))
686 return failure();
687
688 if (initResult.has_value()) {
689 if (parser.resolveOperand(init, variableType, result.operands))
690 return failure();
691 }
692 result.addTypes({variableType});
693
694 return success();
695}
696
697void VariableOp::print(::mlir::OpAsmPrinter &p) {
698 p << " ";
699
700 if (getInit())
701 p << getInit() << " ";
702
703 p.printOptionalAttrDict(getOperation()->getAttrs(), {"name"});
704 p << ": " << getVariable().getType();
705}
706
707LogicalResult VariableOp::verify() {
708 if (getInit() && getInit().getType() != getVariable().getType())
709 return emitOpError(
710 "'init' and 'variable' must have the same type, but got ")
711 << getInit().getType() << " and " << getVariable().getType();
712
713 return success();
714}
715
716//===----------------------------------------------------------------------===//
717// InteropVerilatedOp
718//===----------------------------------------------------------------------===//
719
720/// Create a instance that refers to a known module.
721void InteropVerilatedOp::build(OpBuilder &odsBuilder, OperationState &odsState,
722 Operation *module, StringAttr name,
723 ArrayRef<Value> inputs) {
724 auto mod = cast<hw::HWModuleLike>(module);
725 auto argNames = odsBuilder.getArrayAttr(mod.getInputNames());
726 auto resultNames = odsBuilder.getArrayAttr(mod.getOutputNames());
727 build(odsBuilder, odsState, mod.getHWModuleType().getOutputTypes(), name,
728 FlatSymbolRefAttr::get(SymbolTable::getSymbolName(module)), argNames,
729 resultNames, inputs);
730}
731
732LogicalResult
733InteropVerilatedOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
735 *this, getModuleNameAttr(), getInputs(), getResultTypes(),
736 getInputNames(), getResultNames(), ArrayAttr(), symbolTable);
737}
738
739/// Suggest a name for each result value based on the saved result names
740/// attribute.
741void InteropVerilatedOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
743 getResultNames(), getResults());
744}
745
746//===----------------------------------------------------------------------===//
747// CallOp
748//
749// TODO: The implementation for this operation was copy-pasted from the
750// 'func' dialect. Ideally, this upstream dialect refactored such that we can
751// re-use the implementation here.
752//===----------------------------------------------------------------------===//
753
754// FIXME: This is an exact copy from upstream
755LogicalResult CallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
756 // Check that the callee attribute was specified.
757 auto fnAttr = (*this)->getAttrOfType<FlatSymbolRefAttr>("callee");
758 if (!fnAttr)
759 return emitOpError("requires a 'callee' symbol reference attribute");
760 FuncOp fn = symbolTable.lookupNearestSymbolFrom<FuncOp>(*this, fnAttr);
761 if (!fn)
762 return emitOpError() << "'" << fnAttr.getValue()
763 << "' does not reference a valid function";
764
765 // Verify that the operand and result types match the callee.
766 auto fnType = fn.getFunctionType();
767 if (fnType.getNumInputs() != getNumOperands())
768 return emitOpError("incorrect number of operands for callee");
769
770 for (unsigned i = 0, e = fnType.getNumInputs(); i != e; ++i)
771 if (getOperand(i).getType() != fnType.getInput(i))
772 return emitOpError("operand type mismatch: expected operand type ")
773 << fnType.getInput(i) << ", but provided "
774 << getOperand(i).getType() << " for operand number " << i;
775
776 if (fnType.getNumResults() != getNumResults())
777 return emitOpError("incorrect number of results for callee");
778
779 for (unsigned i = 0, e = fnType.getNumResults(); i != e; ++i)
780 if (getResult(i).getType() != fnType.getResult(i)) {
781 auto diag = emitOpError("result type mismatch at index ") << i;
782 diag.attachNote() << " op result types: " << getResultTypes();
783 diag.attachNote() << "function result types: " << fnType.getResults();
784 return diag;
785 }
786
787 return success();
788}
789
790FunctionType CallOp::getCalleeType() {
791 return FunctionType::get(getContext(), getOperandTypes(), getResultTypes());
792}
793
794// This verifier was added compared to the upstream implementation.
795LogicalResult CallOp::verify() {
796 if (getNumResults() > 1)
797 return emitOpError(
798 "incorrect number of function results (always has to be 0 or 1)");
799
800 return success();
801}
802
803//===----------------------------------------------------------------------===//
804// CallIndirectOp
805//===----------------------------------------------------------------------===//
806
807// This verifier was added compared to the upstream implementation.
808LogicalResult CallIndirectOp::verify() {
809 if (getNumResults() > 1)
810 return emitOpError(
811 "incorrect number of function results (always has to be 0 or 1)");
812
813 return success();
814}
815
816//===----------------------------------------------------------------------===//
817// FuncOp
818//
819// TODO: Most of the implementation for this operation was copy-pasted from the
820// 'func' dialect. Ideally, this upstream dialect refactored such that we can
821// re-use the implementation here.
822//===----------------------------------------------------------------------===//
823
824// Note that the create and build operations are taken from upstream, but the
825// argNames argument was added.
826FuncOp FuncOp::create(Location location, StringRef name, ArrayAttr argNames,
827 FunctionType type, ArrayRef<NamedAttribute> attrs) {
828 OpBuilder builder(location->getContext());
829 OperationState state(location, getOperationName());
830 FuncOp::build(builder, state, name, argNames, type, attrs);
831 return cast<FuncOp>(Operation::create(state));
832}
833
834FuncOp FuncOp::create(Location location, StringRef name, ArrayAttr argNames,
835 FunctionType type, Operation::dialect_attr_range attrs) {
836 SmallVector<NamedAttribute, 8> attrRef(attrs);
837 return create(location, name, argNames, type, ArrayRef(attrRef));
838}
839
840FuncOp FuncOp::create(Location location, StringRef name, ArrayAttr argNames,
841 FunctionType type, ArrayRef<NamedAttribute> attrs,
842 ArrayRef<DictionaryAttr> argAttrs) {
843 FuncOp func = create(location, name, argNames, type, attrs);
844 func.setAllArgAttrs(argAttrs);
845 return func;
846}
847
848void FuncOp::build(OpBuilder &odsBuilder, OperationState &odsState,
849 StringRef name, ArrayAttr argNames, FunctionType type,
850 ArrayRef<NamedAttribute> attrs,
851 ArrayRef<DictionaryAttr> argAttrs) {
852 odsState.addAttribute(getArgNamesAttrName(odsState.name), argNames);
853 odsState.addAttribute(FuncOp::getSymNameAttrName(odsState.name),
854 odsBuilder.getStringAttr(name));
855 odsState.addAttribute(FuncOp::getFunctionTypeAttrName(odsState.name),
856 TypeAttr::get(type));
857 odsState.attributes.append(attrs.begin(), attrs.end());
858 odsState.addRegion();
859
860 if (argAttrs.empty())
861 return;
862 assert(type.getNumInputs() == argAttrs.size());
863 mlir::call_interface_impl::addArgAndResultAttrs(
864 odsBuilder, odsState, argAttrs,
865 /*resultAttrs=*/{}, FuncOp::getArgAttrsAttrName(odsState.name),
866 FuncOp::getResAttrsAttrName(odsState.name));
867}
868
869ParseResult FuncOp::parse(OpAsmParser &parser, OperationState &result) {
870 auto buildFuncType =
871 [](Builder &builder, ArrayRef<Type> argTypes, ArrayRef<Type> results,
872 mlir::function_interface_impl::VariadicFlag,
873 std::string &) { return builder.getFunctionType(argTypes, results); };
874
875 // This was added specifically for our implementation, upstream does not have
876 // this feature.
877 if (succeeded(parser.parseOptionalKeyword("externC")))
878 result.addAttribute(getExternCAttrName(result.name),
879 UnitAttr::get(result.getContext()));
880
881 // FIXME: below is an exact copy of the
882 // mlir::function_interface_impl::parseFunctionOp implementation, this was
883 // needed because we need to access the SSA names of the arguments.
884 SmallVector<OpAsmParser::Argument> entryArgs;
885 SmallVector<DictionaryAttr> resultAttrs;
886 SmallVector<Type> resultTypes;
887 auto &builder = parser.getBuilder();
888
889 // Parse visibility.
890 (void)mlir::impl::parseOptionalVisibilityKeyword(parser, result.attributes);
891
892 // Parse the name as a symbol.
893 StringAttr nameAttr;
894 if (parser.parseSymbolName(nameAttr, FuncOp::getSymNameAttrName(result.name),
895 result.attributes))
896 return failure();
897
898 // Parse the function signature.
899 mlir::SMLoc signatureLocation = parser.getCurrentLocation();
900 bool isVariadic = false;
901 if (mlir::function_interface_impl::parseFunctionSignatureWithArguments(
902 parser, false, entryArgs, isVariadic, resultTypes, resultAttrs))
903 return failure();
904
905 std::string errorMessage;
906 SmallVector<Type> argTypes;
907 argTypes.reserve(entryArgs.size());
908 for (auto &arg : entryArgs)
909 argTypes.push_back(arg.type);
910
911 Type type = buildFuncType(
912 builder, argTypes, resultTypes,
913 mlir::function_interface_impl::VariadicFlag(isVariadic), errorMessage);
914 if (!type) {
915 return parser.emitError(signatureLocation)
916 << "failed to construct function type"
917 << (errorMessage.empty() ? "" : ": ") << errorMessage;
918 }
919 result.addAttribute(FuncOp::getFunctionTypeAttrName(result.name),
920 TypeAttr::get(type));
921
922 // If function attributes are present, parse them.
923 NamedAttrList parsedAttributes;
924 mlir::SMLoc attributeDictLocation = parser.getCurrentLocation();
925 if (parser.parseOptionalAttrDictWithKeyword(parsedAttributes))
926 return failure();
927
928 // Disallow attributes that are inferred from elsewhere in the attribute
929 // dictionary.
930 for (StringRef disallowed :
931 {mlir::SymbolOpInterface::getDefaultVisibilityAttrName(),
932 FuncOp::getSymNameAttrName(result.name).getValue(),
933 FuncOp::getFunctionTypeAttrName(result.name).getValue()}) {
934 if (parsedAttributes.get(disallowed))
935 return parser.emitError(attributeDictLocation, "'")
936 << disallowed
937 << "' is an inferred attribute and should not be specified in the "
938 "explicit attribute dictionary";
939 }
940 result.attributes.append(parsedAttributes);
941
942 // Add the attributes to the function arguments.
943 assert(resultAttrs.size() == resultTypes.size());
944 mlir::call_interface_impl::addArgAndResultAttrs(
945 builder, result, entryArgs, resultAttrs,
946 FuncOp::getArgAttrsAttrName(result.name),
947 FuncOp::getResAttrsAttrName(result.name));
948
949 // Parse the optional function body. The printer will not print the body if
950 // its empty, so disallow parsing of empty body in the parser.
951 auto *body = result.addRegion();
952 mlir::SMLoc loc = parser.getCurrentLocation();
953 mlir::OptionalParseResult parseResult =
954 parser.parseOptionalRegion(*body, entryArgs,
955 /*enableNameShadowing=*/false);
956 if (parseResult.has_value()) {
957 if (failed(*parseResult))
958 return failure();
959 // Function body was parsed, make sure its not empty.
960 if (body->empty())
961 return parser.emitError(loc, "expected non-empty function body");
962 }
963
964 // Everythink below is added compared to the upstream implemenation to handle
965 // argument names.
966 SmallVector<Attribute> argNames;
967 if (!entryArgs.empty() && !entryArgs.front().ssaName.name.empty()) {
968 for (auto &arg : entryArgs)
969 argNames.push_back(
970 StringAttr::get(parser.getContext(), arg.ssaName.name.drop_front()));
971 }
972
973 result.addAttribute(getArgNamesAttrName(result.name),
974 ArrayAttr::get(parser.getContext(), argNames));
975
976 return success();
977}
978
979void FuncOp::print(OpAsmPrinter &p) {
980 if (getExternC())
981 p << " externC";
982
983 mlir::FunctionOpInterface op = *this;
984
985 // FIXME: inlined mlir::function_interface_impl::printFunctionOp because we
986 // need to elide more attributes
987
988 // Print the operation and the function name.
989 auto funcName = cast<mlir::SymbolOpInterface>(op.getOperation()).getName();
990 p << ' ';
991
992 StringRef visibilityAttrName =
993 mlir::SymbolOpInterface::getDefaultVisibilityAttrName();
994 if (auto visibility = op->getAttrOfType<StringAttr>(visibilityAttrName))
995 p << visibility.getValue() << ' ';
996 p.printSymbolName(funcName);
997
998 ArrayRef<Type> argTypes = op.getArgumentTypes();
999 ArrayRef<Type> resultTypes = op.getResultTypes();
1000 mlir::function_interface_impl::printFunctionSignature(p, op, argTypes, false,
1001 resultTypes);
1002 mlir::function_interface_impl::printFunctionAttributes(
1003 p, op,
1004 {visibilityAttrName, "externC", "argNames", getFunctionTypeAttrName(),
1005 getArgAttrsAttrName(), getResAttrsAttrName()});
1006 // Print the body if this is not an external function.
1007 Region &body = op->getRegion(0);
1008 if (!body.empty()) {
1009 p << ' ';
1010 p.printRegion(body, /*printEntryBlockArgs=*/false,
1011 /*printBlockTerminators=*/true);
1012 }
1013}
1014
1015// FIXME: the below clone operation are exact copies from upstream.
1016
1017/// Clone the internal blocks from this function into dest and all attributes
1018/// from this function to dest.
1019void FuncOp::cloneInto(FuncOp dest, IRMapping &mapper) {
1020 // Add the attributes of this function to dest.
1022 for (const auto &attr : dest->getAttrs())
1023 newAttrMap.insert({attr.getName(), attr.getValue()});
1024 for (const auto &attr : (*this)->getAttrs())
1025 newAttrMap.insert({attr.getName(), attr.getValue()});
1026
1027 auto newAttrs = llvm::to_vector(llvm::map_range(
1028 newAttrMap, [](std::pair<StringAttr, Attribute> attrPair) {
1029 return NamedAttribute(attrPair.first, attrPair.second);
1030 }));
1031 dest->setAttrs(DictionaryAttr::get(getContext(), newAttrs));
1032
1033 // Clone the body.
1034 getBody().cloneInto(&dest.getBody(), mapper);
1035}
1036
1037/// Create a deep copy of this function and all of its blocks, remapping
1038/// any operands that use values outside of the function using the map that is
1039/// provided (leaving them alone if no entry is present). Replaces references
1040/// to cloned sub-values with the corresponding value that is copied, and adds
1041/// those mappings to the mapper.
1042FuncOp FuncOp::clone(IRMapping &mapper) {
1043 // Create the new function.
1044 FuncOp newFunc = cast<FuncOp>(getOperation()->cloneWithoutRegions());
1045
1046 // If the function has a body, then the user might be deleting arguments to
1047 // the function by specifying them in the mapper. If so, we don't add the
1048 // argument to the input type vector.
1049 if (!isExternal()) {
1050 FunctionType oldType = getFunctionType();
1051
1052 unsigned oldNumArgs = oldType.getNumInputs();
1053 SmallVector<Type, 4> newInputs;
1054 newInputs.reserve(oldNumArgs);
1055 for (unsigned i = 0; i != oldNumArgs; ++i)
1056 if (!mapper.contains(getArgument(i)))
1057 newInputs.push_back(oldType.getInput(i));
1058
1059 /// If any of the arguments were dropped, update the type and drop any
1060 /// necessary argument attributes.
1061 if (newInputs.size() != oldNumArgs) {
1062 newFunc.setType(FunctionType::get(oldType.getContext(), newInputs,
1063 oldType.getResults()));
1064
1065 if (ArrayAttr argAttrs = getAllArgAttrs()) {
1066 SmallVector<Attribute> newArgAttrs;
1067 newArgAttrs.reserve(newInputs.size());
1068 for (unsigned i = 0; i != oldNumArgs; ++i)
1069 if (!mapper.contains(getArgument(i)))
1070 newArgAttrs.push_back(argAttrs[i]);
1071 newFunc.setAllArgAttrs(newArgAttrs);
1072 }
1073 }
1074 }
1075
1076 /// Clone the current function into the new one and return it.
1077 cloneInto(newFunc, mapper);
1078 return newFunc;
1079}
1080
1081FuncOp FuncOp::clone() {
1082 IRMapping mapper;
1083 return clone(mapper);
1084}
1085
1086// The following functions are entirely new additions compared to upstream.
1087
1088void FuncOp::getAsmBlockArgumentNames(mlir::Region &region,
1089 mlir::OpAsmSetValueNameFn setNameFn) {
1090 if (region.empty())
1091 return;
1092
1093 for (auto [arg, name] : llvm::zip(getArguments(), getArgNames()))
1094 setNameFn(arg, cast<StringAttr>(name).getValue());
1095}
1096
1097LogicalResult FuncOp::verify() {
1098 if (getFunctionType().getNumResults() > 1)
1099 return emitOpError(
1100 "incorrect number of function results (always has to be 0 or 1)");
1101
1102 if (getBody().empty())
1103 return success();
1104
1105 if (getArgNames().size() != getFunctionType().getNumInputs())
1106 return emitOpError("incorrect number of argument names");
1107
1108 for (auto portName : getArgNames()) {
1109 if (cast<StringAttr>(portName).getValue().empty())
1110 return emitOpError("arg name must not be empty");
1111 }
1112
1113 return success();
1114}
1115
1116LogicalResult FuncOp::verifyRegions() {
1117 auto attachNote = [&](mlir::InFlightDiagnostic &diag) {
1118 diag.attachNote(getLoc()) << "in function '@" << getName() << "'";
1119 };
1120 return verifyUniqueNamesInRegion(getOperation(), getArgNames(), attachNote);
1121}
1122
1123//===----------------------------------------------------------------------===//
1124// ReturnOp
1125//
1126// TODO: The implementation for this operation was copy-pasted from the
1127// 'func' dialect. Ideally, this upstream dialect refactored such that we can
1128// re-use the implementation here.
1129//===----------------------------------------------------------------------===//
1130
1131LogicalResult ReturnOp::verify() {
1132 auto function = cast<FuncOp>((*this)->getParentOp());
1133
1134 // The operand number and types must match the function signature.
1135 const auto &results = function.getFunctionType().getResults();
1136 if (getNumOperands() != results.size())
1137 return emitOpError("has ")
1138 << getNumOperands() << " operands, but enclosing function (@"
1139 << function.getName() << ") returns " << results.size();
1140
1141 for (unsigned i = 0, e = results.size(); i != e; ++i)
1142 if (getOperand(i).getType() != results[i])
1143 return emitError() << "type of return operand " << i << " ("
1144 << getOperand(i).getType()
1145 << ") doesn't match function result type ("
1146 << results[i] << ")"
1147 << " in function @" << function.getName();
1148
1149 return success();
1150}
1151
1152//===----------------------------------------------------------------------===//
1153// TableGen generated logic.
1154//===----------------------------------------------------------------------===//
1155
1156// Provide the autogenerated implementation guts for the Op classes.
1157#define GET_OP_CLASSES
1158#include "circt/Dialect/SystemC/SystemC.cpp.inc"
assert(baseType &&"element must be base type")
static Location getLoc(DefSlot slot)
Definition Mem2Reg.cpp:222
static Block * getBodyBlock(FModuleLike mod)
static InstancePath empty
static hw::ModulePort::Direction getDirection(Type type)
static Type wrapPortType(Type type, hw::ModulePort::Direction direction)
static LogicalResult verifyUniqueNamesInRegion(Operation *operation, ArrayAttr argNames, std::function< void(mlir::InFlightDiagnostic &)> attachNote)
This stores lookup tables to make manipulating and working with the IR more efficient.
Definition HWSymCache.h:28
mlir::Operation * getDefinition(mlir::Attribute attr) const override
Lookup a definition for 'symbol' in the cache.
Definition HWSymCache.h:57
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:56
std::string getInstanceName(mlir::func::CallOp callOp)
A helper function to get the instance name.
LogicalResult verifyInstanceOfHWModule(Operation *instance, FlatSymbolRefAttr moduleRef, OperandRange inputs, TypeRange results, ArrayAttr argNames, ArrayAttr resultNames, ArrayAttr parameters, SymbolTableCollection &symbolTable)
Combines verifyReferencedModule, verifyInputs, verifyOutputs, and verifyParameters.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
void getAsmResultNames(OpAsmSetValueNameFn setNameFn, StringRef instanceName, ArrayAttr resultNames, ValueRange results)
Suggest a name for each result value based on the saved result names attribute.
void printModuleSignature(OpAsmPrinter &p, Operation *op, ArrayRef< Type > argTypes, bool isVariadic, ArrayRef< Type > resultTypes, bool &needArgNamesAttr)
Print a module signature with named results.
ParseResult parseModuleFunctionSignature(OpAsmParser &parser, bool &isVariadic, SmallVectorImpl< OpAsmParser::Argument > &args, SmallVectorImpl< Attribute > &argNames, SmallVectorImpl< Attribute > &argLocs, SmallVectorImpl< Attribute > &resultNames, SmallVectorImpl< DictionaryAttr > &resultAttrs, SmallVectorImpl< Attribute > &resultLocs, TypeAttr &type)
This is a variant of mlir::parseFunctionSignature that allows names on result arguments.
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
Definition HWTypes.cpp:122
void info(Twine message)
Definition LSPUtils.cpp:20
Type getSignalBaseType(Type type)
Get the type wrapped by a signal or port (in, inout, out) type.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
ParseResult parseImplicitSSAName(OpAsmParser &parser, StringAttr &attr)
Parse an implicit SSA name string attribute.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition LLVM.h:193
This holds a decoded list of input/inout and output ports for a module or instance.
This holds the name, type, direction of a module's ports.