18#include "mlir/Analysis/TopologicalSortUtils.h"
19#include "mlir/Dialect/Func/IR/FuncOps.h"
20#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
21#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
22#include "mlir/Dialect/SCF/IR/SCF.h"
23#include "mlir/IR/IRMapping.h"
24#include "mlir/IR/ImplicitLocOpBuilder.h"
25#include "mlir/IR/SymbolTable.h"
26#include "mlir/Interfaces/SideEffectInterfaces.h"
27#include "mlir/Pass/Pass.h"
28#include "llvm/ADT/TypeSwitch.h"
29#include "llvm/Support/Debug.h"
31#define DEBUG_TYPE "arc-lower-state"
35#define GEN_PASS_DEF_LOWERSTATEPASS
36#include "circt/Dialect/Arc/ArcPasses.h.inc"
44using llvm::SmallDenseSet;
47enum class Phase { Initial, Old, New, Final };
53 return os <<
"initial";
82 ModuleLowering &
module;
85 SmallVector<std::pair<Operation *, Phase>, 2> pending;
87 OpLowering(Operation *op, Phase phase, ModuleLowering &module)
88 : op(op), phase(phase), module(module) {}
91 LogicalResult lower();
92 LogicalResult lowerDefault();
93 LogicalResult lower(StateOp op);
94 LogicalResult lower(sim::DPICallOp op);
96 lowerStateful(Value clock, Value enable, Value reset, ValueRange inputs,
98 llvm::function_ref<ValueRange(ValueRange)> createMapping);
99 LogicalResult lower(MemoryOp op);
100 LogicalResult lower(TapOp op);
101 LogicalResult lower(InstanceOp op);
102 LogicalResult lower(hw::TriggeredOp op);
103 LogicalResult lower(hw::OutputOp op);
104 LogicalResult lower(seq::InitialOp op);
105 LogicalResult lower(llhd::FinalOp op);
106 LogicalResult lower(llhd::CurrentTimeOp op);
107 LogicalResult lower(sim::ClockedTerminateOp op);
109 scf::IfOp createIfClockOp(Value clock);
115 Value lowerValue(Value value, Phase phase);
116 Value lowerValue(InstanceOp op, OpResult result, Phase phase);
117 Value lowerValue(StateOp op, OpResult result, Phase phase);
118 Value lowerValue(sim::DPICallOp op, OpResult result, Phase phase);
119 Value lowerValue(MemoryReadPortOp op, OpResult result, Phase phase);
120 Value lowerValue(seq::InitialOp op, OpResult result, Phase phase);
121 Value lowerValue(seq::FromImmutableOp op, OpResult result, Phase phase);
123 void addPending(Value value, Phase phase);
124 void addPending(Operation *op, Phase phase);
128struct ModuleLowering {
134 OpBuilder allocBuilder;
136 OpBuilder initialBuilder;
138 OpBuilder finalBuilder;
144 SmallVector<OpLowering> opsWorklist;
146 SmallDenseSet<std::pair<Operation *, Phase>> opsSeen;
148 DenseSet<std::pair<Operation *, Phase>> loweredOps;
150 DenseMap<std::pair<Value, Phase>, Value> loweredValues;
153 SmallVector<Value> allocatedInputs;
156 DenseMap<Value, Value> allocatedStates;
158 DenseMap<OpOperand *, Value> allocatedOutputs;
160 DenseMap<Value, Value> allocatedInitials;
162 DenseMap<Operation *, Value> allocatedTaps;
166 DenseMap<Value, Value> loweredPosedges;
169 std::pair<Value, Value> prevEnable;
172 std::pair<Value, Value> prevReset;
175 : moduleOp(moduleOp), builder(moduleOp), allocBuilder(moduleOp),
176 initialBuilder(moduleOp), finalBuilder(moduleOp) {}
178 LogicalResult lowerOp(Operation *op);
179 Value getAllocatedState(OpResult result);
180 Value detectPosedge(Value clock);
181 OpBuilder &getBuilder(Phase phase);
182 Value requireLoweredValue(Value value, Phase phase, Location useLoc);
190LogicalResult ModuleLowering::run() {
191 LLVM_DEBUG(llvm::dbgs() <<
"Lowering module `" << moduleOp.getModuleName()
196 ModelOp::create(builder, moduleOp.getLoc(), moduleOp.getModuleNameAttr(),
197 TypeAttr::get(moduleOp.getModuleType()),
198 FlatSymbolRefAttr{}, FlatSymbolRefAttr{}, ArrayAttr{});
199 auto &modelBlock = modelOp.getBody().emplaceBlock();
200 storageArg = modelBlock.addArgument(
201 StorageType::get(builder.getContext(), {}), modelOp.getLoc());
202 builder.setInsertionPointToStart(&modelBlock);
208 builder.getI64Type(), -1);
209 SetNextWakeupOp::create(builder, moduleOp.getLoc(), storageArg, noWakeup);
213 auto initialOp = InitialOp::create(builder, moduleOp.getLoc());
214 initialBuilder.setInsertionPointToStart(&initialOp.getBody().emplaceBlock());
217 auto finalOp = FinalOp::create(builder, moduleOp.getLoc());
218 finalBuilder.setInsertionPointToStart(&finalOp.getBody().emplaceBlock());
222 allocBuilder.setInsertionPoint(initialOp);
225 for (
auto arg : moduleOp.
getBodyBlock()->getArguments()) {
226 auto name = moduleOp.getArgName(arg.getArgNumber());
228 RootInputOp::create(allocBuilder, arg.getLoc(),
229 StateType::get(arg.getType()), name, storageArg);
230 allocatedInputs.push_back(state);
234 for (
auto &op : moduleOp.getOps()) {
235 if (mlir::isMemoryEffectFree(&op) &&
236 !isa<hw::OutputOp, sim::ClockedTerminateOp>(op))
238 if (isa<MemoryReadPortOp, MemoryWritePortOp>(op))
240 if (failed(lowerOp(&op)))
246 for (
auto &op :
llvm::make_early_inc_range(
llvm::reverse(modelBlock)))
247 if (
mlir::isOpTriviallyDead(&op))
254LogicalResult ModuleLowering::lowerOp(Operation *op) {
255 LLVM_DEBUG(llvm::dbgs() <<
"- Handling " << *op <<
"\n");
258 SmallVector<Phase, 2> phases = {Phase::New};
259 if (isa<seq::InitialOp>(op))
260 phases = {Phase::Initial};
261 if (isa<llhd::FinalOp>(op))
262 phases = {Phase::Final};
263 if (isa<StateOp>(op))
264 phases = {Phase::Initial, Phase::New};
266 for (
auto phase : phases) {
267 if (loweredOps.contains({op, phase}))
269 opsWorklist.push_back(OpLowering(op, phase, *
this));
270 opsSeen.insert({op, phase});
273 auto dumpWorklist = [&] {
274 for (
auto &opLowering :
llvm::reverse(opsWorklist))
275 opLowering.op->emitRemark()
276 <<
"computing " << opLowering.phase <<
" phase here";
279 while (!opsWorklist.empty()) {
280 auto &opLowering = opsWorklist.back();
283 if (opLowering.initial) {
284 if (failed(opLowering.lower())) {
288 std::reverse(opLowering.pending.begin(), opLowering.pending.end());
289 opLowering.initial =
false;
293 if (!opLowering.pending.empty()) {
294 auto [defOp, phase] = opLowering.pending.pop_back_val();
295 if (loweredOps.contains({defOp, phase}))
297 if (!opsSeen.insert({defOp, phase}).second) {
298 defOp->emitOpError(
"is on a combinational loop");
302 opsWorklist.push_back(OpLowering(defOp, phase, *
this));
308 LLVM_DEBUG(llvm::dbgs() <<
" - Lowering " << opLowering.phase <<
" "
309 << *opLowering.op <<
"\n");
310 if (failed(opLowering.lower())) {
314 loweredOps.insert({opLowering.op, opLowering.phase});
315 opsSeen.erase({opLowering.op, opLowering.phase});
316 opsWorklist.pop_back();
324Value ModuleLowering::getAllocatedState(OpResult result) {
325 if (
auto alloc = allocatedStates.lookup(result))
329 if (
auto memOp = dyn_cast<MemoryOp>(result.getOwner())) {
331 AllocMemoryOp::create(allocBuilder, memOp.getLoc(), memOp.getType(),
332 storageArg, memOp->getAttrs());
333 allocatedStates.insert({result, alloc});
339 AllocStateOp::create(allocBuilder, result.getLoc(),
340 StateType::get(result.getType()), storageArg);
341 allocatedStates.insert({result, alloc});
346 if (
auto instOp = dyn_cast<InstanceOp>(result.getOwner()))
348 "name", builder.getStringAttr(
349 instOp.getInstanceName() +
"/" +
350 instOp.getOutputName(result.getResultNumber()).getValue()));
355 if (isa<StateOp, sim::DPICallOp>(result.getOwner()))
356 if (
auto names = result.getOwner()->getAttrOfType<ArrayAttr>(
"names"))
357 if (result.getResultNumber() < names.size())
358 alloc->setAttr(
"name", names[result.getResultNumber()]);
365Value ModuleLowering::detectPosedge(Value clock) {
366 auto loc = clock.getLoc();
367 if (isa<seq::ClockType>(clock.getType()))
368 clock = seq::FromClockOp::create(builder, loc, clock);
371 auto oldStorage = AllocStateOp::create(
372 allocBuilder, loc, StateType::get(builder.getI1Type()), storageArg);
376 auto oldClock = StateReadOp::create(builder, loc, oldStorage);
377 StateWriteOp::create(builder, loc, oldStorage, clock);
380 auto edge = comb::XorOp::create(builder, loc, oldClock, clock);
381 return comb::AndOp::create(builder, loc, edge, clock);
385OpBuilder &ModuleLowering::getBuilder(Phase phase) {
388 return initialBuilder;
398Value ModuleLowering::requireLoweredValue(Value value, Phase phase,
400 if (
auto lowered = loweredValues.lookup({value, phase}))
402 auto d = emitError(value.getLoc()) <<
"value has not been lowered";
403 d.attachNote(useLoc) <<
"value used here";
415 if (
auto ip = builder.getInsertionPoint(); ip != builder.getBlock()->begin())
416 if (
auto ifOp = dyn_cast<scf::IfOp>(*std::prev(ip)))
417 if (ifOp.getCondition() == condition)
419 return scf::IfOp::create(builder, condition.getLoc(), condition, withElse);
426LogicalResult OpLowering::lower() {
427 return TypeSwitch<Operation *, LogicalResult>(op)
429 .Case<StateOp, sim::DPICallOp, MemoryOp, TapOp, InstanceOp,
430 hw::TriggeredOp, hw::OutputOp, seq::InitialOp, llhd::FinalOp,
431 llhd::CurrentTimeOp, sim::ClockedTerminateOp>(
432 [&](
auto op) {
return lower(op); })
436 .Case<MemoryWritePortOp, MemoryReadPortOp>([&](
auto op) {
437 assert(
false &&
"ports must be lowered by memory op");
442 .Default([&](
auto) {
return lowerDefault(); });
447LogicalResult OpLowering::lowerDefault() {
450 auto anyFailed =
false;
451 op->walk([&](Operation *nestedOp) {
452 for (
auto operand : nestedOp->getOperands()) {
453 if (op->isAncestor(operand.getParentBlock()->getParentOp()))
455 auto lowered = lowerValue(operand, phase);
458 mapping.map(operand, lowered);
467 auto *clonedOp =
module.getBuilder(phase).clone(*op, mapping);
470 for (
auto [oldResult, newResult] :
471 llvm::zip(op->getResults(), clonedOp->getResults()))
472 module.loweredValues[{oldResult, phase}] = newResult;
481LogicalResult OpLowering::lower(StateOp op) {
483 if (phase == Phase::Initial) {
486 for (
auto initial : op.getInitials())
487 lowerValue(initial, Phase::Initial);
492 if (op.getInitials().empty())
494 for (
auto [initial, result] :
495 llvm::zip(op.getInitials(), op.getResults())) {
496 auto value = lowerValue(initial, Phase::Initial);
499 auto state =
module.getAllocatedState(result);
502 StateWriteOp::create(module.initialBuilder, value.getLoc(), state, value);
507 assert(phase == Phase::New);
511 return op.emitOpError() <<
"must have a clock";
512 if (op.getLatency() > 1)
513 return op.emitOpError(
"latencies > 1 not supported yet");
516 return lowerStateful(op.getClock(), op.getEnable(), op.getReset(),
517 op.getInputs(), op.getResults(), [&](ValueRange inputs) {
518 return CallOp::create(module.builder, op.getLoc(),
519 op.getResultTypes(), op.getArc(),
529LogicalResult OpLowering::lower(sim::DPICallOp op) {
531 if (!op.getClock()) {
533 SmallVector<Value> inputs;
534 for (
auto operand : op.getInputs())
535 inputs.push_back(lowerValue(operand, phase));
538 if (llvm::is_contained(inputs, Value{}))
541 return op.emitOpError() <<
"without clock cannot have an enable";
545 func::CallOp::create(module.getBuilder(phase), op.getLoc(),
546 op.getCalleeAttr(), op.getResultTypes(), inputs);
547 for (
auto [oldResult, newResult] :
548 llvm::zip(op.getResults(), callOp.getResults()))
549 module.loweredValues[{oldResult, phase}] = newResult;
553 assert(phase == Phase::New);
555 return lowerStateful(op.getClock(), op.getEnable(), {},
556 op.getInputs(), op.getResults(), [&](ValueRange inputs) {
557 return func::CallOp::create(
558 module.builder, op.getLoc(),
559 op.getCalleeAttr(), op.getResultTypes(),
569LogicalResult OpLowering::lowerStateful(
570 Value clock, Value enable, Value reset, ValueRange inputs,
572 llvm::function_ref<ValueRange(ValueRange)> createMapping) {
578 lowerValue(clock, Phase::New);
580 lowerValue(enable, Phase::Old);
582 lowerValue(reset, Phase::Old);
583 for (
auto input : inputs)
584 lowerValue(input, Phase::Old);
590 auto ifClockOp = createIfClockOp(clock);
593 OpBuilder::InsertionGuard guard(module.builder);
594 module.builder.setInsertionPoint(ifClockOp.thenYield());
598 SmallVector<Value> states;
599 for (
auto result : results) {
600 auto state =
module.getAllocatedState(result);
603 states.push_back(state);
609 auto &[unloweredReset, loweredReset] =
module.prevReset;
610 if (unloweredReset != reset ||
611 loweredReset.getParentBlock() != module.builder.getBlock()) {
612 unloweredReset = reset;
613 loweredReset = lowerValue(reset, Phase::Old);
621 module.builder.setInsertionPoint(ifResetOp.thenYield());
624 for (
auto state : states) {
625 auto type = cast<StateType>(state.getType()).getType();
627 module.builder, loweredReset.getLoc(),
628 module.builder.getIntegerType(hw::getBitWidth(type)), 0);
629 if (value.getType() != type)
632 StateWriteOp::create(module.builder, loweredReset.getLoc(), state, value);
634 module.builder.setInsertionPoint(ifResetOp.elseYield());
640 auto &[unloweredEnable, loweredEnable] =
module.prevEnable;
641 if (unloweredEnable != enable ||
642 loweredEnable.getParentBlock() != module.builder.getBlock()) {
643 unloweredEnable = enable;
644 loweredEnable = lowerValue(enable, Phase::Old);
651 auto ifEnableOp =
createOrReuseIf(module.builder, loweredEnable,
false);
652 module.builder.setInsertionPoint(ifEnableOp.thenYield());
656 SmallVector<Value> loweredInputs;
657 for (
auto input : inputs) {
658 auto lowered = lowerValue(input, Phase::Old);
661 loweredInputs.push_back(lowered);
665 auto loweredResults = createMapping(loweredInputs);
666 for (
auto [state, value] :
llvm::zip(states, loweredResults))
667 StateWriteOp::create(module.builder, value.
getLoc(), state, value);
672 module.builder.setInsertionPoint(ifClockOp);
673 for (
auto [state, result] :
llvm::zip(states, results)) {
674 auto oldValue = StateReadOp::create(module.builder, result.getLoc(), state);
675 module.loweredValues[{result, Phase::Old}] = oldValue;
684LogicalResult OpLowering::lower(MemoryOp op) {
685 assert(phase == Phase::New);
688 SmallVector<MemoryReadPortOp> reads;
689 SmallVector<MemoryWritePortOp> writes;
691 for (
auto *user : op->getUsers()) {
692 if (
auto read = dyn_cast<MemoryReadPortOp>(user)) {
693 reads.push_back(read);
694 }
else if (
auto write = dyn_cast<MemoryWritePortOp>(user)) {
695 writes.push_back(write);
697 auto d = op.emitOpError()
698 <<
"users must all be memory read or write port ops";
699 d.attachNote(user->getLoc())
700 <<
"but found " << user->getName() <<
" user here";
707 for (
auto read : reads)
708 lowerValue(
read, Phase::Old);
709 for (
auto write : writes) {
710 if (
write.getClock())
711 lowerValue(
write.getClock(), Phase::New);
712 for (
auto input :
write.getInputs())
713 lowerValue(input, Phase::Old);
719 auto state =
module.getAllocatedState(op->getResult(0));
723 for (
auto read : reads) {
724 auto oldValue = lowerValue(read, Phase::Old);
727 module.loweredValues[{read, Phase::Old}] = oldValue;
731 for (
auto write : writes) {
732 if (!
write.getClock())
733 return write.emitOpError() <<
"must have a clock";
734 if (
write.getLatency() > 1)
735 return write.emitOpError(
"latencies > 1 not supported yet");
738 auto ifClockOp = createIfClockOp(
write.getClock());
741 OpBuilder::InsertionGuard guard(module.builder);
742 module.builder.setInsertionPoint(ifClockOp.thenYield());
745 SmallVector<Value> inputs;
746 for (
auto input :
write.getInputs()) {
747 auto lowered = lowerValue(input, Phase::Old);
750 inputs.push_back(lowered);
753 CallOp::create(module.builder,
write.getLoc(),
754 write.getArcResultTypes(),
write.getArc(), inputs);
757 if (
write.getEnable()) {
759 module.builder, callOp.getResult(
write.getEnableIdx()),
false);
760 module.builder.setInsertionPoint(ifEnableOp.thenYield());
765 auto address = callOp.getResult(
write.getAddressIdx());
766 auto data = callOp.getResult(
write.getDataIdx());
767 if (
write.getMask()) {
768 auto mask = callOp.getResult(
write.getMaskIdx(
write.getEnable()));
769 auto maskInv =
module.builder.createOrFold<comb::XorOp>(
770 write.getLoc(), mask,
771 ConstantOp::create(module.builder, write.getLoc(), mask.getType(),
775 MemoryReadOp::create(module.builder,
write.getLoc(), state, address);
776 auto oldMasked = comb::AndOp::create(module.builder,
write.getLoc(),
777 maskInv, oldData,
true);
779 comb::AndOp::create(module.builder,
write.getLoc(), mask, data,
true);
780 data = comb::OrOp::create(module.builder,
write.getLoc(), oldMasked,
785 MemoryWriteOp::create(module.builder,
write.getLoc(), state, address, data);
793LogicalResult OpLowering::lower(TapOp op) {
794 assert(phase == Phase::New);
796 auto value = lowerValue(op.getValue(), phase);
802 auto &state =
module.allocatedTaps[op];
804 auto alloc = AllocStateOp::create(module.allocBuilder, op.getLoc(),
805 StateType::get(value.getType()),
806 module.storageArg,
true);
807 alloc->setAttr(
"names", op.getNamesAttr());
810 StateWriteOp::create(module.builder, op.getLoc(), state, value);
817LogicalResult OpLowering::lower(InstanceOp op) {
818 assert(phase == Phase::New);
821 SmallVector<Value> values;
822 for (
auto operand : op.getOperands())
823 values.push_back(lowerValue(operand, Phase::New));
826 if (llvm::is_contained(values, Value{}))
831 for (
auto [value, name] :
llvm::zip(values, op.getArgNames())) {
832 auto state = AllocStateOp::create(module.allocBuilder, value.getLoc(),
833 StateType::get(value.getType()),
835 state->setAttr(
"name", module.builder.getStringAttr(
836 op.getInstanceName() +
"/" +
837 cast<StringAttr>(name).getValue()));
838 StateWriteOp::create(module.builder, value.getLoc(), state, value);
845 for (
auto result : op.getResults())
846 module.getAllocatedState(result);
852LogicalResult OpLowering::lower(hw::TriggeredOp op) {
853 assert(phase == Phase::New);
855 if (op.getEvent() != hw::EventControl::AtPosEdge) {
857 return op.emitOpError(
"only posedge triggers are supported");
861 lowerValue(op.getTrigger(), Phase::New);
862 SmallVector<Value> inputs;
863 for (
auto input : op.getInputs())
864 inputs.push_back(lowerValue(input, Phase::Old));
867 if (llvm::is_contained(inputs, Value{}))
870 auto ifClockOp = createIfClockOp(op.getTrigger());
874 OpBuilder::InsertionGuard guard(module.builder);
875 module.builder.setInsertionPoint(ifClockOp.thenYield());
878 for (
auto [arg, input] :
llvm::zip(op.
getBodyBlock()->getArguments(), inputs))
879 module.loweredValues[{arg, Phase::New}] = input;
881 OpLowering bodyLowering(&bodyOp, Phase::New, module);
882 bodyLowering.initial =
false;
883 if (failed(bodyLowering.lower()))
892LogicalResult OpLowering::lower(hw::OutputOp op) {
893 assert(phase == Phase::New);
896 SmallVector<Value> values;
897 for (
auto operand : op.getOperands())
898 values.push_back(lowerValue(operand, Phase::New));
901 if (llvm::is_contained(values, Value{}))
905 for (
auto [value, name] :
906 llvm::zip(values, module.moduleOp.getOutputNames())) {
907 auto state = RootOutputOp::create(
908 module.allocBuilder, value.getLoc(), StateType::get(value.getType()),
909 cast<StringAttr>(name), module.storageArg);
910 StateWriteOp::create(module.builder, value.getLoc(), state, value);
916LogicalResult OpLowering::lower(seq::InitialOp op) {
917 assert(phase == Phase::Initial);
920 SmallVector<Value> operands;
921 for (
auto operand : op.getOperands())
922 operands.push_back(lowerValue(operand, Phase::Initial));
925 if (llvm::is_contained(operands, Value{}))
929 for (
auto [arg, operand] :
llvm::zip(op.getBody().getArguments(), operands))
930 module.loweredValues[{arg, Phase::Initial}] = operand;
934 IRMapping bodyMapping;
935 auto *initialBlock =
module.initialBuilder.getBlock();
940 auto result = op.walk([&](llhd::CurrentTimeOp timeOp) {
941 if (failed(lower(timeOp)))
942 return WalkResult::interrupt();
943 auto loweredTime =
module.loweredValues.lookup({timeOp.getResult(), phase});
944 timeOp.replaceAllUsesWith(loweredTime);
946 return WalkResult::advance();
948 if (result.wasInterrupted())
951 for (
auto &bodyOp : op.getOps()) {
952 if (isa<seq::YieldOp>(bodyOp))
956 auto *clonedOp =
module.initialBuilder.clone(bodyOp, bodyMapping);
957 auto result = clonedOp->walk([&](Operation *nestedClonedOp) {
958 for (
auto &operand : nestedClonedOp->getOpOperands()) {
960 if (clonedOp->isAncestor(operand.get().getParentBlock()->getParentOp()))
964 if (
auto *defOp = operand.get().getDefiningOp())
965 if (defOp->getBlock() == initialBlock)
967 auto value =
module.requireLoweredValue(operand.get(), Phase::Initial,
968 nestedClonedOp->getLoc());
970 return WalkResult::interrupt();
973 return WalkResult::advance();
975 if (result.wasInterrupted())
979 for (
auto [result, lowered] :
980 llvm::zip(bodyOp.getResults(), clonedOp->getResults())) {
981 bodyMapping.map(result, lowered);
982 module.loweredValues[{result, Phase::Initial}] = lowered;
987 auto *terminator = op.getBodyBlock()->getTerminator();
988 for (
auto [result, operand] :
989 llvm::zip(op.getResults(), terminator->getOperands())) {
990 auto value =
module.requireLoweredValue(operand, Phase::Initial,
991 terminator->getLoc());
994 module.loweredValues[{result, Phase::Initial}] = value;
1001LogicalResult OpLowering::lower(llhd::FinalOp op) {
1002 assert(phase == Phase::Final);
1005 SmallVector<Value> externalOperands;
1006 op.walk([&](Operation *nestedOp) {
1007 for (
auto value : nestedOp->getOperands())
1008 if (!op->
isAncestor(value.getParentBlock()->getParentOp()))
1009 externalOperands.push_back(value);
1014 for (
auto operand : externalOperands) {
1015 auto lowered = lowerValue(operand, Phase::Final);
1016 if (!initial && !lowered)
1018 mapping.map(operand, lowered);
1026 auto result = op.walk([&](llhd::CurrentTimeOp timeOp) {
1027 if (failed(lower(timeOp)))
1028 return WalkResult::interrupt();
1029 auto loweredTime =
module.loweredValues.lookup({timeOp.getResult(), phase});
1030 timeOp.replaceAllUsesWith(loweredTime);
1032 return WalkResult::advance();
1034 if (result.wasInterrupted())
1039 if (op.getBody().hasOneBlock()) {
1040 for (
auto &bodyOp : op.getBody().front().without_terminator())
1041 module.finalBuilder.clone(bodyOp, mapping);
1047 auto executeOp = scf::ExecuteRegionOp::create(module.finalBuilder,
1048 op.getLoc(), TypeRange{});
1049 module.finalBuilder.cloneRegionBefore(op.getBody(), executeOp.getRegion(),
1050 executeOp.getRegion().begin(), mapping);
1051 executeOp.walk([&](llhd::HaltOp haltOp) {
1052 auto builder = OpBuilder(haltOp);
1053 scf::YieldOp::create(builder, haltOp.getLoc());
1064LogicalResult OpLowering::lower(llhd::CurrentTimeOp op) {
1068 auto loc = op.getLoc();
1072 case Phase::Initial: {
1075 module.initialBuilder, loc, module.initialBuilder.getI64Type(), 0);
1076 time = llhd::IntToTimeOp::create(module.initialBuilder, loc, zeroInt);
1081 case Phase::Final: {
1083 auto &builder =
module.getBuilder(phase);
1084 auto timeInt = CurrentTimeOp::create(builder, loc, module.storageArg);
1085 time = llhd::IntToTimeOp::create(builder, loc, timeInt);
1090 module.loweredValues[{op.getResult(), phase}] = time;
1094LogicalResult OpLowering::lower(sim::ClockedTerminateOp op) {
1095 if (phase != Phase::New)
1101 auto ifClockOp = createIfClockOp(op.getClock());
1105 OpBuilder::InsertionGuard guard(module.builder);
1106 module.builder.setInsertionPoint(ifClockOp.thenYield());
1108 auto loc = op.getLoc();
1109 Value cond = lowerValue(op.getCondition(), phase);
1111 return op.emitOpError(
"Failed to lower condition");
1115 return op.emitOpError(
"Failed to create condition block");
1117 module.builder.setInsertionPoint(ifOp.thenYield());
1119 arc::TerminateOp::create(module.builder, loc, module.storageArg,
1120 op.getSuccessAttr());
1129scf::IfOp OpLowering::createIfClockOp(Value clock) {
1130 auto &posedge =
module.loweredPosedges[clock];
1132 auto loweredClock = lowerValue(clock, Phase::New);
1135 posedge =
module.detectPosedge(loweredClock);
1148Value OpLowering::lowerValue(Value value, Phase phase) {
1150 if (
auto lowered = module.loweredValues.lookup({value, phase}))
1154 if (
auto arg = dyn_cast<BlockArgument>(value)) {
1155 if (arg.getOwner() != module.moduleOp.getBodyBlock()) {
1157 emitError(arg.getLoc()) <<
"block argument has not been lowered";
1162 auto state =
module.allocatedInputs[arg.getArgNumber()];
1163 return StateReadOp::create(module.getBuilder(phase), arg.getLoc(), state);
1168 auto result = cast<OpResult>(value);
1169 auto *op = result.getOwner();
1172 if (
auto instOp = dyn_cast<InstanceOp>(op))
1173 return lowerValue(instOp, result, phase);
1174 if (
auto stateOp = dyn_cast<StateOp>(op))
1175 return lowerValue(stateOp, result, phase);
1176 if (
auto dpiOp = dyn_cast<sim::DPICallOp>(op); dpiOp && dpiOp.getClock())
1177 return lowerValue(dpiOp, result, phase);
1178 if (
auto readOp = dyn_cast<MemoryReadPortOp>(op))
1179 return lowerValue(readOp, result, phase);
1180 if (
auto initialOp = dyn_cast<seq::InitialOp>(op))
1181 return lowerValue(initialOp, result, phase);
1182 if (
auto castOp = dyn_cast<seq::FromImmutableOp>(op))
1183 return lowerValue(castOp, result, phase);
1189 addPending(op, phase);
1192 emitError(result.getLoc()) <<
"value has not been lowered";
1198Value OpLowering::lowerValue(InstanceOp op, OpResult result, Phase phase) {
1201 auto state =
module.getAllocatedState(result);
1202 return StateReadOp::create(module.getBuilder(phase), result.getLoc(), state);
1209Value OpLowering::lowerValue(StateOp op, OpResult result, Phase phase) {
1213 if (phase == Phase::New || phase == Phase::Initial)
1214 addPending(op, phase);
1219 if (phase == Phase::Old)
1220 assert(!module.loweredOps.contains({op, Phase::New}) &&
1221 "need old value but new value already written");
1223 auto state =
module.getAllocatedState(result);
1224 return StateReadOp::create(module.getBuilder(phase), result.getLoc(), state);
1231Value OpLowering::lowerValue(sim::DPICallOp op, OpResult result, Phase phase) {
1235 if (phase == Phase::New || phase == Phase::Initial)
1236 addPending(op, phase);
1241 if (phase == Phase::Old)
1242 assert(!module.loweredOps.contains({op, Phase::New}) &&
1243 "need old value but new value already written");
1245 auto state =
module.getAllocatedState(result);
1246 return StateReadOp::create(module.getBuilder(phase), result.getLoc(), state);
1252Value OpLowering::lowerValue(MemoryReadPortOp op, OpResult result,
1254 auto memOp = op.getMemory().getDefiningOp<MemoryOp>();
1257 op->emitOpError() <<
"memory must be defined locally";
1261 auto address = lowerValue(op.getAddress(), phase);
1264 if (phase == Phase::New)
1265 addPending(memOp.getOperation(), Phase::New);
1271 if (phase == Phase::Old) {
1273 assert(!module.loweredOps.contains({memOp, Phase::New}) &&
1274 "need old memory value but new value already written");
1276 assert(phase == Phase::New);
1279 auto state =
module.getAllocatedState(memOp->getResult(0));
1280 return MemoryReadOp::create(module.getBuilder(phase), result.getLoc(), state,
1287Value OpLowering::lowerValue(seq::InitialOp op, OpResult result, Phase phase) {
1290 addPending(op, Phase::Initial);
1293 auto value =
module.loweredValues.lookup({result, Phase::Initial});
1295 emitError(result.getLoc()) <<
"value has not been lowered";
1301 if (phase == Phase::Initial)
1306 auto &state =
module.allocatedInitials[result];
1308 state = AllocStateOp::create(module.allocBuilder, value.getLoc(),
1309 StateType::get(value.getType()),
1311 OpBuilder::InsertionGuard guard(module.initialBuilder);
1312 module.initialBuilder.setInsertionPointAfterValue(value);
1313 StateWriteOp::create(module.initialBuilder, value.getLoc(), state, value);
1317 return StateReadOp::create(module.getBuilder(phase), state.getLoc(), state);
1321Value OpLowering::lowerValue(seq::FromImmutableOp op, OpResult result,
1323 return lowerValue(op.getInput(), phase);
1327void OpLowering::addPending(Value value, Phase phase) {
1328 auto *defOp = value.getDefiningOp();
1329 assert(defOp &&
"block args should never be marked as a dependency");
1330 addPending(defOp, phase);
1335void OpLowering::addPending(Operation *op, Phase phase) {
1336 auto pair = std::make_pair(op, phase);
1337 if (!module.loweredOps.contains(pair))
1338 if (!llvm::is_contained(pending, pair))
1339 pending.push_back(pair);
1347struct LowerStatePass :
public arc::impl::LowerStatePassBase<LowerStatePass> {
1348 using LowerStatePassBase::LowerStatePassBase;
1349 void runOnOperation()
override;
1353void LowerStatePass::runOnOperation() {
1354 auto op = getOperation();
1355 for (
auto moduleOp :
llvm::make_early_inc_range(op.getOps<
HWModuleOp>())) {
1356 if (failed(ModuleLowering(moduleOp).
run()))
1357 return signalPassFailure();
1361 SymbolTable symbolTable(op);
1362 for (
auto extModuleOp :
1366 auto uses = symbolTable.getSymbolUses(extModuleOp, op);
1367 if (!uses->empty()) {
1368 extModuleOp->emitError(
"Failed to remove external module because it is "
1369 "still referenced/instantiated");
1370 return signalPassFailure();
1372 extModuleOp.erase();
assert(baseType &&"element must be base type")
static bool isAncestor(Block *block, Block *other)
static scf::IfOp createOrReuseIf(OpBuilder &builder, Value condition, bool withElse)
Create a new scf.if operation with the given builder, or reuse a previous scf.if if the builder's ins...
static Location getLoc(DefSlot slot)
static Block * getBodyBlock(FModuleLike mod)
OS & operator<<(OS &os, const InnerSymTarget &target)
Printing InnerSymTarget's.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)