18#include "mlir/Analysis/TopologicalSortUtils.h"
19#include "mlir/Dialect/Arith/IR/Arith.h"
20#include "mlir/Dialect/Func/IR/FuncOps.h"
21#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
22#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
23#include "mlir/Dialect/SCF/IR/SCF.h"
24#include "mlir/IR/IRMapping.h"
25#include "mlir/IR/ImplicitLocOpBuilder.h"
26#include "mlir/IR/SymbolTable.h"
27#include "mlir/Interfaces/SideEffectInterfaces.h"
28#include "mlir/Pass/Pass.h"
29#include "llvm/ADT/TypeSwitch.h"
30#include "llvm/Support/Debug.h"
32#define DEBUG_TYPE "arc-lower-state"
36#define GEN_PASS_DEF_LOWERSTATEPASS
37#include "circt/Dialect/Arc/ArcPasses.h.inc"
45using llvm::SmallDenseSet;
48enum class Phase { Initial, Old, New, Final };
54 return os <<
"initial";
83 ModuleLowering &
module;
86 SmallVector<std::pair<Operation *, Phase>, 2> pending;
88 OpLowering(Operation *op, Phase phase, ModuleLowering &module)
89 : op(op), phase(phase), module(module) {}
92 LogicalResult lower();
93 LogicalResult lowerDefault();
94 LogicalResult lower(StateOp op);
95 LogicalResult lower(sim::DPICallOp op);
97 lowerStateful(Value clock, Value enable, Value reset, ValueRange inputs,
99 llvm::function_ref<ValueRange(ValueRange)> createMapping);
100 LogicalResult lower(MemoryOp op);
101 LogicalResult lower(TapOp op);
102 LogicalResult lower(InstanceOp op);
103 LogicalResult lower(CoroutineInstanceOp op);
104 LogicalResult lower(hw::TriggeredOp op);
105 LogicalResult lower(hw::OutputOp op);
106 LogicalResult lower(seq::InitialOp op);
107 LogicalResult lower(llhd::FinalOp op);
108 LogicalResult lower(llhd::CurrentTimeOp op);
109 LogicalResult lower(sim::ClockedTerminateOp op);
111 scf::IfOp createIfClockOp(Value clock);
117 Value lowerValue(Value value, Phase phase);
118 Value lowerValue(InstanceOp op, OpResult result, Phase phase);
119 Value lowerValue(CoroutineInstanceOp op, OpResult result, Phase phase);
120 Value lowerValue(StateOp op, OpResult result, Phase phase);
121 Value lowerValue(sim::DPICallOp op, OpResult result, Phase phase);
122 Value lowerValue(MemoryReadPortOp op, OpResult result, Phase phase);
123 Value lowerValue(seq::InitialOp op, OpResult result, Phase phase);
124 Value lowerValue(seq::FromImmutableOp op, OpResult result, Phase phase);
126 void addPending(Value value, Phase phase);
127 void addPending(Operation *op, Phase phase);
131struct ModuleLowering {
137 OpBuilder allocBuilder;
139 OpBuilder initialBuilder;
141 OpBuilder finalBuilder;
148 SymbolTable &symbolTable;
151 SmallVector<OpLowering> opsWorklist;
153 SmallDenseSet<std::pair<Operation *, Phase>> opsSeen;
155 DenseSet<std::pair<Operation *, Phase>> loweredOps;
157 DenseMap<std::pair<Value, Phase>, Value> loweredValues;
160 SmallVector<Value> allocatedInputs;
163 DenseMap<Value, Value> allocatedStates;
165 DenseMap<OpOperand *, Value> allocatedOutputs;
167 DenseMap<Value, Value> allocatedInitials;
169 DenseMap<Operation *, Value> allocatedTaps;
173 DenseMap<Value, Value> loweredPosedges;
176 std::pair<Value, Value> prevEnable;
179 std::pair<Value, Value> prevReset;
181 ModuleLowering(
HWModuleOp moduleOp, SymbolTable &symbolTable)
182 : moduleOp(moduleOp), builder(moduleOp), allocBuilder(moduleOp),
183 initialBuilder(moduleOp), finalBuilder(moduleOp),
184 symbolTable(symbolTable) {}
186 LogicalResult lowerOp(Operation *op);
187 Value getAllocatedState(OpResult result);
188 Value detectPosedge(Value clock);
189 OpBuilder &getBuilder(Phase phase);
190 Value requireLoweredValue(Value value, Phase phase, Location useLoc);
198LogicalResult ModuleLowering::run() {
199 LLVM_DEBUG(llvm::dbgs() <<
"Lowering module `" << moduleOp.getModuleName()
204 ModelOp::create(builder, moduleOp.getLoc(), moduleOp.getModuleNameAttr(),
205 TypeAttr::get(moduleOp.getModuleType()), IntegerAttr{},
206 FlatSymbolRefAttr{}, FlatSymbolRefAttr{}, ArrayAttr{});
207 auto &modelBlock = modelOp.getBody().emplaceBlock();
208 storageArg = modelBlock.addArgument(StorageType::get(builder.getContext()),
210 builder.setInsertionPointToStart(&modelBlock);
216 builder.getI64Type(), -1);
217 SetNextWakeupOp::create(builder, moduleOp.getLoc(), storageArg, noWakeup);
221 auto initialOp = InitialOp::create(builder, moduleOp.getLoc());
222 initialBuilder.setInsertionPointToStart(&initialOp.getBody().emplaceBlock());
225 auto finalOp = FinalOp::create(builder, moduleOp.getLoc());
226 finalBuilder.setInsertionPointToStart(&finalOp.getBody().emplaceBlock());
230 allocBuilder.setInsertionPoint(initialOp);
233 for (
auto arg : moduleOp.
getBodyBlock()->getArguments()) {
234 auto name = moduleOp.getArgName(arg.getArgNumber());
236 RootInputOp::create(allocBuilder, arg.getLoc(),
237 StateType::get(arg.getType()), name, storageArg);
238 allocatedInputs.push_back(state);
242 for (
auto &op : moduleOp.getOps()) {
243 if (mlir::isMemoryEffectFree(&op) &&
244 !isa<hw::OutputOp, sim::ClockedTerminateOp>(op))
246 if (isa<MemoryReadPortOp, MemoryWritePortOp>(op))
248 if (failed(lowerOp(&op)))
254 for (
auto &op :
llvm::make_early_inc_range(
llvm::reverse(modelBlock)))
255 if (
mlir::isOpTriviallyDead(&op))
262LogicalResult ModuleLowering::lowerOp(Operation *op) {
263 LLVM_DEBUG(llvm::dbgs() <<
"- Handling " << *op <<
"\n");
266 SmallVector<Phase, 2> phases = {Phase::New};
267 if (isa<seq::InitialOp>(op))
268 phases = {Phase::Initial};
269 if (isa<llhd::FinalOp>(op))
270 phases = {Phase::Final};
271 if (isa<StateOp>(op))
272 phases = {Phase::Initial, Phase::New};
274 for (
auto phase : phases) {
275 if (loweredOps.contains({op, phase}))
277 opsWorklist.push_back(OpLowering(op, phase, *
this));
278 opsSeen.insert({op, phase});
281 auto dumpWorklist = [&] {
282 for (
auto &opLowering :
llvm::reverse(opsWorklist))
283 opLowering.op->emitRemark()
284 <<
"computing " << opLowering.phase <<
" phase here";
287 while (!opsWorklist.empty()) {
288 auto &opLowering = opsWorklist.back();
291 if (opLowering.initial) {
292 if (failed(opLowering.lower())) {
296 std::reverse(opLowering.pending.begin(), opLowering.pending.end());
297 opLowering.initial =
false;
301 if (!opLowering.pending.empty()) {
302 auto [defOp, phase] = opLowering.pending.pop_back_val();
303 if (loweredOps.contains({defOp, phase}))
305 if (!opsSeen.insert({defOp, phase}).second) {
306 defOp->emitOpError(
"is on a combinational loop");
310 opsWorklist.push_back(OpLowering(defOp, phase, *
this));
316 LLVM_DEBUG(llvm::dbgs() <<
" - Lowering " << opLowering.phase <<
" "
317 << *opLowering.op <<
"\n");
318 if (failed(opLowering.lower())) {
322 loweredOps.insert({opLowering.op, opLowering.phase});
323 opsSeen.erase({opLowering.op, opLowering.phase});
324 opsWorklist.pop_back();
332Value ModuleLowering::getAllocatedState(OpResult result) {
333 if (
auto alloc = allocatedStates.lookup(result))
337 if (
auto memOp = dyn_cast<MemoryOp>(result.getOwner())) {
339 AllocMemoryOp::create(allocBuilder, memOp.getLoc(), memOp.getType(),
340 storageArg, memOp->getAttrs());
341 allocatedStates.insert({result, alloc});
347 AllocStateOp::create(allocBuilder, result.getLoc(),
348 StateType::get(result.getType()), storageArg);
349 allocatedStates.insert({result, alloc});
354 if (
auto instOp = dyn_cast<InstanceOp>(result.getOwner()))
356 "name", builder.getStringAttr(
357 instOp.getInstanceName() +
"/" +
358 instOp.getOutputName(result.getResultNumber()).getValue()));
363 if (isa<StateOp, sim::DPICallOp>(result.getOwner()))
364 if (
auto names = result.getOwner()->getAttrOfType<ArrayAttr>(
"names"))
365 if (result.getResultNumber() < names.size())
366 alloc->setAttr(
"name", names[result.getResultNumber()]);
373Value ModuleLowering::detectPosedge(Value clock) {
374 auto loc = clock.getLoc();
375 if (isa<seq::ClockType>(clock.getType()))
376 clock = seq::FromClockOp::create(builder, loc, clock);
379 auto oldStorage = AllocStateOp::create(
380 allocBuilder, loc, StateType::get(builder.getI1Type()), storageArg);
384 auto oldClock = StateReadOp::create(builder, loc, oldStorage);
385 StateWriteOp::create(builder, loc, oldStorage, clock);
388 auto edge = comb::XorOp::create(builder, loc, oldClock, clock);
389 return comb::AndOp::create(builder, loc, edge, clock);
393OpBuilder &ModuleLowering::getBuilder(Phase phase) {
396 return initialBuilder;
406Value ModuleLowering::requireLoweredValue(Value value, Phase phase,
408 if (
auto lowered = loweredValues.lookup({value, phase}))
410 auto d = emitError(value.getLoc()) <<
"value has not been lowered";
411 d.attachNote(useLoc) <<
"value used here";
423 if (
auto ip = builder.getInsertionPoint(); ip != builder.getBlock()->begin())
424 if (
auto ifOp = dyn_cast<scf::IfOp>(*std::prev(ip)))
425 if (ifOp.getCondition() == condition)
427 return scf::IfOp::create(builder, condition.getLoc(), condition, withElse);
434LogicalResult OpLowering::lower() {
435 return TypeSwitch<Operation *, LogicalResult>(op)
437 .Case<StateOp, sim::DPICallOp, MemoryOp, TapOp, InstanceOp,
438 CoroutineInstanceOp, hw::TriggeredOp, hw::OutputOp, seq::InitialOp,
439 llhd::FinalOp, llhd::CurrentTimeOp, sim::ClockedTerminateOp>(
440 [&](
auto op) {
return lower(op); })
444 .Case<MemoryWritePortOp, MemoryReadPortOp>([&](
auto op) {
445 assert(
false &&
"ports must be lowered by memory op");
450 .Default([&](
auto) {
return lowerDefault(); });
455LogicalResult OpLowering::lowerDefault() {
458 auto anyFailed =
false;
459 op->walk([&](Operation *nestedOp) {
460 for (
auto operand : nestedOp->getOperands()) {
461 if (op->isAncestor(operand.getParentBlock()->getParentOp()))
463 auto lowered = lowerValue(operand, phase);
466 mapping.map(operand, lowered);
475 auto *clonedOp =
module.getBuilder(phase).clone(*op, mapping);
478 for (
auto [oldResult, newResult] :
479 llvm::zip(op->getResults(), clonedOp->getResults()))
480 module.loweredValues[{oldResult, phase}] = newResult;
489LogicalResult OpLowering::lower(StateOp op) {
491 if (phase == Phase::Initial) {
494 for (
auto initial : op.getInitials())
495 lowerValue(initial, Phase::Initial);
500 if (op.getInitials().empty())
502 for (
auto [initial, result] :
503 llvm::zip(op.getInitials(), op.getResults())) {
504 auto value = lowerValue(initial, Phase::Initial);
507 auto state =
module.getAllocatedState(result);
510 StateWriteOp::create(module.initialBuilder, value.getLoc(), state, value);
515 assert(phase == Phase::New);
519 return op.emitOpError() <<
"must have a clock";
520 if (op.getLatency() > 1)
521 return op.emitOpError(
"latencies > 1 not supported yet");
524 return lowerStateful(op.getClock(), op.getEnable(), op.getReset(),
525 op.getInputs(), op.getResults(), [&](ValueRange inputs) {
526 return CallOp::create(module.builder, op.getLoc(),
527 op.getResultTypes(), op.getArc(),
537LogicalResult OpLowering::lower(sim::DPICallOp op) {
539 if (!op.getClock()) {
541 SmallVector<Value> inputs;
542 for (
auto operand : op.getInputs())
543 inputs.push_back(lowerValue(operand, phase));
546 if (llvm::is_contained(inputs, Value{}))
549 return op.emitOpError() <<
"without clock cannot have an enable";
553 func::CallOp::create(module.getBuilder(phase), op.getLoc(),
554 op.getCalleeAttr(), op.getResultTypes(), inputs);
555 for (
auto [oldResult, newResult] :
556 llvm::zip(op.getResults(), callOp.getResults()))
557 module.loweredValues[{oldResult, phase}] = newResult;
561 assert(phase == Phase::New);
563 return lowerStateful(op.getClock(), op.getEnable(), {},
564 op.getInputs(), op.getResults(), [&](ValueRange inputs) {
565 return func::CallOp::create(
566 module.builder, op.getLoc(),
567 op.getCalleeAttr(), op.getResultTypes(),
577LogicalResult OpLowering::lowerStateful(
578 Value clock, Value enable, Value reset, ValueRange inputs,
580 llvm::function_ref<ValueRange(ValueRange)> createMapping) {
586 lowerValue(clock, Phase::New);
588 lowerValue(enable, Phase::Old);
590 lowerValue(reset, Phase::Old);
591 for (
auto input : inputs)
592 lowerValue(input, Phase::Old);
598 auto ifClockOp = createIfClockOp(clock);
601 OpBuilder::InsertionGuard guard(module.builder);
602 module.builder.setInsertionPoint(ifClockOp.thenYield());
606 SmallVector<Value> states;
607 for (
auto result : results) {
608 auto state =
module.getAllocatedState(result);
611 states.push_back(state);
617 auto &[unloweredReset, loweredReset] =
module.prevReset;
618 if (unloweredReset != reset ||
619 loweredReset.getParentBlock() != module.builder.getBlock()) {
620 unloweredReset = reset;
621 loweredReset = lowerValue(reset, Phase::Old);
629 module.builder.setInsertionPoint(ifResetOp.thenYield());
632 for (
auto state : states) {
633 auto type = cast<StateType>(state.getType()).getType();
635 module.builder, loweredReset.getLoc(),
636 module.builder.getIntegerType(hw::getBitWidth(type)), 0);
637 if (value.getType() != type)
640 StateWriteOp::create(module.builder, loweredReset.getLoc(), state, value);
642 module.builder.setInsertionPoint(ifResetOp.elseYield());
648 auto &[unloweredEnable, loweredEnable] =
module.prevEnable;
649 if (unloweredEnable != enable ||
650 loweredEnable.getParentBlock() != module.builder.getBlock()) {
651 unloweredEnable = enable;
652 loweredEnable = lowerValue(enable, Phase::Old);
659 auto ifEnableOp =
createOrReuseIf(module.builder, loweredEnable,
false);
660 module.builder.setInsertionPoint(ifEnableOp.thenYield());
664 SmallVector<Value> loweredInputs;
665 for (
auto input : inputs) {
666 auto lowered = lowerValue(input, Phase::Old);
669 loweredInputs.push_back(lowered);
673 auto loweredResults = createMapping(loweredInputs);
674 for (
auto [state, value] :
llvm::zip(states, loweredResults))
675 StateWriteOp::create(module.builder, value.
getLoc(), state, value);
680 module.builder.setInsertionPoint(ifClockOp);
681 for (
auto [state, result] :
llvm::zip(states, results)) {
682 auto oldValue = StateReadOp::create(module.builder, result.getLoc(), state);
683 module.loweredValues[{result, Phase::Old}] = oldValue;
692LogicalResult OpLowering::lower(MemoryOp op) {
693 assert(phase == Phase::New);
696 SmallVector<MemoryReadPortOp> reads;
697 SmallVector<MemoryWritePortOp> writes;
699 for (
auto *user : op->getUsers()) {
700 if (
auto read = dyn_cast<MemoryReadPortOp>(user)) {
701 reads.push_back(read);
702 }
else if (
auto write = dyn_cast<MemoryWritePortOp>(user)) {
703 writes.push_back(write);
705 auto d = op.emitOpError()
706 <<
"users must all be memory read or write port ops";
707 d.attachNote(user->getLoc())
708 <<
"but found " << user->getName() <<
" user here";
715 for (
auto read : reads)
716 lowerValue(
read, Phase::Old);
717 for (
auto write : writes) {
718 if (
write.getClock())
719 lowerValue(
write.getClock(), Phase::New);
720 for (
auto input :
write.getInputs())
721 lowerValue(input, Phase::Old);
727 auto state =
module.getAllocatedState(op->getResult(0));
731 for (
auto read : reads) {
732 auto oldValue = lowerValue(read, Phase::Old);
735 module.loweredValues[{read, Phase::Old}] = oldValue;
739 for (
auto write : writes) {
740 if (!
write.getClock())
741 return write.emitOpError() <<
"must have a clock";
742 if (
write.getLatency() > 1)
743 return write.emitOpError(
"latencies > 1 not supported yet");
746 auto ifClockOp = createIfClockOp(
write.getClock());
749 OpBuilder::InsertionGuard guard(module.builder);
750 module.builder.setInsertionPoint(ifClockOp.thenYield());
753 SmallVector<Value> inputs;
754 for (
auto input :
write.getInputs()) {
755 auto lowered = lowerValue(input, Phase::Old);
758 inputs.push_back(lowered);
761 CallOp::create(module.builder,
write.getLoc(),
762 write.getArcResultTypes(),
write.getArc(), inputs);
765 if (
write.getEnable()) {
767 module.builder, callOp.getResult(
write.getEnableIdx()),
false);
768 module.builder.setInsertionPoint(ifEnableOp.thenYield());
773 auto address = callOp.getResult(
write.getAddressIdx());
774 auto data = callOp.getResult(
write.getDataIdx());
775 if (
write.getMask()) {
776 auto mask = callOp.getResult(
write.getMaskIdx(
write.getEnable()));
777 auto maskInv =
module.builder.createOrFold<comb::XorOp>(
778 write.getLoc(), mask,
779 ConstantOp::create(module.builder, write.getLoc(), mask.getType(),
783 MemoryReadOp::create(module.builder,
write.getLoc(), state, address);
784 auto oldMasked = comb::AndOp::create(module.builder,
write.getLoc(),
785 maskInv, oldData,
true);
787 comb::AndOp::create(module.builder,
write.getLoc(), mask, data,
true);
788 data = comb::OrOp::create(module.builder,
write.getLoc(), oldMasked,
793 MemoryWriteOp::create(module.builder,
write.getLoc(), state, address, data);
801LogicalResult OpLowering::lower(TapOp op) {
802 assert(phase == Phase::New);
804 auto value = lowerValue(op.getValue(), phase);
810 auto &state =
module.allocatedTaps[op];
812 auto alloc = AllocStateOp::create(module.allocBuilder, op.getLoc(),
813 StateType::get(value.getType()),
814 module.storageArg,
true);
815 alloc->setAttr(
"names", op.getNamesAttr());
818 StateWriteOp::create(module.builder, op.getLoc(), state, value);
825LogicalResult OpLowering::lower(InstanceOp op) {
826 assert(phase == Phase::New);
829 SmallVector<Value> values;
830 for (
auto operand : op.getOperands())
831 values.push_back(lowerValue(operand, Phase::New));
834 if (llvm::is_contained(values, Value{}))
839 for (
auto [value, name] :
llvm::zip(values, op.getArgNames())) {
840 auto state = AllocStateOp::create(module.allocBuilder, value.getLoc(),
841 StateType::get(value.getType()),
843 state->setAttr(
"name", module.builder.getStringAttr(
844 op.getInstanceName() +
"/" +
845 cast<StringAttr>(name).getValue()));
846 StateWriteOp::create(module.builder, value.getLoc(), state, value);
853 for (
auto result : op.getResults())
854 module.getAllocatedState(result);
874LogicalResult OpLowering::lower(CoroutineInstanceOp op) {
875 assert(phase == Phase::New);
880 SmallVector<Value> inputs;
881 for (
auto input : op.getArgs())
882 inputs.push_back(lowerValue(input, Phase::New));
885 if (llvm::is_contained(inputs, Value{}))
891 auto callee = op.getCalleeAttr();
893 module.symbolTable.lookup<CoroutineDefineOp>(callee.getAttr());
894 assert(defineOp &&
"verified by CoroutineInstanceOp::verifySymbolUses");
895 auto loc = op.getLoc();
896 auto *
context = op.getContext();
897 auto stateType = CoroutineStateType::get(
context, callee);
898 auto pcType = CoroutinePCType::get(
context, callee);
899 auto i64Type =
module.builder.getI64Type();
904 auto pcSlot = AllocStateOp::create(module.allocBuilder, loc,
905 StateType::get(pcType), module.storageArg);
906 auto stateSlot = AllocStateOp::create(
907 module.allocBuilder, loc, StateType::get(stateType), module.storageArg);
908 auto wakeupSlot = AllocStateOp::create(
909 module.allocBuilder, loc, StateType::get(i64Type), module.storageArg);
913 SmallVector<Value> resultSlots;
914 for (
auto result : op.getResults()) {
915 auto slot =
module.getAllocatedState(result);
918 resultSlots.push_back(slot);
929 module.builder.getI1Type(), 0);
930 if (!inputs.empty()) {
931 auto maskType =
module.builder.getIntegerType(inputs.size());
932 maskSlot = AllocStateOp::create(
933 module.allocBuilder, loc, StateType::get(maskType), module.storageArg);
934 auto mask = StateReadOp::create(module.builder, loc, maskSlot);
935 for (
auto [index, input] :
llvm::enumerate(inputs)) {
936 if (!op.getSensitivityMask()[index])
938 auto prevSlot = AllocStateOp::create(module.allocBuilder, loc,
939 StateType::get(input.getType()),
941 auto prev = StateReadOp::create(module.builder, loc, prevSlot);
942 StateWriteOp::create(module.builder, loc, prevSlot, input);
943 auto changed = comb::ICmpOp::create(module.builder, loc,
944 comb::ICmpPredicate::ne, input, prev);
947 static_cast<unsigned>(index), 1);
948 auto masked = comb::AndOp::create(module.builder, loc, changed, maskBit);
949 anyChange = comb::OrOp::create(module.builder, loc, anyChange, masked);
955 auto now = CurrentTimeOp::create(module.builder, loc, module.storageArg);
956 auto wakeup = StateReadOp::create(module.builder, loc, wakeupSlot);
957 auto timeReady = comb::ICmpOp::create(module.builder, loc,
958 comb::ICmpPredicate::uge, now, wakeup);
959 auto ready = comb::OrOp::create(module.builder, loc, timeReady, anyChange);
961 scf::IfOp::create(module.builder, loc, ready,
false);
963 OpBuilder::InsertionGuard guard(module.builder);
964 module.builder.setInsertionPoint(ifOp.thenYield());
966 auto oldState = StateReadOp::create(module.builder, loc, stateSlot);
967 auto oldPc = StateReadOp::create(module.builder, loc, pcSlot);
971 SmallVector<Type> callResultTypes;
972 callResultTypes.push_back(stateType);
973 callResultTypes.push_back(pcType);
974 llvm::append_range(callResultTypes, defineOp.getResultTypes());
975 auto call = CoroutineCallOp::create(module.builder, loc, callResultTypes,
976 callee, oldState, oldPc, inputs);
977 auto newState = call.getResult(0);
978 auto newPc = call.getResult(1);
979 auto wakeupNew = call.getResults().back();
980 auto maskNew = call.getResult(2 + op.getNumResults());
984 auto isHalt = CoroutinePCIsHaltOp::create(module.builder, loc, newPc);
985 auto isReturn = CoroutinePCIsReturnOp::create(module.builder, loc, newPc);
986 auto isDone = comb::OrOp::create(module.builder, loc, isHalt, isReturn);
989 comb::MuxOp::create(module.builder, loc, isDone, never, wakeupNew);
991 StateWriteOp::create(module.builder, loc, stateSlot, newState);
992 StateWriteOp::create(module.builder, loc, pcSlot, newPc);
993 StateWriteOp::create(module.builder, loc, wakeupSlot, wakeupEff);
995 StateWriteOp::create(module.builder, loc, maskSlot, maskNew);
996 for (
auto [index, slot] :
llvm::enumerate(resultSlots))
997 StateWriteOp::create(module.builder, loc, slot,
998 call.getResult(2 + index));
1004 auto curWakeup = StateReadOp::create(module.builder, loc, wakeupSlot);
1006 GetNextWakeupOp::create(module.builder, loc, module.storageArg);
1008 arith::MinUIOp::create(module.builder, loc, curWakeup, nextWakeup);
1009 SetNextWakeupOp::create(module.builder, loc, module.storageArg, minWakeup);
1015LogicalResult OpLowering::lower(hw::TriggeredOp op) {
1016 assert(phase == Phase::New);
1018 if (op.getEvent() != hw::EventControl::AtPosEdge) {
1020 return op.emitOpError(
"only posedge triggers are supported");
1024 lowerValue(op.getTrigger(), Phase::New);
1025 SmallVector<Value> inputs;
1026 for (
auto input : op.getInputs())
1027 inputs.push_back(lowerValue(input, Phase::Old));
1030 if (llvm::is_contained(inputs, Value{}))
1033 auto ifClockOp = createIfClockOp(op.getTrigger());
1037 OpBuilder::InsertionGuard guard(module.builder);
1038 module.builder.setInsertionPoint(ifClockOp.thenYield());
1041 for (
auto [arg, input] :
llvm::zip(op.
getBodyBlock()->getArguments(), inputs))
1042 module.loweredValues[{arg, Phase::New}] = input;
1044 OpLowering bodyLowering(&bodyOp, Phase::New, module);
1045 bodyLowering.initial =
false;
1046 if (failed(bodyLowering.lower()))
1055LogicalResult OpLowering::lower(hw::OutputOp op) {
1056 assert(phase == Phase::New);
1059 SmallVector<Value> values;
1060 for (
auto operand : op.getOperands())
1061 values.push_back(lowerValue(operand, Phase::New));
1064 if (llvm::is_contained(values, Value{}))
1068 for (
auto [value, name] :
1069 llvm::zip(values, module.moduleOp.getOutputNames())) {
1070 auto state = RootOutputOp::create(
1071 module.allocBuilder, value.getLoc(), StateType::get(value.getType()),
1072 cast<StringAttr>(name), module.storageArg);
1073 StateWriteOp::create(module.builder, value.getLoc(), state, value);
1079LogicalResult OpLowering::lower(seq::InitialOp op) {
1080 assert(phase == Phase::Initial);
1083 SmallVector<Value> operands;
1084 for (
auto operand : op.getOperands())
1085 operands.push_back(lowerValue(operand, Phase::Initial));
1088 if (llvm::is_contained(operands, Value{}))
1092 for (
auto [arg, operand] :
llvm::zip(op.getBody().getArguments(), operands))
1093 module.loweredValues[{arg, Phase::Initial}] = operand;
1097 IRMapping bodyMapping;
1098 auto *initialBlock =
module.initialBuilder.getBlock();
1103 auto result = op.walk([&](llhd::CurrentTimeOp timeOp) {
1104 if (failed(lower(timeOp)))
1105 return WalkResult::interrupt();
1106 auto loweredTime =
module.loweredValues.lookup({timeOp.getResult(), phase});
1107 timeOp.replaceAllUsesWith(loweredTime);
1109 return WalkResult::advance();
1111 if (result.wasInterrupted())
1114 for (
auto &bodyOp : op.getOps()) {
1115 if (isa<seq::YieldOp>(bodyOp))
1119 auto *clonedOp =
module.initialBuilder.clone(bodyOp, bodyMapping);
1120 auto result = clonedOp->walk([&](Operation *nestedClonedOp) {
1121 for (
auto &operand : nestedClonedOp->getOpOperands()) {
1123 if (clonedOp->isAncestor(operand.get().getParentBlock()->getParentOp()))
1127 if (
auto *defOp = operand.get().getDefiningOp())
1128 if (defOp->getBlock() == initialBlock)
1130 auto value =
module.requireLoweredValue(operand.get(), Phase::Initial,
1131 nestedClonedOp->getLoc());
1133 return WalkResult::interrupt();
1136 return WalkResult::advance();
1138 if (result.wasInterrupted())
1142 for (
auto [result, lowered] :
1143 llvm::zip(bodyOp.getResults(), clonedOp->getResults())) {
1144 bodyMapping.map(result, lowered);
1145 module.loweredValues[{result, Phase::Initial}] = lowered;
1150 auto *terminator = op.getBodyBlock()->getTerminator();
1151 for (
auto [result, operand] :
1152 llvm::zip(op.getResults(), terminator->getOperands())) {
1153 auto value =
module.requireLoweredValue(operand, Phase::Initial,
1154 terminator->getLoc());
1157 module.loweredValues[{result, Phase::Initial}] = value;
1164LogicalResult OpLowering::lower(llhd::FinalOp op) {
1165 assert(phase == Phase::Final);
1168 SmallVector<Value> externalOperands;
1169 op.walk([&](Operation *nestedOp) {
1170 for (
auto value : nestedOp->getOperands())
1171 if (!op->
isAncestor(value.getParentBlock()->getParentOp()))
1172 externalOperands.push_back(value);
1177 for (
auto operand : externalOperands) {
1178 auto lowered = lowerValue(operand, Phase::Final);
1179 if (!initial && !lowered)
1181 mapping.map(operand, lowered);
1189 auto result = op.walk([&](llhd::CurrentTimeOp timeOp) {
1190 if (failed(lower(timeOp)))
1191 return WalkResult::interrupt();
1192 auto loweredTime =
module.loweredValues.lookup({timeOp.getResult(), phase});
1193 timeOp.replaceAllUsesWith(loweredTime);
1195 return WalkResult::advance();
1197 if (result.wasInterrupted())
1202 if (op.getBody().hasOneBlock()) {
1203 for (
auto &bodyOp : op.getBody().front().without_terminator())
1204 module.finalBuilder.clone(bodyOp, mapping);
1210 auto executeOp = scf::ExecuteRegionOp::create(module.finalBuilder,
1211 op.getLoc(), TypeRange{});
1212 module.finalBuilder.cloneRegionBefore(op.getBody(), executeOp.getRegion(),
1213 executeOp.getRegion().begin(), mapping);
1214 executeOp.walk([&](llhd::HaltOp haltOp) {
1215 auto builder = OpBuilder(haltOp);
1216 scf::YieldOp::create(builder, haltOp.getLoc());
1227LogicalResult OpLowering::lower(llhd::CurrentTimeOp op) {
1231 auto loc = op.getLoc();
1235 case Phase::Initial: {
1238 module.initialBuilder, loc, module.initialBuilder.getI64Type(), 0);
1239 time = llhd::IntToTimeOp::create(module.initialBuilder, loc, zeroInt);
1244 case Phase::Final: {
1246 auto &builder =
module.getBuilder(phase);
1247 auto timeInt = CurrentTimeOp::create(builder, loc, module.storageArg);
1248 time = llhd::IntToTimeOp::create(builder, loc, timeInt);
1253 module.loweredValues[{op.getResult(), phase}] = time;
1257LogicalResult OpLowering::lower(sim::ClockedTerminateOp op) {
1258 if (phase != Phase::New)
1264 auto ifClockOp = createIfClockOp(op.getClock());
1268 OpBuilder::InsertionGuard guard(module.builder);
1269 module.builder.setInsertionPoint(ifClockOp.thenYield());
1271 auto loc = op.getLoc();
1272 Value cond = lowerValue(op.getCondition(), phase);
1274 return op.emitOpError(
"Failed to lower condition");
1278 return op.emitOpError(
"Failed to create condition block");
1280 module.builder.setInsertionPoint(ifOp.thenYield());
1282 arc::TerminateOp::create(module.builder, loc, module.storageArg,
1283 op.getSuccessAttr());
1292scf::IfOp OpLowering::createIfClockOp(Value clock) {
1293 auto &posedge =
module.loweredPosedges[clock];
1295 auto loweredClock = lowerValue(clock, Phase::New);
1298 posedge =
module.detectPosedge(loweredClock);
1311Value OpLowering::lowerValue(Value value, Phase phase) {
1313 if (
auto lowered = module.loweredValues.lookup({value, phase}))
1317 if (
auto arg = dyn_cast<BlockArgument>(value)) {
1318 if (arg.getOwner() != module.moduleOp.getBodyBlock()) {
1320 emitError(arg.getLoc()) <<
"block argument has not been lowered";
1325 auto state =
module.allocatedInputs[arg.getArgNumber()];
1326 return StateReadOp::create(module.getBuilder(phase), arg.getLoc(), state);
1331 auto result = cast<OpResult>(value);
1332 auto *op = result.getOwner();
1335 if (
auto instOp = dyn_cast<InstanceOp>(op))
1336 return lowerValue(instOp, result, phase);
1337 if (
auto instOp = dyn_cast<CoroutineInstanceOp>(op))
1338 return lowerValue(instOp, result, phase);
1339 if (
auto stateOp = dyn_cast<StateOp>(op))
1340 return lowerValue(stateOp, result, phase);
1341 if (
auto dpiOp = dyn_cast<sim::DPICallOp>(op); dpiOp && dpiOp.getClock())
1342 return lowerValue(dpiOp, result, phase);
1343 if (
auto readOp = dyn_cast<MemoryReadPortOp>(op))
1344 return lowerValue(readOp, result, phase);
1345 if (
auto initialOp = dyn_cast<seq::InitialOp>(op))
1346 return lowerValue(initialOp, result, phase);
1347 if (
auto castOp = dyn_cast<seq::FromImmutableOp>(op))
1348 return lowerValue(castOp, result, phase);
1354 addPending(op, phase);
1357 emitError(result.getLoc()) <<
"value has not been lowered";
1363Value OpLowering::lowerValue(InstanceOp op, OpResult result, Phase phase) {
1366 auto state =
module.getAllocatedState(result);
1367 return StateReadOp::create(module.getBuilder(phase), result.getLoc(), state);
1374Value OpLowering::lowerValue(CoroutineInstanceOp op, OpResult result,
1379 if (phase == Phase::New)
1380 addPending(op, Phase::New);
1385 if (phase == Phase::Old)
1386 assert(!module.loweredOps.contains({op, Phase::New}) &&
1387 "need old value but new value already written");
1389 auto state =
module.getAllocatedState(result);
1390 return StateReadOp::create(module.getBuilder(phase), result.getLoc(), state);
1397Value OpLowering::lowerValue(StateOp op, OpResult result, Phase phase) {
1401 if (phase == Phase::New || phase == Phase::Initial)
1402 addPending(op, phase);
1407 if (phase == Phase::Old)
1408 assert(!module.loweredOps.contains({op, Phase::New}) &&
1409 "need old value but new value already written");
1411 auto state =
module.getAllocatedState(result);
1412 return StateReadOp::create(module.getBuilder(phase), result.getLoc(), state);
1419Value OpLowering::lowerValue(sim::DPICallOp op, OpResult result, Phase phase) {
1423 if (phase == Phase::New || phase == Phase::Initial)
1424 addPending(op, phase);
1429 if (phase == Phase::Old)
1430 assert(!module.loweredOps.contains({op, Phase::New}) &&
1431 "need old value but new value already written");
1433 auto state =
module.getAllocatedState(result);
1434 return StateReadOp::create(module.getBuilder(phase), result.getLoc(), state);
1440Value OpLowering::lowerValue(MemoryReadPortOp op, OpResult result,
1442 auto memOp = op.getMemory().getDefiningOp<MemoryOp>();
1445 op->emitOpError() <<
"memory must be defined locally";
1449 auto address = lowerValue(op.getAddress(), phase);
1452 if (phase == Phase::New)
1453 addPending(memOp.getOperation(), Phase::New);
1459 if (phase == Phase::Old) {
1461 assert(!module.loweredOps.contains({memOp, Phase::New}) &&
1462 "need old memory value but new value already written");
1464 assert(phase == Phase::New);
1467 auto state =
module.getAllocatedState(memOp->getResult(0));
1468 return MemoryReadOp::create(module.getBuilder(phase), result.getLoc(), state,
1475Value OpLowering::lowerValue(seq::InitialOp op, OpResult result, Phase phase) {
1478 addPending(op, Phase::Initial);
1481 auto value =
module.loweredValues.lookup({result, Phase::Initial});
1483 emitError(result.getLoc()) <<
"value has not been lowered";
1489 if (phase == Phase::Initial)
1494 auto &state =
module.allocatedInitials[result];
1496 state = AllocStateOp::create(module.allocBuilder, value.getLoc(),
1497 StateType::get(value.getType()),
1499 OpBuilder::InsertionGuard guard(module.initialBuilder);
1500 module.initialBuilder.setInsertionPointAfterValue(value);
1501 StateWriteOp::create(module.initialBuilder, value.getLoc(), state, value);
1505 return StateReadOp::create(module.getBuilder(phase), state.getLoc(), state);
1509Value OpLowering::lowerValue(seq::FromImmutableOp op, OpResult result,
1511 return lowerValue(op.getInput(), phase);
1515void OpLowering::addPending(Value value, Phase phase) {
1516 auto *defOp = value.getDefiningOp();
1517 assert(defOp &&
"block args should never be marked as a dependency");
1518 addPending(defOp, phase);
1523void OpLowering::addPending(Operation *op, Phase phase) {
1524 auto pair = std::make_pair(op, phase);
1525 if (!module.loweredOps.contains(pair))
1526 if (!llvm::is_contained(pending, pair))
1527 pending.push_back(pair);
1535struct LowerStatePass :
public arc::impl::LowerStatePassBase<LowerStatePass> {
1536 using LowerStatePassBase::LowerStatePassBase;
1537 void runOnOperation()
override;
1541void LowerStatePass::runOnOperation() {
1542 auto op = getOperation();
1543 auto &symbolTable = getAnalysis<SymbolTable>();
1544 for (
auto moduleOp :
llvm::make_early_inc_range(op.getOps<
HWModuleOp>())) {
1545 if (failed(ModuleLowering(moduleOp, symbolTable).
run()))
1546 return signalPassFailure();
1550 for (
auto extModuleOp :
1554 auto uses = symbolTable.getSymbolUses(extModuleOp, op);
1555 if (!uses->empty()) {
1556 extModuleOp->emitError(
"Failed to remove external module because it is "
1557 "still referenced/instantiated");
1558 return signalPassFailure();
1560 extModuleOp.erase();
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
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, List[str] cmdline_args=sys.argv)