diff --git a/include/dynamatic/Dialect/Handshake/HandshakeAttributes.td b/include/dynamatic/Dialect/Handshake/HandshakeAttributes.td index 91d7018f2..50d1275c0 100644 --- a/include/dynamatic/Dialect/Handshake/HandshakeAttributes.td +++ b/include/dynamatic/Dialect/Handshake/HandshakeAttributes.td @@ -208,25 +208,29 @@ def MemDependenceAttr : Handshake_Attr<"MemDependence", "dep"> { The dependency is furthermore characterized by the loop depth at which the dependency is (`loopDepth`) and a list of dependence components (`components`) whose size indicates the number of commom loops surrounding - both operations. + both operations. Also, there is a boolean (`isActive`) that demonstrates + whether the dependency needs to be considered. An example of when there is + no need to consider a dependency is when it is guaranteed by data dependency. }]; let parameters = (ins "::mlir::StringAttr":$dstAccess, "unsigned":$loopDepth, - ArrayRefParameter<"::dynamatic::handshake::DependenceComponentAttr">:$components + ArrayRefParameter<"::dynamatic::handshake::DependenceComponentAttr">:$components, + "bool":$isActive ); let builders = [ AttrBuilder<(ins "::mlir::StringRef":$dstAccess, "unsigned":$loopDepth, - "::mlir::ArrayRef<::mlir::affine::DependenceComponent>":$components), [{ + "::mlir::ArrayRef<::mlir::affine::DependenceComponent>":$components, + "bool":$isActive), [{ SmallVector<::dynamatic::handshake::DependenceComponentAttr> compAttrs; for (auto &comp : components) compAttrs.push_back(::dynamatic::handshake::DependenceComponentAttr::get( context, comp.lb, comp.ub)); return $_get(context, ::mlir::StringAttr::get(context, dstAccess), - loopDepth, compAttrs); + loopDepth, compAttrs, isActive); }]> ]; diff --git a/include/dynamatic/Transforms/HandshakeAnalyzeLSQUsage.h b/include/dynamatic/Transforms/HandshakeInactivateEnforcedDeps.h similarity index 50% rename from include/dynamatic/Transforms/HandshakeAnalyzeLSQUsage.h rename to include/dynamatic/Transforms/HandshakeInactivateEnforcedDeps.h index c7b2ea5cb..c7cb4264e 100644 --- a/include/dynamatic/Transforms/HandshakeAnalyzeLSQUsage.h +++ b/include/dynamatic/Transforms/HandshakeInactivateEnforcedDeps.h @@ -1,4 +1,4 @@ -//===- HandshakeAnalyzeLSQUsage.h - LSQ flow analysis -----------*- C++ -*-===// +//===- HandshakeInactivateEnforcedDeps.h - Inactivate Deps ------*- C++ -*-===// // // Dynamatic is under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,23 +6,24 @@ // //===----------------------------------------------------------------------===// // -// This file declares the --handshake-analyze-lsq-usage pass. +// This file declares the --handshake-inactiveate-enfroced-deps pass. // //===----------------------------------------------------------------------===// -#ifndef DYNAMATIC_TRANSFORMS_HANDSHAKEANALYZELSQUSAGE_H -#define DYNAMATIC_TRANSFORMS_HANDSHAKEANALYZELSQUSAGE_H +#ifndef DYNAMATIC_TRANSFORMS_HANDSHAKEINACTIVATEENFORCEDDEPS_H +#define DYNAMATIC_TRANSFORMS_HANDSHAKEINACTIVATEENFORCEDDEPS_H #include "dynamatic/Support/DynamaticPass.h" namespace dynamatic { -#define GEN_PASS_DECL_HANDSHAKEANALYZELSQUSAGE -#define GEN_PASS_DEF_HANDSHAKEANALYZELSQUSAGE +#define GEN_PASS_DECL_HANDSHAKEINACTIVATEENFORCEDDEPS +#define GEN_PASS_DEF_HANDSHAKEINACTIVATEENFORCEDDEPS #include "dynamatic/Transforms/Passes.h.inc" -std::unique_ptr createHandshakeAnalyzeLSQUsage(); +std::unique_ptr +createHandshakeInactivateEnforcedDeps(); } // namespace dynamatic -#endif // DYNAMATIC_TRANSFORMS_HANDSHAKEANALYZELSQUSAGE_H +#endif // DYNAMATIC_TRANSFORMS_HANDSHAKEINACTIVATEENFORCEDDEPS_H diff --git a/include/dynamatic/Transforms/Passes.h b/include/dynamatic/Transforms/Passes.h index b6b5042cc..107ec9987 100644 --- a/include/dynamatic/Transforms/Passes.h +++ b/include/dynamatic/Transforms/Passes.h @@ -22,7 +22,7 @@ #include "dynamatic/Transforms/ForceMemoryInterface.h" #include "dynamatic/Transforms/FuncMaximizeSSA.h" #include "dynamatic/Transforms/FuncSetArgNames.h" -#include "dynamatic/Transforms/HandshakeAnalyzeLSQUsage.h" +#include "dynamatic/Transforms/HandshakeInactivateEnforcedDeps.h" #include "dynamatic/Transforms/HandshakeCanonicalize.h" #include "dynamatic/Transforms/HandshakeHoistExtInstances.h" #include "dynamatic/Transforms/HandshakeInferBasicBlocks.h" diff --git a/include/dynamatic/Transforms/Passes.td b/include/dynamatic/Transforms/Passes.td index 4b28f6bbf..32fdf46bd 100644 --- a/include/dynamatic/Transforms/Passes.td +++ b/include/dynamatic/Transforms/Passes.td @@ -109,22 +109,23 @@ def FuncSetArgNames : DynamaticPass<"func-set-arg-names"> { // Handshake passes //===----------------------------------------------------------------------===// -def HandshakeAnalyzeLSQUsage : DynamaticPass<"handshake-analyze-lsq-usage"> { - let summary = "Analyzes memory accesses to LSQs to find unnecessary ones"; +def HandshakeInactivateEnforcedDeps : +DynamaticPass<"handshake-inactivate-enforced-deps"> { + let summary = + "Marks the enforced dependencies as `inactive`"; let description = [{ Performs flow analysis to identify WAR dependencies between memory accesses that are ensured by the circuit's semantics and which therefore do not need - to be enforced by an LSQ. This allows to reduce the number of memory ports - that go to LSQs, thereby improving the circuit's performance and area thanks - to LSQ size reduction (or elimination, in some instances). The pass does not - modify the IR's structure, it only sets `handshake::MemInterfaceAttr` - attributes on memory ports to encode the analysis' results. + to be enforced by an LSQ. It also finds the WAW dependencies between an + operation and itself. The pass then modifies the IR's structure by setting + these dependencies as inactive and determining + `handshake::MemInterfaceAttr` attributes on memory ports at the end. The pass requires that all eligible operations within Handshake functions are tagged with the basic block they belong to, and that all memory access operations are uniquely named. }]; - let constructor = "dynamatic::createHandshakeAnalyzeLSQUsage()"; + let constructor = "dynamatic::createHandshakeInactivateEnforcedDeps()"; } def HandshakeCanonicalize : DynamaticPass<"handshake-canonicalize"> { diff --git a/lib/Dialect/Handshake/HandshakeAttributes.cpp b/lib/Dialect/Handshake/HandshakeAttributes.cpp index f5262c0d5..b0b743579 100644 --- a/lib/Dialect/Handshake/HandshakeAttributes.cpp +++ b/lib/Dialect/Handshake/HandshakeAttributes.cpp @@ -90,7 +90,12 @@ static ParseResult parseDependenceComponent(AsmParser &odsParser, void MemDependenceAttr::print(AsmPrinter &odsPrinter) const { // Print destination memory access and loop depth odsPrinter << "<\"" << getDstAccess().str() << "\" (" << getLoopDepth() - << ")"; + << ") "; + std::string isActiveStr = "inactive"; + if (getIsActive()) + isActiveStr = "active"; + + odsPrinter << "\"" << isActiveStr << "\""; // Print dependence components, if present auto components = getComponents(); @@ -122,6 +127,19 @@ Attribute MemDependenceAttr::parse(AsmParser &odsParser, Type odsType) { odsParser.parseRParen()) return nullptr; + // Parse isActive + std::string boolStr; + if (odsParser.parseString(&boolStr)) + return nullptr; + bool isActive; + if (boolStr == "active") + isActive = true; + else if (boolStr == "inactive") + isActive = false; + else + return nullptr; + + // Parse dependence components if present SmallVector components; if (!odsParser.parseOptionalLSquare()) { @@ -147,7 +165,8 @@ Attribute MemDependenceAttr::parse(AsmParser &odsParser, Type odsType) { if (odsParser.parseGreater()) return nullptr; - return MemDependenceAttr::get(ctx, dstAccess, loopDepth, components); + return MemDependenceAttr::get(ctx, dstAccess, loopDepth, components, + isActive); } //===----------------------------------------------------------------------===// diff --git a/lib/Dialect/Handshake/MemoryInterfaces.cpp b/lib/Dialect/Handshake/MemoryInterfaces.cpp index a78148c0f..f2f7e72d6 100644 --- a/lib/Dialect/Handshake/MemoryInterfaces.cpp +++ b/lib/Dialect/Handshake/MemoryInterfaces.cpp @@ -70,7 +70,7 @@ bool MemoryOpLowering::renameDependencies(Operation *topLevelOp) { if (opWasReplaced) { StringAttr newName = StringAttr::get(ctx, replacedName->second); newMemDeps.push_back(MemDependenceAttr::get( - ctx, newName, oldDep.getLoopDepth(), oldDep.getComponents())); + ctx, newName, oldDep.getLoopDepth(), oldDep.getComponents(), oldDep.getIsActive())); } else { newMemDeps.push_back(oldDep); } diff --git a/lib/Support/CFG.cpp b/lib/Support/CFG.cpp index c2c9aeabf..5b15c5eb6 100644 --- a/lib/Support/CFG.cpp +++ b/lib/Support/CFG.cpp @@ -595,7 +595,7 @@ static GIIDStatus isGIIDRec(Value predecessor, OpOperand &oprd, [&](handshake::ConditionalBranchOp condBrOp) { // The data operand or the condition operand must depend on the // predecessor - return foldGIIDStatusAnd(recurse, condBrOp->getOperands()); + return foldGIIDStatusOr(recurse, condBrOp->getOperands()); }) .Case([&](auto) { // The data input on the path must depend on the predecessor @@ -632,10 +632,11 @@ static GIIDStatus isGIIDRec(Value predecessor, OpOperand &oprd, handshake::BranchOp, handshake::AddIOp, handshake::AndIOp, handshake::CmpIOp, handshake::DivSIOp, handshake::DivUIOp, handshake::ExtSIOp, handshake::ExtUIOp, handshake::MulIOp, - handshake::OrIOp, handshake::ShLIOp, handshake::ShRUIOp, - handshake::SubIOp, handshake::TruncIOp, handshake::XOrIOp, - handshake::AddFOp, handshake::CmpFOp, handshake::DivFOp, - handshake::MulFOp, handshake::SubFOp>([&](auto) { + handshake::OrIOp, handshake::AddFOp, handshake::CmpFOp, + handshake::DivFOp, handshake::MulFOp, handshake::ShLIOp, + handshake::ShRSIOp, handshake::ShRUIOp, handshake::SubFOp, + handshake::SubIOp, handshake::TruncIOp, handshake::TruncFOp, + handshake::XOrIOp, handshake::SIToFPOp, handshake::FPToSIOp>([&](auto) { // At least one operand must depend on the predecessor return foldGIIDStatusOr(recurse, defOp->getOperands()); }) diff --git a/lib/Transforms/CMakeLists.txt b/lib/Transforms/CMakeLists.txt index aa7987463..7f0493fd4 100644 --- a/lib/Transforms/CMakeLists.txt +++ b/lib/Transforms/CMakeLists.txt @@ -5,7 +5,7 @@ add_dynamatic_library(DynamaticTransforms ForceMemoryInterface.cpp FuncMaximizeSSA.cpp FuncSetArgNames.cpp - HandshakeAnalyzeLSQUsage.cpp + HandshakeInactivateEnforcedDeps.cpp HandshakeCanonicalize.cpp HandshakeHoistExtInstances.cpp HandshakeMaterialize.cpp diff --git a/lib/Transforms/HandshakeAnalyzeLSQUsage.cpp b/lib/Transforms/HandshakeInactivateEnforcedDeps.cpp similarity index 57% rename from lib/Transforms/HandshakeAnalyzeLSQUsage.cpp rename to lib/Transforms/HandshakeInactivateEnforcedDeps.cpp index 5e142bd9e..a9db3f58f 100644 --- a/lib/Transforms/HandshakeAnalyzeLSQUsage.cpp +++ b/lib/Transforms/HandshakeInactivateEnforcedDeps.cpp @@ -1,4 +1,4 @@ -//===- HandshakeAnalyzeLSQUsage.cpp - LSQ flow analysis ---------*- C++ -*-===// +//===- HandshakeInactivateEnforcedDeps.h - Inactivate Deps ------*- C++ -*-===// // // Dynamatic is under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,12 +6,12 @@ // //===----------------------------------------------------------------------===// // -// Implements the --handshake-analyze-lsq-usage pass, using the logic +// Implements the --handshake-inactivate-enforced-deps pass, using the logic // introduced in https://ieeexplore.ieee.org/document/8977873. // //===----------------------------------------------------------------------===// -#include "dynamatic/Transforms/HandshakeAnalyzeLSQUsage.h" +#include "dynamatic/Transforms/HandshakeInactivateEnforcedDeps.h" #include "dynamatic/Analysis/NameAnalysis.h" #include "dynamatic/Dialect/Handshake/HandshakeAttributes.h" #include "dynamatic/Dialect/Handshake/HandshakeOps.h" @@ -23,19 +23,22 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Debug.h" -#define DEBUG_TYPE "handshake-analyze-lsq-usage" +#define DEBUG_TYPE "handshake-inactivate-enforced-deps" using namespace mlir; using namespace dynamatic; using namespace dynamatic::handshake; +using DependencyMap = DenseMap>; + namespace { -/// Simple pass driver for the LSQ usage analysis pass. Does not modify the IR -/// beyong setting `handshake::MemInterfaceAttr` attributes on memory ports. -struct HandshakeAnalyzeLSQUsagePass - : public dynamatic::impl::HandshakeAnalyzeLSQUsageBase< - HandshakeAnalyzeLSQUsagePass> { +/// Simple pass driver for the Inactivate Enforced Dependencies pass. +/// Modifies the IR by marking the enforced dependencies as inactive and then +/// setting `handshake::MemInterfaceAttr` attributes on memory ports. +struct HandshakeInactivateEnforcedDepsPass + : public dynamatic::impl::HandshakeInactivateEnforcedDepsBase< + HandshakeInactivateEnforcedDepsPass> { void runDynamaticPass() override; @@ -51,7 +54,7 @@ struct HandshakeAnalyzeLSQUsagePass }; } // namespace -void HandshakeAnalyzeLSQUsagePass::runDynamaticPass() { +void HandshakeInactivateEnforcedDepsPass::runDynamaticPass() { mlir::ModuleOp modOp = getOperation(); // Check that memory access ports are named @@ -83,7 +86,8 @@ void HandshakeAnalyzeLSQUsagePass::runDynamaticPass() { analyzeFunction(funcOp); } -void HandshakeAnalyzeLSQUsagePass::analyzeFunction(handshake::FuncOp funcOp) { +void HandshakeInactivateEnforcedDepsPass::analyzeFunction( + handshake::FuncOp funcOp) { for (BlockArgument arg : funcOp.getArguments()) { HandshakeCFG cfg(funcOp); if (auto memref = dyn_cast>(arg)) @@ -91,29 +95,7 @@ void HandshakeAnalyzeLSQUsagePass::analyzeFunction(handshake::FuncOp funcOp) { } } -/// Determines whether there exists any RAW dependency between the load and the -/// stores. -static bool hasRAW(handshake::LoadOp loadOp, - DenseSet &storeOps) { - StringRef loadName = getUniqueName(loadOp); - for (handshake::StoreOp storeOp : storeOps) { - if (auto deps = getDialectAttr(storeOp)) { - for (MemDependenceAttr dependency : deps.getDependencies()) { - if (dependency.getDstAccess() == loadName) { - LLVM_DEBUG({ - llvm::dbgs() << "\tKeeping '" << loadName - << "': RAW dependency with '" << getUniqueName(storeOp) - << "'\n"; - }); - return true; - } - } - } - } - return false; -} - -/// Determines whether the load is globally in-order independent (GIID) on the +/// Determines whether the load is globally in-order dependent (GIID) on the /// store along all non-cyclic CFG paths between them. static bool isStoreGIIDOnLoad(handshake::LoadOp loadOp, handshake::StoreOp storeOp, HandshakeCFG &cfg) { @@ -136,12 +118,20 @@ static bool isStoreGIIDOnLoad(handshake::LoadOp loadOp, }); } -/// Determines whether the load is globally in-order independent (GIID) on all -/// stores with which it has a WAR dependency along all non-cyclic CFG paths -/// between them. -static bool hasEnforcedWARs(handshake::LoadOp loadOp, - DenseSet &storeOps, - HandshakeCFG &cfg) { +/// Returns the inactivated version of the dependency. +static MemDependenceAttr +getInactivatedDependency(MemDependenceAttr dependency) { + MLIRContext *ctx = dependency.getContext(); + return MemDependenceAttr::get( + ctx, dependency.getDstAccess(), dependency.getLoopDepth(), + dependency.getComponents(), false); +} + +/// Inactivates the dependencies that are enforced by cheking whether the load +/// is globally [Instruction] in-order dependent (GIID) on the store or not. +static void inactivateEnforcedWARs(DenseSet &loadOps, + DenseSet &storeOps, + DependencyMap &opDeps, HandshakeCFG &cfg) { DenseMap storesByName; for (handshake::StoreOp storeOp : storeOps) storesByName.insert({getUniqueName(storeOp), storeOp}); @@ -149,39 +139,98 @@ static bool hasEnforcedWARs(handshake::LoadOp loadOp, // We only need to check stores that depend on the load (WAR dependencies) as // others are already provably independent. We may check a single store // multiple times if it depends on the load at multiple loop depths - if (auto deps = getDialectAttr(loadOp)) { - for (MemDependenceAttr dependency : deps.getDependencies()) { - auto storeOp = storesByName.at(dependency.getDstAccess()); - if (!isStoreGIIDOnLoad(loadOp, storeOp, cfg)) { - LLVM_DEBUG({ - llvm::dbgs() << "\tKeeping '" << getUniqueName(loadOp) - << "': non-enforced WAR with '" << getUniqueName(storeOp) - << "'\n"; - }); - return false; + for (handshake::LoadOp loadOp : loadOps) { + if (auto deps = getDialectAttr(loadOp)) { + for (MemDependenceAttr dependency : deps.getDependencies()) { + if (!dependency.getIsActive()) + continue; + auto storeOp = storesByName.at(dependency.getDstAccess()); + // if the laod is GIID which means there is a data dependency, + // the dependency should be inactivated + if (isStoreGIIDOnLoad(loadOp, storeOp, cfg)) + opDeps[loadOp].push_back(getInactivatedDependency(dependency)); + else + opDeps[loadOp].push_back(dependency); + } + } + } +} + +/// Replaces the memory dependence array attribute with the dependencies +/// given in the dictionary `opDeps`. +static void changeOpDeps(DependencyMap &opDeps, MLIRContext *ctx) { + for (auto &[op, deps] : opDeps) + setDialectAttr(op, ctx, deps); +} + +/// Inactivates the WAW dependencies between an operation and itself. +static void inactivateEnforcedWAWs( + DenseSet &storeOps, + DenseMap> &opDeps) { + for (handshake::StoreOp storeOp : storeOps) { + if (auto deps = getDialectAttr(storeOp)) { + StringRef storeName = getUniqueName(storeOp); + for (MemDependenceAttr dependency : deps.getDependencies()) { + if (!dependency.getIsActive()) + continue; + StringRef dstName = dependency.getDstAccess(); + + // a WAW dependency between an operation and itself may be ignored. + if (storeName == dstName) + opDeps[storeOp].push_back(getInactivatedDependency(dependency)); + else + opDeps[storeOp].push_back(dependency); } } } - return true; +} + +/// Gets the set of all load/store operations and returns the subset of +/// operations that are involved in at least one active dependency. +static DenseSet +getOpsWithNonEnforcedDeps(DenseSet &loadStoreOps) { + DenseMap nameToOpMapping; + for (Operation *op : loadStoreOps) { + StringRef name = getUniqueName(op); + nameToOpMapping[name] = op; + } + + DenseSet opsWithNonEnforcedDeps; + bool hasAtLeastOneActive; + + for (Operation *op : loadStoreOps) { + hasAtLeastOneActive = false; + if (auto deps = getDialectAttr(op)) { + for (MemDependenceAttr dependency : deps.getDependencies()) { + if (!dependency.getIsActive()) + continue; + hasAtLeastOneActive = true; + Operation *dstOp = nameToOpMapping[dependency.getDstAccess()]; + opsWithNonEnforcedDeps.insert(dstOp); + } + } + if (hasAtLeastOneActive) + opsWithNonEnforcedDeps.insert(op); + } + return opsWithNonEnforcedDeps; } /// Mark all accesses with the `MemInterfaceAttr`, indicating whether they /// should connect to an MC or LSQ depending on their dependencies with other /// accesses. -template -static void markLSQPorts(const DenseSet &accesses, - const DenseSet &dependentAccesses, +static void markLSQPorts(const DenseSet allOps, + const DenseSet opsWithNonEnforcedDeps, const DenseMap &groupMap, MLIRContext *ctx) { - for (Op accessOp : accesses) { - if (dependentAccesses.contains(accessOp)) - setDialectAttr(accessOp, ctx, groupMap.at(accessOp)); + for (Operation *op : allOps) { + if (opsWithNonEnforcedDeps.contains(op)) + setDialectAttr(op, ctx, groupMap.at(op)); else - setDialectAttr(accessOp, ctx); + setDialectAttr(op, ctx); } }; -void HandshakeAnalyzeLSQUsagePass::analyzeMemRef( +void HandshakeInactivateEnforcedDepsPass::analyzeMemRef( handshake::FuncOp funcOp, TypedValue memref, HandshakeCFG &cfg) { LLVM_DEBUG({ @@ -224,11 +273,13 @@ void HandshakeAnalyzeLSQUsagePass::analyzeMemRef( // Identify load and store accesses to the LSQ DenseSet lsqLoadOps; DenseSet lsqStoreOps; + DenseSet loadStoreOps; DenseMap groupMap; LSQPorts lsqPorts = lsqOp.getPorts(); for (LSQGroup &group : lsqPorts.getGroups()) { for (MemoryPort &port : group->accessPorts) { groupMap.insert({port.portOp, group.groupID}); + loadStoreOps.insert(port.portOp); if (auto loadOp = dyn_cast(port.portOp)) lsqLoadOps.insert(loadOp); else @@ -236,78 +287,20 @@ void HandshakeAnalyzeLSQUsagePass::analyzeMemRef( } } - NameAnalysis &namer = getAnalysis(); - - // Check whether we can prove independence of some loads w.r.t. other accesses - DenseSet dependentLoads; - DenseSet dependentStores; - for (handshake::LoadOp loadOp : lsqLoadOps) { - // Loads with no RAW dependencies and which satisfy the GIID property with - // all stores they have a dependency with may be removed - if (hasRAW(loadOp, lsqStoreOps) || - !hasEnforcedWARs(loadOp, lsqStoreOps, cfg)) { - dependentLoads.insert(loadOp); - - // All stores involved in a WAR with the load are still dependent - if (auto deps = getDialectAttr(loadOp)) { - for (MemDependenceAttr dependency : deps.getDependencies()) { - Operation *dstOp = namer.getOp(dependency.getDstAccess()); - if (auto storeOp = dyn_cast(dstOp)) - dependentStores.insert(storeOp); - } - } - } else { - LLVM_DEBUG({ - llvm::dbgs() << "\tRerouting '" << getUniqueName(loadOp) << "' to MC\n"; - }); - } - } - - // Stores involed in a RAW ar WAW dependency with another operation are sill - // dependent - for (handshake::StoreOp storeOp : lsqStoreOps) { - auto deps = getDialectAttr(storeOp); - if (!deps) - continue; - - // Iterate over all RAW and WAW dependencies to determine those which must - // still be honored by an LSQ - StringRef storeName = getUniqueName(storeOp); - for (MemDependenceAttr dependency : deps.getDependencies()) { - StringRef dstName = dependency.getDstAccess(); - - // WAW dependencies on the same operation can be ignored, they are - // enforced automatically by the dataflow circuit's construction - if (storeName == dstName) - continue; - - // The dependency must still be honored - Operation *dstOp = namer.getOp(dstName); - dependentStores.insert(storeOp); - if (auto dstStoreOp = dyn_cast(dstOp)) - dependentStores.insert(dstStoreOp); - } - } + DependencyMap opDeps; + inactivateEnforcedWARs(lsqLoadOps, lsqStoreOps, opDeps, cfg); + inactivateEnforcedWAWs(lsqStoreOps, opDeps); + changeOpDeps(opDeps, ctx); - LLVM_DEBUG({ - for (handshake::StoreOp storeOp : lsqStoreOps) { - if (dependentStores.contains(storeOp)) { - llvm::dbgs() << "\tKeeping '" << getUniqueName(storeOp) - << "': WAW or RAW dependency with other access\n"; - } else { - llvm::dbgs() << "\tRerouting '" << getUniqueName(storeOp) - << "' to MC\n"; - } - } - }); + DenseSet opsWithNonEnforcedDeps; + opsWithNonEnforcedDeps = getOpsWithNonEnforcedDeps(loadStoreOps); // Tag LSQ access ports with the interface they should actually connect to, // which may be different from the one they currently connect to - markLSQPorts(lsqLoadOps, dependentLoads, groupMap, ctx); - markLSQPorts(lsqStoreOps, dependentStores, groupMap, ctx); + markLSQPorts(loadStoreOps, opsWithNonEnforcedDeps, groupMap, ctx); } std::unique_ptr -dynamatic::createHandshakeAnalyzeLSQUsage() { - return std::make_unique(); -} +dynamatic::createHandshakeInactivateEnforcedDeps() { + return std::make_unique(); +} \ No newline at end of file diff --git a/lib/Transforms/MarkMemoryDependencies.cpp b/lib/Transforms/MarkMemoryDependencies.cpp index 0f2f26afd..ee35459ea 100644 --- a/lib/Transforms/MarkMemoryDependencies.cpp +++ b/lib/Transforms/MarkMemoryDependencies.cpp @@ -163,8 +163,9 @@ LogicalResult MarkMemoryDependenciesPass::checkAffineAccessPair( StringRef dstName = namer.getName(dstOp); if (result.value == DependenceResult::HasDependence) { // Add the dependence to the source operation + // At this point the dependency should be initialized as active opDeps[srcOp].push_back( - MemDependenceAttr::get(ctx, dstName, loopDepth, components)); + MemDependenceAttr::get(ctx, dstName, loopDepth, components, true)); } else if (result.value == DependenceResult::Failure) { return srcOp->emitError() << "Dependence check failed with memory access '" << dstName @@ -185,8 +186,9 @@ LogicalResult MarkMemoryDependenciesPass::checkNonAffineAccessPair( NameAnalysis &namer = getAnalysis(); StringRef dstName = namer.getName(dstOp); + MLIRContext* ctx = &getContext(); opDeps[srcOp].push_back(MemDependenceAttr::get( - &getContext(), dstName, 0, ArrayRef{})); + ctx, dstName, 0, ArrayRef{}, true)); return success(); } diff --git a/lib/Transforms/MarkMemoryInterfaces.cpp b/lib/Transforms/MarkMemoryInterfaces.cpp index 079881e7e..6de53ec99 100644 --- a/lib/Transforms/MarkMemoryInterfaces.cpp +++ b/lib/Transforms/MarkMemoryInterfaces.cpp @@ -132,6 +132,8 @@ void MarkMemoryInterfacesPass::markMemoryInterfaces(func::FuncOp funcOp) { bool connectToMC = true; if (auto allDeps = getDialectAttr(op)) { for (MemDependenceAttr memDep : allDeps.getDependencies()) { + if (!memDep.getIsActive()) + continue; // Both the source and destination operation need to connect to an LSQ StringRef dstOpName = memDep.getDstAccess(); if (srcOpName == dstOpName) { diff --git a/tools/dynamatic/scripts/compile.sh b/tools/dynamatic/scripts/compile.sh index a884baf27..624967b6d 100755 --- a/tools/dynamatic/scripts/compile.sh +++ b/tools/dynamatic/scripts/compile.sh @@ -119,7 +119,7 @@ exit_on_fail "Failed to compile cf to handshake" "Compiled cf to handshake" # handshake transformations "$DYNAMATIC_OPT_BIN" "$F_HANDSHAKE" \ - --handshake-analyze-lsq-usage --handshake-replace-memory-interfaces \ + --handshake-inactivate-enforced-deps --handshake-replace-memory-interfaces \ --handshake-minimize-cst-width --handshake-optimize-bitwidths \ --handshake-materialize --handshake-infer-basic-blocks \ > "$F_HANDSHAKE_TRANSFORMED"