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;
149 SymbolTable &symbolTable;
152 SmallVector<OpLowering> opsWorklist;
154 SmallDenseSet<std::pair<Operation *, Phase>> opsSeen;
156 DenseSet<std::pair<Operation *, Phase>> loweredOps;
158 DenseMap<std::pair<Value, Phase>, Value> loweredValues;
161 SmallVector<Value> allocatedInputs;
164 DenseMap<Value, Value> allocatedStates;
166 DenseMap<OpOperand *, Value> allocatedOutputs;
168 DenseMap<Value, Value> allocatedInitials;
170 DenseMap<Operation *, Value> allocatedTaps;
174 DenseMap<Value, Value> loweredPosedges;
177 std::pair<Value, Value> prevEnable;
180 std::pair<Value, Value> prevReset;
182 ModuleLowering(
HWModuleOp moduleOp, SymbolTable &symbolTable)
183 : moduleOp(moduleOp), builder(moduleOp), allocBuilder(moduleOp),
184 initialBuilder(moduleOp), finalBuilder(moduleOp),
185 symbolTable(symbolTable) {}
187 LogicalResult lowerOp(Operation *op);
188 Value getAllocatedState(OpResult result);
189 Value detectPosedge(Value clock);
190 OpBuilder &getBuilder(Phase phase);
191 Value requireLoweredValue(Value value, Phase phase, Location useLoc);
199LogicalResult ModuleLowering::run() {
200 LLVM_DEBUG(llvm::dbgs() <<
"Lowering module `" << moduleOp.getModuleName()
205 ModelOp::create(builder, moduleOp.getLoc(), moduleOp.getModuleNameAttr(),
206 TypeAttr::get(moduleOp.getModuleType()), IntegerAttr{},
207 FlatSymbolRefAttr{}, FlatSymbolRefAttr{}, ArrayAttr{});
208 auto &modelBlock = modelOp.getBody().emplaceBlock();
209 storageArg = modelBlock.addArgument(StorageType::get(builder.getContext()),
211 builder.setInsertionPointToStart(&modelBlock);
212 arcContext = AsContextOp::create(builder, moduleOp.getLoc(), storageArg);
218 builder.getI64Type(), -1);
219 SetNextWakeupOp::create(builder, moduleOp.getLoc(), arcContext, noWakeup);
223 auto initialOp = InitialOp::create(builder, moduleOp.getLoc());
224 initialBuilder.setInsertionPointToStart(&initialOp.getBody().emplaceBlock());
227 auto finalOp = FinalOp::create(builder, moduleOp.getLoc());
228 finalBuilder.setInsertionPointToStart(&finalOp.getBody().emplaceBlock());
232 allocBuilder.setInsertionPoint(initialOp);
235 for (
auto arg : moduleOp.
getBodyBlock()->getArguments()) {
236 auto name = moduleOp.getArgName(arg.getArgNumber());
238 RootInputOp::create(allocBuilder, arg.getLoc(),
239 StateType::get(arg.getType()), name, storageArg);
240 allocatedInputs.push_back(state);
244 for (
auto &op : moduleOp.getOps()) {
245 if (mlir::isMemoryEffectFree(&op) &&
246 !isa<hw::OutputOp, sim::ClockedTerminateOp>(op))
248 if (isa<MemoryReadPortOp, MemoryWritePortOp>(op))
250 if (failed(lowerOp(&op)))
256 for (
auto &op :
llvm::make_early_inc_range(
llvm::reverse(modelBlock)))
257 if (
mlir::isOpTriviallyDead(&op))
264LogicalResult ModuleLowering::lowerOp(Operation *op) {
265 LLVM_DEBUG(llvm::dbgs() <<
"- Handling " << *op <<
"\n");
268 SmallVector<Phase, 2> phases = {Phase::New};
269 if (isa<seq::InitialOp>(op))
270 phases = {Phase::Initial};
271 if (isa<llhd::FinalOp>(op))
272 phases = {Phase::Final};
273 if (isa<StateOp>(op))
274 phases = {Phase::Initial, Phase::New};
276 for (
auto phase : phases) {
277 if (loweredOps.contains({op, phase}))
279 opsWorklist.push_back(OpLowering(op, phase, *
this));
280 opsSeen.insert({op, phase});
283 auto dumpWorklist = [&] {
284 for (
auto &opLowering :
llvm::reverse(opsWorklist))
285 opLowering.op->emitRemark()
286 <<
"computing " << opLowering.phase <<
" phase here";
289 while (!opsWorklist.empty()) {
290 auto &opLowering = opsWorklist.back();
293 if (opLowering.initial) {
294 if (failed(opLowering.lower())) {
298 std::reverse(opLowering.pending.begin(), opLowering.pending.end());
299 opLowering.initial =
false;
303 if (!opLowering.pending.empty()) {
304 auto [defOp, phase] = opLowering.pending.pop_back_val();
305 if (loweredOps.contains({defOp, phase}))
307 if (!opsSeen.insert({defOp, phase}).second) {
308 defOp->emitOpError(
"is on a combinational loop");
312 opsWorklist.push_back(OpLowering(defOp, phase, *
this));
318 LLVM_DEBUG(llvm::dbgs() <<
" - Lowering " << opLowering.phase <<
" "
319 << *opLowering.op <<
"\n");
320 if (failed(opLowering.lower())) {
324 loweredOps.insert({opLowering.op, opLowering.phase});
325 opsSeen.erase({opLowering.op, opLowering.phase});
326 opsWorklist.pop_back();
334Value ModuleLowering::getAllocatedState(OpResult result) {
335 if (
auto alloc = allocatedStates.lookup(result))
339 if (
auto memOp = dyn_cast<MemoryOp>(result.getOwner())) {
341 AllocMemoryOp::create(allocBuilder, memOp.getLoc(), memOp.getType(),
342 storageArg, memOp->getAttrs());
343 allocatedStates.insert({result, alloc});
349 AllocStateOp::create(allocBuilder, result.getLoc(),
350 StateType::get(result.getType()), storageArg);
351 allocatedStates.insert({result, alloc});
356 if (
auto instOp = dyn_cast<InstanceOp>(result.getOwner()))
358 "name", builder.getStringAttr(
359 instOp.getInstanceName() +
"/" +
360 instOp.getOutputName(result.getResultNumber()).getValue()));
365 if (isa<StateOp, sim::DPICallOp>(result.getOwner()))
366 if (
auto names = result.getOwner()->getAttrOfType<ArrayAttr>(
"names"))
367 if (result.getResultNumber() < names.size())
368 alloc->setAttr(
"name", names[result.getResultNumber()]);
375Value ModuleLowering::detectPosedge(Value clock) {
376 auto loc = clock.getLoc();
377 if (isa<seq::ClockType>(clock.getType()))
378 clock = seq::FromClockOp::create(builder, loc, clock);
381 auto oldStorage = AllocStateOp::create(
382 allocBuilder, loc, StateType::get(builder.getI1Type()), storageArg);
386 auto oldClock = StateReadOp::create(builder, loc, oldStorage);
387 StateWriteOp::create(builder, loc, oldStorage, clock);
390 auto edge = comb::XorOp::create(builder, loc, oldClock, clock);
391 return comb::AndOp::create(builder, loc, edge, clock);
395OpBuilder &ModuleLowering::getBuilder(Phase phase) {
398 return initialBuilder;
408Value ModuleLowering::requireLoweredValue(Value value, Phase phase,
410 if (
auto lowered = loweredValues.lookup({value, phase}))
412 auto d = emitError(value.getLoc()) <<
"value has not been lowered";
413 d.attachNote(useLoc) <<
"value used here";
425 if (
auto ip = builder.getInsertionPoint(); ip != builder.getBlock()->begin())
426 if (
auto ifOp = dyn_cast<scf::IfOp>(*std::prev(ip)))
427 if (ifOp.getCondition() == condition)
429 return scf::IfOp::create(builder, condition.getLoc(), condition, withElse);
436LogicalResult OpLowering::lower() {
437 return TypeSwitch<Operation *, LogicalResult>(op)
439 .Case<StateOp, sim::DPICallOp, MemoryOp, TapOp, InstanceOp,
440 CoroutineInstanceOp, hw::TriggeredOp, hw::OutputOp, seq::InitialOp,
441 llhd::FinalOp, llhd::CurrentTimeOp, sim::ClockedTerminateOp>(
442 [&](
auto op) {
return lower(op); })
446 .Case<MemoryWritePortOp, MemoryReadPortOp>([&](
auto op) {
447 assert(
false &&
"ports must be lowered by memory op");
452 .Default([&](
auto) {
return lowerDefault(); });
457LogicalResult OpLowering::lowerDefault() {
460 auto anyFailed =
false;
461 op->walk([&](Operation *nestedOp) {
462 for (
auto operand : nestedOp->getOperands()) {
463 if (op->isAncestor(operand.getParentBlock()->getParentOp()))
465 auto lowered = lowerValue(operand, phase);
468 mapping.map(operand, lowered);
477 auto *clonedOp =
module.getBuilder(phase).clone(*op, mapping);
480 for (
auto [oldResult, newResult] :
481 llvm::zip(op->getResults(), clonedOp->getResults()))
482 module.loweredValues[{oldResult, phase}] = newResult;
491LogicalResult OpLowering::lower(StateOp op) {
493 if (phase == Phase::Initial) {
496 for (
auto initial : op.getInitials())
497 lowerValue(initial, Phase::Initial);
502 if (op.getInitials().empty())
504 for (
auto [initial, result] :
505 llvm::zip(op.getInitials(), op.getResults())) {
506 auto value = lowerValue(initial, Phase::Initial);
509 auto state =
module.getAllocatedState(result);
512 StateWriteOp::create(module.initialBuilder, value.getLoc(), state, value);
517 assert(phase == Phase::New);
521 return op.emitOpError() <<
"must have a clock";
522 if (op.getLatency() > 1)
523 return op.emitOpError(
"latencies > 1 not supported yet");
526 return lowerStateful(op.getClock(), op.getEnable(), op.getReset(),
527 op.getInputs(), op.getResults(), [&](ValueRange inputs) {
528 return CallOp::create(module.builder, op.getLoc(),
529 op.getResultTypes(), op.getArc(),
539LogicalResult OpLowering::lower(sim::DPICallOp op) {
541 if (!op.getClock()) {
543 SmallVector<Value> inputs;
544 for (
auto operand : op.getInputs())
545 inputs.push_back(lowerValue(operand, phase));
548 if (llvm::is_contained(inputs, Value{}))
551 return op.emitOpError() <<
"without clock cannot have an enable";
555 func::CallOp::create(module.getBuilder(phase), op.getLoc(),
556 op.getCalleeAttr(), op.getResultTypes(), inputs);
557 for (
auto [oldResult, newResult] :
558 llvm::zip(op.getResults(), callOp.getResults()))
559 module.loweredValues[{oldResult, phase}] = newResult;
563 assert(phase == Phase::New);
565 return lowerStateful(op.getClock(), op.getEnable(), {},
566 op.getInputs(), op.getResults(), [&](ValueRange inputs) {
567 return func::CallOp::create(
568 module.builder, op.getLoc(),
569 op.getCalleeAttr(), op.getResultTypes(),
579LogicalResult OpLowering::lowerStateful(
580 Value clock, Value enable, Value reset, ValueRange inputs,
582 llvm::function_ref<ValueRange(ValueRange)> createMapping) {
588 lowerValue(clock, Phase::New);
590 lowerValue(enable, Phase::Old);
592 lowerValue(reset, Phase::Old);
593 for (
auto input : inputs)
594 lowerValue(input, Phase::Old);
600 auto ifClockOp = createIfClockOp(clock);
603 OpBuilder::InsertionGuard guard(module.builder);
604 module.builder.setInsertionPoint(ifClockOp.thenYield());
608 SmallVector<Value> states;
609 for (
auto result : results) {
610 auto state =
module.getAllocatedState(result);
613 states.push_back(state);
619 auto &[unloweredReset, loweredReset] =
module.prevReset;
620 if (unloweredReset != reset ||
621 loweredReset.getParentBlock() != module.builder.getBlock()) {
622 unloweredReset = reset;
623 loweredReset = lowerValue(reset, Phase::Old);
631 module.builder.setInsertionPoint(ifResetOp.thenYield());
634 for (
auto state : states) {
635 auto type = cast<StateType>(state.getType()).getType();
637 module.builder, loweredReset.getLoc(),
638 module.builder.getIntegerType(hw::getBitWidth(type)), 0);
639 if (value.getType() != type)
642 StateWriteOp::create(module.builder, loweredReset.getLoc(), state, value);
644 module.builder.setInsertionPoint(ifResetOp.elseYield());
650 auto &[unloweredEnable, loweredEnable] =
module.prevEnable;
651 if (unloweredEnable != enable ||
652 loweredEnable.getParentBlock() != module.builder.getBlock()) {
653 unloweredEnable = enable;
654 loweredEnable = lowerValue(enable, Phase::Old);
661 auto ifEnableOp =
createOrReuseIf(module.builder, loweredEnable,
false);
662 module.builder.setInsertionPoint(ifEnableOp.thenYield());
666 SmallVector<Value> loweredInputs;
667 for (
auto input : inputs) {
668 auto lowered = lowerValue(input, Phase::Old);
671 loweredInputs.push_back(lowered);
675 auto loweredResults = createMapping(loweredInputs);
676 for (
auto [state, value] :
llvm::zip(states, loweredResults))
677 StateWriteOp::create(module.builder, value.
getLoc(), state, value);
682 module.builder.setInsertionPoint(ifClockOp);
683 for (
auto [state, result] :
llvm::zip(states, results)) {
684 auto oldValue = StateReadOp::create(module.builder, result.getLoc(), state);
685 module.loweredValues[{result, Phase::Old}] = oldValue;
694LogicalResult OpLowering::lower(MemoryOp op) {
695 assert(phase == Phase::New);
698 SmallVector<MemoryReadPortOp> reads;
699 SmallVector<MemoryWritePortOp> writes;
701 for (
auto *user : op->getUsers()) {
702 if (
auto read = dyn_cast<MemoryReadPortOp>(user)) {
703 reads.push_back(read);
704 }
else if (
auto write = dyn_cast<MemoryWritePortOp>(user)) {
705 writes.push_back(write);
707 auto d = op.emitOpError()
708 <<
"users must all be memory read or write port ops";
709 d.attachNote(user->getLoc())
710 <<
"but found " << user->getName() <<
" user here";
717 for (
auto read : reads)
718 lowerValue(
read, Phase::Old);
719 for (
auto write : writes) {
720 if (
write.getClock())
721 lowerValue(
write.getClock(), Phase::New);
722 for (
auto input :
write.getInputs())
723 lowerValue(input, Phase::Old);
729 auto state =
module.getAllocatedState(op->getResult(0));
733 for (
auto read : reads) {
734 auto oldValue = lowerValue(read, Phase::Old);
737 module.loweredValues[{read, Phase::Old}] = oldValue;
741 for (
auto write : writes) {
742 if (!
write.getClock())
743 return write.emitOpError() <<
"must have a clock";
744 if (
write.getLatency() > 1)
745 return write.emitOpError(
"latencies > 1 not supported yet");
748 auto ifClockOp = createIfClockOp(
write.getClock());
751 OpBuilder::InsertionGuard guard(module.builder);
752 module.builder.setInsertionPoint(ifClockOp.thenYield());
755 SmallVector<Value> inputs;
756 for (
auto input :
write.getInputs()) {
757 auto lowered = lowerValue(input, Phase::Old);
760 inputs.push_back(lowered);
763 CallOp::create(module.builder,
write.getLoc(),
764 write.getArcResultTypes(),
write.getArc(), inputs);
767 if (
write.getEnable()) {
769 module.builder, callOp.getResult(
write.getEnableIdx()),
false);
770 module.builder.setInsertionPoint(ifEnableOp.thenYield());
775 auto address = callOp.getResult(
write.getAddressIdx());
776 auto data = callOp.getResult(
write.getDataIdx());
777 if (
write.getMask()) {
778 auto mask = callOp.getResult(
write.getMaskIdx(
write.getEnable()));
779 auto maskInv =
module.builder.createOrFold<comb::XorOp>(
780 write.getLoc(), mask,
781 ConstantOp::create(module.builder, write.getLoc(), mask.getType(),
785 MemoryReadOp::create(module.builder,
write.getLoc(), state, address);
786 auto oldMasked = comb::AndOp::create(module.builder,
write.getLoc(),
787 maskInv, oldData,
true);
789 comb::AndOp::create(module.builder,
write.getLoc(), mask, data,
true);
790 data = comb::OrOp::create(module.builder,
write.getLoc(), oldMasked,
795 MemoryWriteOp::create(module.builder,
write.getLoc(), state, address, data);
803LogicalResult OpLowering::lower(TapOp op) {
804 assert(phase == Phase::New);
806 auto value = lowerValue(op.getValue(), phase);
812 auto &state =
module.allocatedTaps[op];
814 auto alloc = AllocStateOp::create(module.allocBuilder, op.getLoc(),
815 StateType::get(value.getType()),
816 module.storageArg,
true);
817 alloc->setAttr(
"names", op.getNamesAttr());
820 StateWriteOp::create(module.builder, op.getLoc(), state, value);
827LogicalResult OpLowering::lower(InstanceOp op) {
828 assert(phase == Phase::New);
831 SmallVector<Value> values;
832 for (
auto operand : op.getOperands())
833 values.push_back(lowerValue(operand, Phase::New));
836 if (llvm::is_contained(values, Value{}))
841 for (
auto [value, name] :
llvm::zip(values, op.getArgNames())) {
842 auto state = AllocStateOp::create(module.allocBuilder, value.getLoc(),
843 StateType::get(value.getType()),
845 state->setAttr(
"name", module.builder.getStringAttr(
846 op.getInstanceName() +
"/" +
847 cast<StringAttr>(name).getValue()));
848 StateWriteOp::create(module.builder, value.getLoc(), state, value);
855 for (
auto result : op.getResults())
856 module.getAllocatedState(result);
876LogicalResult OpLowering::lower(CoroutineInstanceOp op) {
877 assert(phase == Phase::New);
882 SmallVector<Value> inputs;
883 for (
auto input : op.getArgs())
884 inputs.push_back(lowerValue(input, Phase::New));
887 if (llvm::is_contained(inputs, Value{}))
893 auto callee = op.getCalleeAttr();
895 module.symbolTable.lookup<CoroutineDefineOp>(callee.getAttr());
896 assert(defineOp &&
"verified by CoroutineInstanceOp::verifySymbolUses");
897 auto loc = op.getLoc();
898 auto *
context = op.getContext();
899 auto stateType = CoroutineStateType::get(
context, callee);
900 auto pcType = CoroutinePCType::get(
context, callee);
901 auto i64Type =
module.builder.getI64Type();
906 auto pcSlot = AllocStateOp::create(module.allocBuilder, loc,
907 StateType::get(pcType), module.storageArg);
908 auto stateSlot = AllocStateOp::create(
909 module.allocBuilder, loc, StateType::get(stateType), module.storageArg);
910 auto wakeupSlot = AllocStateOp::create(
911 module.allocBuilder, loc, StateType::get(i64Type), module.storageArg);
915 SmallVector<Value> resultSlots;
916 for (
auto result : op.getResults()) {
917 auto slot =
module.getAllocatedState(result);
920 resultSlots.push_back(slot);
931 module.builder.getI1Type(), 0);
932 if (!inputs.empty()) {
933 auto maskType =
module.builder.getIntegerType(inputs.size());
934 maskSlot = AllocStateOp::create(
935 module.allocBuilder, loc, StateType::get(maskType), module.storageArg);
936 auto mask = StateReadOp::create(module.builder, loc, maskSlot);
937 for (
auto [index, input] :
llvm::enumerate(inputs)) {
938 if (!op.getSensitivityMask()[index])
940 auto prevSlot = AllocStateOp::create(module.allocBuilder, loc,
941 StateType::get(input.getType()),
943 auto prev = StateReadOp::create(module.builder, loc, prevSlot);
944 StateWriteOp::create(module.builder, loc, prevSlot, input);
945 auto changed = comb::ICmpOp::create(module.builder, loc,
946 comb::ICmpPredicate::ne, input, prev);
949 static_cast<unsigned>(index), 1);
950 auto masked = comb::AndOp::create(module.builder, loc, changed, maskBit);
951 anyChange = comb::OrOp::create(module.builder, loc, anyChange, masked);
957 auto now = CurrentTimeOp::create(module.builder, loc, module.arcContext);
958 auto wakeup = StateReadOp::create(module.builder, loc, wakeupSlot);
959 auto timeReady = comb::ICmpOp::create(module.builder, loc,
960 comb::ICmpPredicate::uge, now, wakeup);
961 auto ready = comb::OrOp::create(module.builder, loc, timeReady, anyChange);
963 scf::IfOp::create(module.builder, loc, ready,
false);
965 OpBuilder::InsertionGuard guard(module.builder);
966 module.builder.setInsertionPoint(ifOp.thenYield());
968 auto oldState = StateReadOp::create(module.builder, loc, stateSlot);
969 auto oldPc = StateReadOp::create(module.builder, loc, pcSlot);
973 SmallVector<Type> callResultTypes;
974 callResultTypes.push_back(stateType);
975 callResultTypes.push_back(pcType);
976 llvm::append_range(callResultTypes, defineOp.getResultTypes());
977 auto call = CoroutineCallOp::create(module.builder, loc, callResultTypes,
978 callee, oldState, oldPc, inputs);
979 auto newState = call.getResult(0);
980 auto newPc = call.getResult(1);
981 auto wakeupNew = call.getResults().back();
982 auto maskNew = call.getResult(2 + op.getNumResults());
986 auto isHalt = CoroutinePCIsHaltOp::create(module.builder, loc, newPc);
987 auto isReturn = CoroutinePCIsReturnOp::create(module.builder, loc, newPc);
988 auto isDone = comb::OrOp::create(module.builder, loc, isHalt, isReturn);
991 comb::MuxOp::create(module.builder, loc, isDone, never, wakeupNew);
993 StateWriteOp::create(module.builder, loc, stateSlot, newState);
994 StateWriteOp::create(module.builder, loc, pcSlot, newPc);
995 StateWriteOp::create(module.builder, loc, wakeupSlot, wakeupEff);
997 StateWriteOp::create(module.builder, loc, maskSlot, maskNew);
998 for (
auto [index, slot] :
llvm::enumerate(resultSlots))
999 StateWriteOp::create(module.builder, loc, slot,
1000 call.getResult(2 + index));
1006 auto curWakeup = StateReadOp::create(module.builder, loc, wakeupSlot);
1008 GetNextWakeupOp::create(module.builder, loc, module.arcContext);
1010 arith::MinUIOp::create(module.builder, loc, curWakeup, nextWakeup);
1011 SetNextWakeupOp::create(module.builder, loc, module.arcContext, minWakeup);
1017LogicalResult OpLowering::lower(hw::TriggeredOp op) {
1018 assert(phase == Phase::New);
1020 if (op.getEvent() != hw::EventControl::AtPosEdge) {
1022 return op.emitOpError(
"only posedge triggers are supported");
1026 lowerValue(op.getTrigger(), Phase::New);
1027 SmallVector<Value> inputs;
1028 for (
auto input : op.getInputs())
1029 inputs.push_back(lowerValue(input, Phase::Old));
1032 if (llvm::is_contained(inputs, Value{}))
1035 auto ifClockOp = createIfClockOp(op.getTrigger());
1039 OpBuilder::InsertionGuard guard(module.builder);
1040 module.builder.setInsertionPoint(ifClockOp.thenYield());
1043 for (
auto [arg, input] :
llvm::zip(op.
getBodyBlock()->getArguments(), inputs))
1044 module.loweredValues[{arg, Phase::New}] = input;
1046 OpLowering bodyLowering(&bodyOp, Phase::New, module);
1047 bodyLowering.initial =
false;
1048 if (failed(bodyLowering.lower()))
1057LogicalResult OpLowering::lower(hw::OutputOp op) {
1058 assert(phase == Phase::New);
1061 SmallVector<Value> values;
1062 for (
auto operand : op.getOperands())
1063 values.push_back(lowerValue(operand, Phase::New));
1066 if (llvm::is_contained(values, Value{}))
1070 for (
auto [value, name] :
1071 llvm::zip(values, module.moduleOp.getOutputNames())) {
1072 auto state = RootOutputOp::create(
1073 module.allocBuilder, value.getLoc(), StateType::get(value.getType()),
1074 cast<StringAttr>(name), module.storageArg);
1075 StateWriteOp::create(module.builder, value.getLoc(), state, value);
1081LogicalResult OpLowering::lower(seq::InitialOp op) {
1082 assert(phase == Phase::Initial);
1085 SmallVector<Value> operands;
1086 for (
auto operand : op.getOperands())
1087 operands.push_back(lowerValue(operand, Phase::Initial));
1090 if (llvm::is_contained(operands, Value{}))
1094 for (
auto [arg, operand] :
llvm::zip(op.getBody().getArguments(), operands))
1095 module.loweredValues[{arg, Phase::Initial}] = operand;
1099 IRMapping bodyMapping;
1100 auto *initialBlock =
module.initialBuilder.getBlock();
1105 auto result = op.walk([&](llhd::CurrentTimeOp timeOp) {
1106 if (failed(lower(timeOp)))
1107 return WalkResult::interrupt();
1108 auto loweredTime =
module.loweredValues.lookup({timeOp.getResult(), phase});
1109 timeOp.replaceAllUsesWith(loweredTime);
1111 return WalkResult::advance();
1113 if (result.wasInterrupted())
1116 for (
auto &bodyOp : op.getOps()) {
1117 if (isa<seq::YieldOp>(bodyOp))
1121 auto *clonedOp =
module.initialBuilder.clone(bodyOp, bodyMapping);
1122 auto result = clonedOp->walk([&](Operation *nestedClonedOp) {
1123 for (
auto &operand : nestedClonedOp->getOpOperands()) {
1125 if (clonedOp->isAncestor(operand.get().getParentBlock()->getParentOp()))
1129 if (
auto *defOp = operand.get().getDefiningOp())
1130 if (defOp->getBlock() == initialBlock)
1132 auto value =
module.requireLoweredValue(operand.get(), Phase::Initial,
1133 nestedClonedOp->getLoc());
1135 return WalkResult::interrupt();
1138 return WalkResult::advance();
1140 if (result.wasInterrupted())
1144 for (
auto [result, lowered] :
1145 llvm::zip(bodyOp.getResults(), clonedOp->getResults())) {
1146 bodyMapping.map(result, lowered);
1147 module.loweredValues[{result, Phase::Initial}] = lowered;
1152 auto *terminator = op.getBodyBlock()->getTerminator();
1153 for (
auto [result, operand] :
1154 llvm::zip(op.getResults(), terminator->getOperands())) {
1155 auto value =
module.requireLoweredValue(operand, Phase::Initial,
1156 terminator->getLoc());
1159 module.loweredValues[{result, Phase::Initial}] = value;
1166LogicalResult OpLowering::lower(llhd::FinalOp op) {
1167 assert(phase == Phase::Final);
1170 SmallVector<Value> externalOperands;
1171 op.walk([&](Operation *nestedOp) {
1172 for (
auto value : nestedOp->getOperands())
1173 if (!op->
isAncestor(value.getParentBlock()->getParentOp()))
1174 externalOperands.push_back(value);
1179 for (
auto operand : externalOperands) {
1180 auto lowered = lowerValue(operand, Phase::Final);
1181 if (!initial && !lowered)
1183 mapping.map(operand, lowered);
1191 auto result = op.walk([&](llhd::CurrentTimeOp timeOp) {
1192 if (failed(lower(timeOp)))
1193 return WalkResult::interrupt();
1194 auto loweredTime =
module.loweredValues.lookup({timeOp.getResult(), phase});
1195 timeOp.replaceAllUsesWith(loweredTime);
1197 return WalkResult::advance();
1199 if (result.wasInterrupted())
1204 if (op.getBody().hasOneBlock()) {
1205 for (
auto &bodyOp : op.getBody().front().without_terminator())
1206 module.finalBuilder.clone(bodyOp, mapping);
1212 auto executeOp = scf::ExecuteRegionOp::create(module.finalBuilder,
1213 op.getLoc(), TypeRange{});
1214 module.finalBuilder.cloneRegionBefore(op.getBody(), executeOp.getRegion(),
1215 executeOp.getRegion().begin(), mapping);
1216 executeOp.walk([&](llhd::HaltOp haltOp) {
1217 auto builder = OpBuilder(haltOp);
1218 scf::YieldOp::create(builder, haltOp.getLoc());
1229LogicalResult OpLowering::lower(llhd::CurrentTimeOp op) {
1233 auto loc = op.getLoc();
1237 case Phase::Initial: {
1240 module.initialBuilder, loc, module.initialBuilder.getI64Type(), 0);
1241 time = llhd::IntToTimeOp::create(module.initialBuilder, loc, zeroInt);
1246 case Phase::Final: {
1248 auto &builder =
module.getBuilder(phase);
1249 auto timeInt = CurrentTimeOp::create(builder, loc, module.arcContext);
1250 time = llhd::IntToTimeOp::create(builder, loc, timeInt);
1255 module.loweredValues[{op.getResult(), phase}] = time;
1259LogicalResult OpLowering::lower(sim::ClockedTerminateOp op) {
1260 if (phase != Phase::New)
1266 auto ifClockOp = createIfClockOp(op.getClock());
1270 OpBuilder::InsertionGuard guard(module.builder);
1271 module.builder.setInsertionPoint(ifClockOp.thenYield());
1273 auto loc = op.getLoc();
1274 Value cond = lowerValue(op.getCondition(), phase);
1276 return op.emitOpError(
"Failed to lower condition");
1280 return op.emitOpError(
"Failed to create condition block");
1282 module.builder.setInsertionPoint(ifOp.thenYield());
1283 arc::TerminateOp::create(module.builder, loc, module.arcContext,
1284 op.getSuccessAttr());
1293scf::IfOp OpLowering::createIfClockOp(Value clock) {
1294 auto &posedge =
module.loweredPosedges[clock];
1296 auto loweredClock = lowerValue(clock, Phase::New);
1299 posedge =
module.detectPosedge(loweredClock);
1312Value OpLowering::lowerValue(Value value, Phase phase) {
1314 if (
auto lowered = module.loweredValues.lookup({value, phase}))
1318 if (
auto arg = dyn_cast<BlockArgument>(value)) {
1319 if (arg.getOwner() != module.moduleOp.getBodyBlock()) {
1321 emitError(arg.getLoc()) <<
"block argument has not been lowered";
1326 auto state =
module.allocatedInputs[arg.getArgNumber()];
1327 return StateReadOp::create(module.getBuilder(phase), arg.getLoc(), state);
1332 auto result = cast<OpResult>(value);
1333 auto *op = result.getOwner();
1336 if (
auto instOp = dyn_cast<InstanceOp>(op))
1337 return lowerValue(instOp, result, phase);
1338 if (
auto instOp = dyn_cast<CoroutineInstanceOp>(op))
1339 return lowerValue(instOp, result, phase);
1340 if (
auto stateOp = dyn_cast<StateOp>(op))
1341 return lowerValue(stateOp, result, phase);
1342 if (
auto dpiOp = dyn_cast<sim::DPICallOp>(op); dpiOp && dpiOp.getClock())
1343 return lowerValue(dpiOp, result, phase);
1344 if (
auto readOp = dyn_cast<MemoryReadPortOp>(op))
1345 return lowerValue(readOp, result, phase);
1346 if (
auto initialOp = dyn_cast<seq::InitialOp>(op))
1347 return lowerValue(initialOp, result, phase);
1348 if (
auto castOp = dyn_cast<seq::FromImmutableOp>(op))
1349 return lowerValue(castOp, result, phase);
1355 addPending(op, phase);
1358 emitError(result.getLoc()) <<
"value has not been lowered";
1364Value OpLowering::lowerValue(InstanceOp op, OpResult result, Phase phase) {
1367 auto state =
module.getAllocatedState(result);
1368 return StateReadOp::create(module.getBuilder(phase), result.getLoc(), state);
1375Value OpLowering::lowerValue(CoroutineInstanceOp op, OpResult result,
1380 if (phase == Phase::New)
1381 addPending(op, Phase::New);
1386 if (phase == Phase::Old)
1387 assert(!module.loweredOps.contains({op, Phase::New}) &&
1388 "need old value but new value already written");
1390 auto state =
module.getAllocatedState(result);
1391 return StateReadOp::create(module.getBuilder(phase), result.getLoc(), state);
1398Value OpLowering::lowerValue(StateOp op, OpResult result, Phase phase) {
1402 if (phase == Phase::New || phase == Phase::Initial)
1403 addPending(op, phase);
1408 if (phase == Phase::Old)
1409 assert(!module.loweredOps.contains({op, Phase::New}) &&
1410 "need old value but new value already written");
1412 auto state =
module.getAllocatedState(result);
1413 return StateReadOp::create(module.getBuilder(phase), result.getLoc(), state);
1420Value OpLowering::lowerValue(sim::DPICallOp op, OpResult result, Phase phase) {
1424 if (phase == Phase::New || phase == Phase::Initial)
1425 addPending(op, phase);
1430 if (phase == Phase::Old)
1431 assert(!module.loweredOps.contains({op, Phase::New}) &&
1432 "need old value but new value already written");
1434 auto state =
module.getAllocatedState(result);
1435 return StateReadOp::create(module.getBuilder(phase), result.getLoc(), state);
1441Value OpLowering::lowerValue(MemoryReadPortOp op, OpResult result,
1443 auto memOp = op.getMemory().getDefiningOp<MemoryOp>();
1446 op->emitOpError() <<
"memory must be defined locally";
1450 auto address = lowerValue(op.getAddress(), phase);
1453 if (phase == Phase::New)
1454 addPending(memOp.getOperation(), Phase::New);
1460 if (phase == Phase::Old) {
1462 assert(!module.loweredOps.contains({memOp, Phase::New}) &&
1463 "need old memory value but new value already written");
1465 assert(phase == Phase::New);
1468 auto state =
module.getAllocatedState(memOp->getResult(0));
1469 return MemoryReadOp::create(module.getBuilder(phase), result.getLoc(), state,
1476Value OpLowering::lowerValue(seq::InitialOp op, OpResult result, Phase phase) {
1479 addPending(op, Phase::Initial);
1482 auto value =
module.loweredValues.lookup({result, Phase::Initial});
1484 emitError(result.getLoc()) <<
"value has not been lowered";
1490 if (phase == Phase::Initial)
1495 auto &state =
module.allocatedInitials[result];
1497 state = AllocStateOp::create(module.allocBuilder, value.getLoc(),
1498 StateType::get(value.getType()),
1500 OpBuilder::InsertionGuard guard(module.initialBuilder);
1501 module.initialBuilder.setInsertionPointAfterValue(value);
1502 StateWriteOp::create(module.initialBuilder, value.getLoc(), state, value);
1506 return StateReadOp::create(module.getBuilder(phase), state.getLoc(), state);
1510Value OpLowering::lowerValue(seq::FromImmutableOp op, OpResult result,
1512 return lowerValue(op.getInput(), phase);
1516void OpLowering::addPending(Value value, Phase phase) {
1517 auto *defOp = value.getDefiningOp();
1518 assert(defOp &&
"block args should never be marked as a dependency");
1519 addPending(defOp, phase);
1524void OpLowering::addPending(Operation *op, Phase phase) {
1525 auto pair = std::make_pair(op, phase);
1526 if (!module.loweredOps.contains(pair))
1527 if (!llvm::is_contained(pending, pair))
1528 pending.push_back(pair);
1536struct LowerStatePass :
public arc::impl::LowerStatePassBase<LowerStatePass> {
1537 using LowerStatePassBase::LowerStatePassBase;
1538 void runOnOperation()
override;
1542void LowerStatePass::runOnOperation() {
1543 auto op = getOperation();
1544 auto &symbolTable = getAnalysis<SymbolTable>();
1545 for (
auto moduleOp :
llvm::make_early_inc_range(op.getOps<
HWModuleOp>())) {
1546 if (failed(ModuleLowering(moduleOp, symbolTable).
run()))
1547 return signalPassFailure();
1551 for (
auto extModuleOp :
1555 auto uses = symbolTable.getSymbolUses(extModuleOp, op);
1556 if (!uses->empty()) {
1557 extModuleOp->emitError(
"Failed to remove external module because it is "
1558 "still referenced/instantiated");
1559 return signalPassFailure();
1561 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)