Skip to content
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
53ca2ff
algorithm for finding IOGs
Apr 13, 2026
e56f380
annotating invariant 6: Consecutive tokens within an IOG must have an…
Apr 16, 2026
8a4696f
invariant now works for any slot within an operation (rather than jus…
Apr 16, 2026
935a000
small cleanup of IOG generation code with better naming
Apr 16, 2026
e742725
extraction of IOG finding into separate file
Apr 16, 2026
b9f632b
fixed IOG path code to support multiple possible paths
Apr 16, 2026
3c64778
handle pipeline slots as well as normal slots in invariant 6
Apr 17, 2026
b4e5fd4
added some more explaining comments for the IOG finding algorithm
Apr 22, 2026
f52748b
start of IOG-single-token invariant:
Apr 25, 2026
5accc26
getAllSlotsOfOperation properly handles load's hidden slot in MC
Apr 25, 2026
c05778f
slot of terminating sink is annotatable
Apr 25, 2026
82ec8c6
change testbench generator to work with IOG single-token invariant
Apr 25, 2026
eb3e39f
Merge branch 'main' into invariant5
Apr 29, 2026
dcb74b6
change testbench to:
Apr 29, 2026
bb75abf
set default testbench to not use dead buffers
Apr 29, 2026
f437c3a
format
Apr 29, 2026
bc68eef
change types.mlir unit test
Apr 30, 2026
789d98b
attempt fix types.mlir
Apr 30, 2026
39adf31
more comments
May 6, 2026
7418c16
fixed an issue that made smv module arguments mismatch
May 6, 2026
daa3dc0
Merge branch 'main' into invariant5
May 6, 2026
df03ade
replace template with ValueRange
May 6, 2026
e1401b5
Add dead buffer unit to ends of IOGs:
May 8, 2026
b69f6f6
Merge branch 'main' into invariant5
Basmet0 May 8, 2026
4dbefde
fixed some bugs due to merge
Basmet0 May 8, 2026
d0d5f97
fixed test case to without attribute
Basmet0 May 8, 2026
833fcef
flow expresssions now handle dead buffer op
May 9, 2026
5ed765b
resolved merge-conflicts
May 9, 2026
20e867b
Merge branch 'main' into invariant5
May 9, 2026
c239467
more logical TokenCountNamer which does not need special smv definitions
May 9, 2026
b227f81
formatting
May 9, 2026
f33ef2f
Merge branch 'main' into invariant5
May 11, 2026
4ee3a5d
comments
May 11, 2026
3fce970
small fixes for clarity
May 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ struct SmvTestbenchConfig {
// Determines if the outputs are synchronized with a join or if they are
// consumed by sinks
bool syncOutput = false;

// Determines if the output is modelled as a dead buffer (i.e. accepts one
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the output or all outputs? Technically we can choose whether to have a single output or multiple outputs

// token, then the output is no longer ready)
bool deadBufferOutput = false;
};

// Create a wrapper for the provided SMV file.
Expand Down Expand Up @@ -157,4 +161,4 @@ MODULE sink(ins_valid)
DEFINE ins_ready := TRUE;)DELIM";

} // namespace dynamatic::experimental
#endif // DYNAMATIC_EXPERIMENTAL_ELASTIC_MITER_CREATE_FORMAL_TESTBENCH_H
#endif // DYNAMATIC_EXPERIMENTAL_ELASTIC_MITER_CREATE_FORMAL_TESTBENCH_H
66 changes: 66 additions & 0 deletions experimental/include/experimental/Support/FormalProperty.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class FormalProperty {
EagerForkNotAllOutputSent,
CopiedSlotsOfActiveForksAreFull,
ReconvergentPathFlow,
IOGSingleToken,
IOGConsecutiveTokens,
};

TAG getTag() const { return tag; }
Expand Down Expand Up @@ -271,6 +273,70 @@ class ReconvergentPathFlow : public FormalProperty {
inline static const StringLiteral EQUATIONS_LIT = "equations";
};

// An IOG contains a single token at the start (at the entry), and no other
// tokens will ever enter or leave. Tokens can be duplicated by eager forks, but
// they keep track of this within their `sent` state. Because of this, the
// number of occupied slots within an IOG is equal to one plus the number of
// duplicated tokens by fork.
class IOGSingleToken : public FormalProperty {
Comment thread
Jiahui17 marked this conversation as resolved.
public:
llvm::json::Value extraInfoToJSON() const override;
static std::unique_ptr<IOGSingleToken>
fromJSON(const llvm::json::Value &value, llvm::json::Path path);

IOGSingleToken() = default;
IOGSingleToken(unsigned long id, TAG tag,
std::vector<std::unique_ptr<InternalStateNamer>> slots,
std::vector<EagerForkSentNamer> forks)
: FormalProperty(id, tag, TYPE::IOGSingleToken), slots(std::move(slots)),
forks(std::move(forks)) {};
~IOGSingleToken() = default;

static bool classof(const FormalProperty *fp) {
return fp->getType() == TYPE::IOGSingleToken;
}

std::vector<std::unique_ptr<InternalStateNamer>> slots;
std::vector<EagerForkSentNamer> forks;

private:
inline static const StringLiteral SLOTS_LIT = "slots";
inline static const StringLiteral FORKS_LIT = "forks";
};

// Within an IOG, multiple slots can be occupied when forks duplicate tokens.
// Whereas the previous invariant only states that there needs to be an active
// fork somewhere, this invariant determines where the active fork could be: If
// any two slots are occupied, there must be at least one path connecting them,
// and for some path p, there must be an active fork along p with the start of p
// as the copied slot (i.e. no other slots between the fork and the starting
// slot)
class IOGConsecutiveTokens : public FormalProperty {
Comment thread
Jiahui17 marked this conversation as resolved.
public:
llvm::json::Value extraInfoToJSON() const override;
static std::unique_ptr<IOGConsecutiveTokens>
fromJSON(const llvm::json::Value &value, llvm::json::Path path);

IOGConsecutiveTokens() = default;
IOGConsecutiveTokens(unsigned long id, TAG tag, const TokenCountNamer &slot1,
const TokenCountNamer &slot2,
std::vector<EagerForkSentNamer> sents);
~IOGConsecutiveTokens() = default;

static bool classof(const FormalProperty *fp) {
return fp->getType() == TYPE::IOGConsecutiveTokens;
}

TokenCountNamer slot1;
TokenCountNamer slot2;
std::vector<EagerForkSentNamer> sents;

private:
inline static const StringLiteral SLOT1_LIT = "slot1";
inline static const StringLiteral SLOT2_LIT = "slot2";
inline static const StringLiteral SENTS_LIT = "sents";
};

class FormalPropertyTable {
public:
FormalPropertyTable() = default;
Expand Down
79 changes: 79 additions & 0 deletions experimental/include/experimental/Support/IOG.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#pragma once

#include "dynamatic/Analysis/NameAnalysis.h"
#include "dynamatic/Support/LLVM.h"
#include "mlir/IR/Value.h"
#include <unordered_set>

namespace dynamatic {
struct IOG;
struct IOGPathSet;

// An IOG path set is a structure that describes a set of paths between two
// operations: For any unit `u` within the IOG, it says whether there exists a
// path going through `u` or not.
struct IOGPathSet {
Comment thread
Jiahui17 marked this conversation as resolved.
IOGPathSet(const IOG &iog, Operation *start, Operation *end);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be a class method of IOG?

iog.getPathSet(start, end)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are identical. I prefer the one I chose.
IOGPathSet paths(iog, start, end) vs auto paths = iog.getPathSet(start, end)


std::unordered_set<Operation *> units;
Operation *start;
Operation *end;
};

// An In-order graph (IOG) of a dataflow circuit is a subgraph of the dataflow
// circuit. It contains one and only one entry channel (i.e. an argument of a
// FuncOp) which can reach all other operations using only channels part of the
// IOG. The IOG does not lose or gain any tokens: For any merge/mux, all the
// data inputs must be contained in the IOG, and for any fork, only a single
// output can be part of the IOG. Similarly, all branch outputs are part of the
// IOG, and for each join-operation, only a single input is part of the IOG.
// This way, there is a fixed number of tokens within the IOG.
struct IOG {
Comment thread
Jiahui17 marked this conversation as resolved.
IOG() = default;
std::unordered_set<Operation *> units;
llvm::DenseSet<mlir::Value> channels;
BlockArgument entry;

inline bool contains(Operation *op) const {
auto iter = units.find(op);
return iter != units.end();
}

inline bool contains(mlir::Value channel) const {
auto iter = channels.find(channel);
return iter != channels.end();
}

void debug() const {
std::vector<mlir::Value> stack;
llvm::DenseSet<mlir::Value> visited;
stack.push_back(entry);
visited.insert(entry);
while (!stack.empty()) {
mlir::Value channel = stack.back();
stack.pop_back();
Operation *prev = channel.getDefiningOp();
if (prev == nullptr) {
llvm::errs() << "entry";
} else {
llvm::errs() << getUniqueName(prev);
}
Operation *next = channel.getUses().begin()->getOwner();
assert(next);
assert(contains(next));
llvm::errs() << " -> " << getUniqueName(next) << "\n";
for (mlir::Value out : next->getResults()) {
if (auto iter = visited.find(out); iter != visited.end())
continue;
if (contains(out)) {
visited.insert(out);
stack.push_back(out);
}
}
}
}
};

std::vector<IOG> findAllIOGs(ModuleOp modOp);

} // namespace dynamatic
Loading
Loading