Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
108 changes: 108 additions & 0 deletions include/loom/Visualization/DotStyleConfig.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//===- DotStyleConfig.h - Centralized DOT Visualization Styles --*- C++ -*-===//
//
// Centralized styling configuration for all DOT graph visualizations.
// This file defines the SINGLE source of truth for all operator type →
// (shape, color, border) mappings used by handshake CDFG visualizations.
//
//===----------------------------------------------------------------------===//

#ifndef LOOM_VISUALIZATION_DOTSTYLECONFIG_H
#define LOOM_VISUALIZATION_DOTSTYLECONFIG_H

#include "llvm/ADT/StringRef.h"
#include <string>

namespace loom {
namespace visualization {

//===----------------------------------------------------------------------===//
// Node Style Configuration
//===----------------------------------------------------------------------===//

/// Complete visual style for a DOT node
struct DotNodeStyle {
std::string shape; // DOT shape (box, circle, triangle, etc.)
std::string fillColor; // Fill color
std::string fontColor; // Font color (default: black)
std::string borderStyle; // Border line style (solid, dashed, dotted)
double borderWidth; // Border width
std::string extraAttrs; // Extra DOT attributes (e.g., width, height)

DotNodeStyle(llvm::StringRef s = "box", llvm::StringRef fc = "white",
llvm::StringRef fnc = "black", llvm::StringRef bs = "solid",
double bw = 1.0, llvm::StringRef extra = "")
: shape(s), fillColor(fc), fontColor(fnc), borderStyle(bs),
borderWidth(bw), extraAttrs(extra) {}
};

/// Complete visual style for a DOT edge
struct DotEdgeStyle {
std::string color; // Edge color
std::string style; // Edge style (solid, dashed, dotted)
double width; // Edge width
bool showLabel; // Whether to show label (port mapping)

DotEdgeStyle(llvm::StringRef c = "black", llvm::StringRef s = "solid",
double w = 1.0, bool label = false)
: color(c), style(s), width(w), showLabel(label) {}
};

//===----------------------------------------------------------------------===//
// Centralized Style Provider
//===----------------------------------------------------------------------===//

class DotStyleConfig {
public:
/// Get node style for a given operation
/// \param opName Operation name (e.g., "arith.addi", "handshake.fork")
/// \param isMapped Whether this node is mapped (for hardware graphs)
/// \param isFuncArg Whether this is a function argument
/// \param isReturn Whether this is a return operation
static DotNodeStyle getNodeStyle(llvm::StringRef opName, bool isMapped = true,
bool isFuncArg = false,
bool isReturn = false);

/// Get edge style
/// \param isMapped Whether this edge is mapped
/// \param showPortLabel Whether to show port mapping label
static DotEdgeStyle getEdgeStyle(bool isMapped = true,
bool showPortLabel = false);

/// Get fallback style for unhandled operations
static DotNodeStyle getFallbackStyle();

/// Format port mapping label
/// \param srcPort Source port index
/// \param dstPort Destination port index
static std::string formatPortLabel(int srcPort, int dstPort);

private:
/// Check if operation is from arith dialect
static bool isArithOp(llvm::StringRef opName);

/// Check if operation is from math dialect
static bool isMathOp(llvm::StringRef opName);

/// Check if operation is an LLVM intrinsic
static bool isLLVMIntrinsic(llvm::StringRef opName);

/// Check if operation is UB dialect
static bool isUBOp(llvm::StringRef opName);

/// Check if operation is handshake memory-related
static bool isHandshakeMemoryOp(llvm::StringRef opName);

/// Check if operation is handshake.fork
static bool isHandshakeFork(llvm::StringRef opName);

/// Check if operation is handshake.mux
static bool isHandshakeMux(llvm::StringRef opName);

/// Get style for handshake memory operations
static DotNodeStyle getHandshakeMemoryStyle(llvm::StringRef opName);
};

} // namespace visualization
} // namespace loom

#endif // LOOM_VISUALIZATION_DOTSTYLECONFIG_H
23 changes: 23 additions & 0 deletions include/loom/Visualization/HandshakeToDot.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//===- HandshakeToDot.h - Handshake MLIR to DOT Export ----------*- C++ -*-===//
//
// Exports handshake MLIR modules to Graphviz DOT format for visualization.
//
//===----------------------------------------------------------------------===//

#ifndef LOOM_VISUALIZATION_HANDSHAKETODOT_H
#define LOOM_VISUALIZATION_HANDSHAKETODOT_H

#include "mlir/IR/BuiltinOps.h"
#include "llvm/Support/raw_ostream.h"

namespace loom {
namespace visualization {

/// Write DOT representation for all handshake.func ops in module to \p os.
/// Uses subgraphs if multiple functions are present.
void exportModuleToDot(mlir::ModuleOp module, llvm::raw_ostream &os);

} // namespace visualization
} // namespace loom

#endif // LOOM_VISUALIZATION_HANDSHAKETODOT_H
1 change: 1 addition & 0 deletions lib/loom/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
add_subdirectory(Dialect)
add_subdirectory(Hardware)
add_subdirectory(Visualization)

set(LOOM_CONVERSION_SOURCES
Conversion/LLVMToSCFFunction.cpp
Expand Down
20 changes: 20 additions & 0 deletions lib/loom/Visualization/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
add_llvm_library(LoomVisualization
DotStyleConfig.cpp
HandshakeToDot.cpp
LINK_LIBS PUBLIC
CIRCTHandshake
MLIRArithDialect
MLIRUBDialect
MLIRIR
MLIRSupport
LLVMSupport
)

target_include_directories(LoomVisualization
PUBLIC $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
PRIVATE "${LLVM_SOURCE_DIR}/include" "${LLVM_BINARY_DIR}/include"
"${MLIR_SOURCE_DIR}/include" "${MLIR_BINARY_DIR}/include"
"${CIRCT_SOURCE_DIR}/include" "${CIRCT_BINARY_DIR}/include"
)

llvm_update_compile_flags(LoomVisualization)
184 changes: 184 additions & 0 deletions lib/loom/Visualization/DotStyleConfig.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
//===- DotStyleConfig.cpp - Centralized DOT Visualization Styles ---------===//
//
// Implementation of centralized styling for all DOT visualizations.
//
//===----------------------------------------------------------------------===//

#include "loom/Visualization/DotStyleConfig.h"
#include "llvm/ADT/StringRef.h"
#include <sstream>

using namespace loom::visualization;

//===----------------------------------------------------------------------===//
// Helper Functions
//===----------------------------------------------------------------------===//

bool DotStyleConfig::isArithOp(llvm::StringRef opName) {
return opName.starts_with("arith.") && opName != "arith.constant";
}

bool DotStyleConfig::isMathOp(llvm::StringRef opName) {
return opName.starts_with("math.");
}

bool DotStyleConfig::isLLVMIntrinsic(llvm::StringRef opName) {
return opName.starts_with("llvm.intr.");
}

bool DotStyleConfig::isUBOp(llvm::StringRef opName) {
return opName == "ub.poison";
}

bool DotStyleConfig::isHandshakeMemoryOp(llvm::StringRef opName) {
return opName == "handshake.extmemory" || opName == "handshake.memory" ||
opName == "handshake.load" || opName == "handshake.store";
}

bool DotStyleConfig::isHandshakeFork(llvm::StringRef opName) {
return opName == "handshake.fork" || opName == "fork";
}

bool DotStyleConfig::isHandshakeMux(llvm::StringRef opName) {
return opName == "handshake.mux" || opName == "mux";
}

//===----------------------------------------------------------------------===//
// Style Implementation (Per Specification)
//===----------------------------------------------------------------------===//

DotNodeStyle DotStyleConfig::getHandshakeMemoryStyle(llvm::StringRef opName) {
if (opName == "handshake.extmemory") {
return DotNodeStyle("hexagon", "gold", "black", "solid", 1.0);
} else if (opName == "handshake.memory") {
return DotNodeStyle("doubleoctagon", "orange", "black", "solid", 1.0);
} else if (opName == "handshake.load") {
return DotNodeStyle("cylinder", "skyblue", "black", "solid", 1.0);
} else if (opName == "handshake.store") {
return DotNodeStyle("box3d", "salmon", "black", "solid", 1.0);
}

return getFallbackStyle();
}

DotNodeStyle DotStyleConfig::getNodeStyle(llvm::StringRef opName, bool isMapped,
bool isFuncArg, bool isReturn) {
// Special nodes: Function interface
if (isFuncArg) {
return DotNodeStyle("invhouse", "lightpink", "black", "solid", 1.0);
}

if (isReturn) {
return DotNodeStyle("house", "lightcoral", "black", "solid", 1.0);
}

// Handshake.fork: small circle + light blue with label "f:<ID>"
if (isHandshakeFork(opName)) {
return DotNodeStyle("circle", "lightblue", "black", "solid", 1.0,
"width=0.7,height=0.7,fixedsize=true");
}

// Handshake.mux: inverted triangle + light cyan
if (isHandshakeMux(opName)) {
return DotNodeStyle("invtriangle", "lightcyan", "black", "solid", 1.0);
}

// Handshake memory operations: unique shapes and colors
if (isHandshakeMemoryOp(opName)) {
DotNodeStyle style = getHandshakeMemoryStyle(opName);
if (!isMapped) {
style.fillColor = "white";
style.borderStyle = "dashed";
}
return style;
}

// Other handshake operations (excluding fork, mux, memory ops)
if (opName.starts_with("handshake.")) {
DotNodeStyle style("ellipse", "lightblue", "black", "solid", 1.0);
if (!isMapped) {
style.fillColor = "white";
style.borderStyle = "dashed";
}
return style;
}

// All arith.* ops: Msquare + dark green
if (isArithOp(opName)) {
DotNodeStyle style("Msquare", "darkgreen", "white", "solid", 1.0);
if (!isMapped) {
style.fillColor = "white";
style.fontColor = "black";
style.borderStyle = "dashed";
}
return style;
}

// All math.* ops: trapezoid + dark purple
if (isMathOp(opName)) {
DotNodeStyle style("trapezium", "purple4", "white", "solid", 1.0);
if (!isMapped) {
style.fillColor = "white";
style.fontColor = "black";
style.borderStyle = "dashed";
}
return style;
}

// LLVM intrinsics: square + magenta
if (isLLVMIntrinsic(opName)) {
DotNodeStyle style("square", "magenta", "black", "solid", 1.0);
if (!isMapped) {
style.fillColor = "white";
style.borderStyle = "dashed";
}
return style;
}

// ub.poison: triangle + pure red
if (isUBOp(opName)) {
DotNodeStyle style("triangle", "red", "white", "solid", 1.0);
if (!isMapped) {
style.fillColor = "white";
style.fontColor = "black";
style.borderStyle = "dashed";
}
return style;
}

// Constants: octagon + wheat
if (opName == "arith.constant" || opName == "handshake.constant") {
DotNodeStyle style("octagon", "wheat", "black", "solid", 1.0);
if (!isMapped) {
style.fillColor = "white";
style.borderStyle = "dashed";
}
return style;
}

// Fallback for unhandled ops: five-pointed star + pure red
DotNodeStyle style = getFallbackStyle();
if (!isMapped) {
style.fillColor = "white";
style.borderStyle = "dashed";
}
return style;
}

DotNodeStyle DotStyleConfig::getFallbackStyle() {
return DotNodeStyle("star", "red", "white", "solid", 2.0);
}

DotEdgeStyle DotStyleConfig::getEdgeStyle(bool isMapped, bool showPortLabel) {
if (isMapped) {
return DotEdgeStyle("black", "solid", 2.5, showPortLabel);
} else {
return DotEdgeStyle("gray", "dashed", 1.0, false);
}
}

std::string DotStyleConfig::formatPortLabel(int srcPort, int dstPort) {
std::ostringstream os;
os << "port" << srcPort << " -> port" << dstPort;
return os.str();
}
Loading