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