diff --git a/include/NeuraDialect/Architecture/Architecture.h b/include/NeuraDialect/Architecture/Architecture.h index 6d2cbd51..8d7028cf 100644 --- a/include/NeuraDialect/Architecture/Architecture.h +++ b/include/NeuraDialect/Architecture/Architecture.h @@ -1,6 +1,7 @@ #ifndef NEURA_ARCHITECTURE_H #define NEURA_ARCHITECTURE_H +#include #include #include #include @@ -9,10 +10,12 @@ #include #include +#include "NeuraDialect/Architecture/ArchitectureSpec.h" + namespace mlir { namespace neura { -// Enum for identifying resource type. +// Enumeration for identifying resource type. enum class ResourceKind { Tile, Link, @@ -22,15 +25,40 @@ enum class ResourceKind { RegisterFileCluster, }; -// Enum for function unit resource type. +// Enumeration for function unit resource type. enum class FunctionUnitKind { FixedPointAdder, FixedPointMultiplier, CustomizableFunctionUnit, }; -// Enum for supported operation types. -enum OperationKind { IAdd = 0, IMul = 1, FAdd = 2, FMul = 3 }; +// Enumeration for supported operation types. +enum OperationKind { + // Integer arithmetic operations. + IAdd = 0, IMul = 1, ISub = 2, IDiv = 3, IRem = 4, + // Floating-point arithmetic operations. + FAdd = 5, FMul = 6, FSub = 7, FDiv = 8, + // Memory operations. + ILoad = 9, IStore = 10, ILoadIndexed = 11, IStoreIndexed = 12, IAlloca = 13, + // Logical operations. + IOr = 14, INot = 15, ICmp = 16, FCmp = 17, ISel = 18, + // Type conversion operations. + ICast = 19, ISExt = 20, IZExt = 21, IShl = 22, + // Vector operations. + VFMul = 23, + // Fused operations. + FAddFAdd = 24, FMulFAdd = 25, + // Control flow operations. + IReturn = 26, IPhi = 27, + // Data movement operations. + IDataMov = 28, ICtrlMov = 29, + // Predicate operations. + IReserve = 30, IGrantPredicate = 31, IGrantOnce = 32, IGrantAlways = 33, + // Loop control operations. + ILoopControl = 34, + // Constant operations. + IConstant = 35 +}; //===----------------------------------------------------------------------===// // BasicResource: abstract base class for Tile, Link, etc. @@ -45,7 +73,7 @@ class BasicResource { }; //===----------------------------------------------------------------------===// -// Forward declaration for use in Tile +// Forward declaration for use in Tile. class Tile; class Link; class FunctionUnit; @@ -94,24 +122,6 @@ class FunctionUnit : public BasicResource { Tile *tile; }; -class FixedPointAdder : public FunctionUnit { -public: - FixedPointAdder(int id) : FunctionUnit(id) { - supported_operations.insert(OperationKind::IAdd); - } - std::string getType() const override { return "fixed_point_adder"; } - ResourceKind getKind() const override { return ResourceKind::FunctionUnit; } -}; - -class FixedPointMultiplier : public FunctionUnit { -public: - FixedPointMultiplier(int id) : FunctionUnit(id) { - supported_operations.insert(OperationKind::IMul); - } - std::string getType() const override { return "fixed_point_multiplier"; } - ResourceKind getKind() const override { return ResourceKind::FunctionUnit; } -}; - class CustomizableFunctionUnit : public FunctionUnit { public: CustomizableFunctionUnit(int id) : FunctionUnit(id) {} @@ -123,7 +133,7 @@ class CustomizableFunctionUnit : public FunctionUnit { }; //===----------------------------------------------------------------------===// -// Tile +// Tile. //===----------------------------------------------------------------------===// class Tile : public BasicResource { @@ -143,6 +153,7 @@ class Tile : public BasicResource { int getY() const; void linkDstTile(Link *link, Tile *tile); + void unlinkDstTile(Link *link, Tile *tile); const std::set &getDstTiles() const; const std::set &getSrcTiles() const; const std::set &getOutLinks() const; @@ -155,16 +166,20 @@ class Tile : public BasicResource { functional_units.insert(functional_unit_storage.back().get()); } + void clearFunctionUnits() { + functional_unit_storage.clear(); + functional_units.clear(); + } + bool canSupportOperation(OperationKind operation) const { + // Checks if any function unit in this tile supports the operation. + // The implementation checks all functional units in the tile. for (FunctionUnit *fu : functional_units) { if (fu->canSupportOperation(operation)) { return true; } } - // TODO: Check if the tile can support the operation based on its - // capabilities. - // @Jackcuii, https://github.com/coredac/dataflow/issues/82. - return true; + return false; } void addRegisterFileCluster(RegisterFileCluster *register_file_cluster); @@ -175,6 +190,17 @@ class Tile : public BasicResource { const std::vector getRegisters() const; + // Port management. + const std::vector &getPorts() const { return ports; } + void setPorts(const std::vector &new_ports) { ports = new_ports; } + bool hasPort(const std::string &port) const { + return std::find(ports.begin(), ports.end(), port) != ports.end(); + } + + // Memory management. + int getMemoryCapacity() const { return memory_capacity; } + void setMemoryCapacity(int capacity) { memory_capacity = capacity; } + private: int id; int x, y; @@ -186,10 +212,14 @@ class Tile : public BasicResource { functional_unit_storage; // Owns FUs. std::set functional_units; // Non-owning, for fast lookup. RegisterFileCluster *register_file_cluster = nullptr; + + // Port and memory configuration. + std::vector ports; + int memory_capacity = -1; // -1 means not configured. }; //===----------------------------------------------------------------------===// -// Link +// Link. //===----------------------------------------------------------------------===// class Link : public BasicResource { @@ -210,14 +240,22 @@ class Link : public BasicResource { void connect(Tile *src, Tile *dst); + // Link properties. + int getLatency() const { return latency; } + int getBandwidth() const { return bandwidth; } + void setLatency(int l) { latency = l; } + void setBandwidth(int b) { bandwidth = b; } + private: int id; Tile *src_tile; Tile *dst_tile; + int latency = 1; // Latency in cycles. + int bandwidth = 32; // Bandwidth in bits per cycle. }; //===----------------------------------------------------------------------===// -// Register +// Register. //===----------------------------------------------------------------------===// class Register : public BasicResource { @@ -246,7 +284,7 @@ class Register : public BasicResource { }; //===----------------------------------------------------------------------===// -// Register File +// Register File. //===----------------------------------------------------------------------===// class RegisterFile : public BasicResource { @@ -279,7 +317,7 @@ class RegisterFile : public BasicResource { }; //===----------------------------------------------------------------------===// -// Register File Cluster +// Register File Cluster. //===----------------------------------------------------------------------===// class RegisterFileCluster : public BasicResource { @@ -318,11 +356,23 @@ struct PairHash { } }; +// Forward declaration. +struct TileDefaults; +struct TileOverride; +struct LinkDefaults; +struct LinkOverride; + // Describes the CGRA architecture template. -// TODO: Model architecture in detail (e.g., registers, ports). +// Now supports comprehensive configuration via YAML including ports, memory, and function units. class Architecture { public: - Architecture(int width, int height); + // Single constructor - handles all cases internally. + Architecture(int width, int height, + const TileDefaults& tile_defaults, + const std::vector& tile_overrides, + const LinkDefaults& link_defaults, + const std::vector& link_overrides, + BaseTopology base_topology = BaseTopology::MESH); Tile *getTile(int id); Tile *getTile(int x, int y); @@ -331,18 +381,39 @@ class Architecture { int getHeight() const { return height; } Link *getLink(int id); + void removeLink(int link_id); + + // Tile management. + void removeTile(int tile_id); int getNumTiles() const; std::vector getAllTiles() const; std::vector getAllLinks() const; private: - // TODO: Model architecture in detail, e.g., ports, registers, crossbars, etc. - // https://github.com/coredac/dataflow/issues/52. - std::vector> tile_storage; - std::vector> link_storage; - std::unordered_map id_to_tile; - std::unordered_map, Tile *, PairHash> coord_to_tile; + // Helper methods for constructor initialization. + void initializeTiles(int width, int height); + void configureDefaultTileSettings(const TileDefaults& tile_defaults); + void applyTileOverrides(const std::vector& tile_overrides); + void createLinks(const LinkDefaults& link_defaults, BaseTopology base_topology); + void applyLinkOverrides(const std::vector& link_overrides); + void createRegisterFileCluster(Tile *tile, int num_registers, int ®_id); + void recreateRegisterFileCluster(Tile *tile, int num_registers); + bool linkExists(Tile *src_tile, Tile *dst_tile); + + // Helper methods for creating different topology links. + void createSingleLink(int &link_id, Tile *src_tile, Tile *dst_tile, const LinkDefaults& link_defaults); + void createLinkIfValid(int &link_id, Tile *src_tile, int dst_x, int dst_y, const LinkDefaults& link_defaults); + void createMeshLinks(int &link_id, const LinkDefaults& link_defaults); + void createKingMeshLinks(int &link_id, const LinkDefaults& link_defaults); + void createRingLinks(int &link_id, const LinkDefaults& link_defaults); + + // Architecture components: tiles, links, and their mappings. + // Ports and memory are now modeled as part of Tile class. + std::map> tile_storage; // Owns tiles, key is unique tile_id. + std::map> link_storage; // Owns links, key is unique link_id. + std::unordered_map id_to_tile; // Maps unique tile_id to Tile pointer. + std::unordered_map, Tile *, PairHash> coord_to_tile; // Maps (x,y) coordinates to Tile pointer. int width; int height; diff --git a/include/NeuraDialect/Architecture/ArchitectureSpec.h b/include/NeuraDialect/Architecture/ArchitectureSpec.h new file mode 100644 index 00000000..9e8d79be --- /dev/null +++ b/include/NeuraDialect/Architecture/ArchitectureSpec.h @@ -0,0 +1,76 @@ +#ifndef NEURA_ARCHITECTURE_SPEC_H +#define NEURA_ARCHITECTURE_SPEC_H + +#include +#include + +namespace mlir { +namespace neura { + +// Enumeration for base topology types. +enum class BaseTopology { + MESH, // 4-connected mesh (N, S, W, E). + KING_MESH, // 8-connected mesh (N, S, W, E, NE, NW, SE, SW). + RING // Ring topology (only outer boundary connections). +}; + +// Structure for holding tile default configuration. +struct TileDefaults { + int num_registers = 64; // default value. + std::vector default_ports = {"N", "S", "W", "E"}; // default ports. + std::vector operations = { + "add", "mul", "sub", "div", "rem", + "fadd", "fmul", "fsub", "fdiv", + "or", "not", "icmp", "fcmp", "sel", + "cast", "sext", "zext", "shl", + "vfmul", "fadd_fadd", "fmul_fadd", + "data_mov", "ctrl_mov", + "reserve", "grant_predicate", "grant_once", "grant_always", + "loop_control", "phi", "constant", + "load", "store", "return", + "load_indexed", "store_indexed", "alloca" + }; // default operations - includes all supported operations for newbie convenience. +}; + +// Structure for holding memory configuration. +struct MemoryConfig { + int capacity = -1; // Memory capacity in bytes. +}; + +// Structure for holding tile override configuration. +struct TileOverride { + int id = -1; + int x = -1, y = -1; + std::vector operations; + int num_registers = -1; + std::vector ports; + MemoryConfig memory; +}; + +// Structure for holding link default configuration. +struct LinkDefaults { + int latency = 1; // default latency. + int bandwidth = 32; // default bandwidth. +}; + +// Structure for holding link override configuration. +struct LinkOverride { + int id = -1; + int latency = -1; + int bandwidth = -1; + int src_tile_id = -1; + int dst_tile_id = -1; + bool existence = true; +}; + +// Function for getting the architecture specification file path. +// This is set by the command line tool when a YAML file is provided. +std::string getArchitectureSpecFile(); + +// Function for getting tile defaults configuration. +TileDefaults getTileDefaults(); + +} // namespace neura +} // namespace mlir + +#endif // NEURA_ARCHITECTURE_SPEC_H diff --git a/lib/NeuraDialect/Architecture/Architecture.cpp b/lib/NeuraDialect/Architecture/Architecture.cpp index 3143139b..19ce87ed 100644 --- a/lib/NeuraDialect/Architecture/Architecture.cpp +++ b/lib/NeuraDialect/Architecture/Architecture.cpp @@ -1,11 +1,140 @@ #include "NeuraDialect/Architecture/Architecture.h" +#include "NeuraDialect/Architecture/ArchitectureSpec.h" #include "llvm/Support/raw_ostream.h" - #include +#include +#include +#include +#include using namespace mlir; using namespace mlir::neura; +// Configures all supported operations for a function unit. +void configureSupportedOperations(CustomizableFunctionUnit *function_unit, const std::string &operation) { + // Integer arithmetic operations. + if (operation == "add") { + function_unit->addSupportedOperation(IAdd); + } else if (operation == "sub") { + function_unit->addSupportedOperation(ISub); + } else if (operation == "mul") { + function_unit->addSupportedOperation(IMul); + } else if (operation == "div") { + function_unit->addSupportedOperation(IDiv); + } else if (operation == "rem") { + function_unit->addSupportedOperation(IRem); + } + // Floating-point arithmetic operations. + else if (operation == "fadd") { + function_unit->addSupportedOperation(FAdd); + } else if (operation == "fsub") { + function_unit->addSupportedOperation(FSub); + } else if (operation == "fmul") { + function_unit->addSupportedOperation(FMul); + } else if (operation == "fdiv") { + function_unit->addSupportedOperation(FDiv); + } + // Memory operations. + else if (operation == "load") { + function_unit->addSupportedOperation(ILoad); + } else if (operation == "store") { + function_unit->addSupportedOperation(IStore); + } else if (operation == "load_indexed") { + function_unit->addSupportedOperation(ILoadIndexed); + } else if (operation == "store_indexed") { + function_unit->addSupportedOperation(IStoreIndexed); + } else if (operation == "alloca") { + function_unit->addSupportedOperation(IAlloca); + } + // Logical operations. + else if (operation == "or") { + function_unit->addSupportedOperation(IOr); + } else if (operation == "not") { + function_unit->addSupportedOperation(INot); + } else if (operation == "icmp") { + function_unit->addSupportedOperation(ICmp); + } else if (operation == "fcmp") { + function_unit->addSupportedOperation(FCmp); + } else if (operation == "sel") { + function_unit->addSupportedOperation(ISel); + } + // Type conversion operations. + else if (operation == "cast") { + function_unit->addSupportedOperation(ICast); + } else if (operation == "sext") { + function_unit->addSupportedOperation(ISExt); + } else if (operation == "zext") { + function_unit->addSupportedOperation(IZExt); + } else if (operation == "shl") { + function_unit->addSupportedOperation(IShl); + } + // Vector and fused operations. + else if (operation == "vfmul") { + function_unit->addSupportedOperation(VFMul); + } else if (operation == "fadd_fadd") { + function_unit->addSupportedOperation(FAddFAdd); + } else if (operation == "fmul_fadd") { + function_unit->addSupportedOperation(FMulFAdd); + } + // Control flow operations. + else if (operation == "return") { + function_unit->addSupportedOperation(IReturn); + } else if (operation == "phi") { + function_unit->addSupportedOperation(IPhi); + } else if (operation == "loop_control") { + function_unit->addSupportedOperation(ILoopControl); + } + // Data movement operations. + else if (operation == "data_mov") { + function_unit->addSupportedOperation(IDataMov); + } else if (operation == "ctrl_mov") { + function_unit->addSupportedOperation(ICtrlMov); + } + // Predicate and reservation operations. + else if (operation == "reserve") { + function_unit->addSupportedOperation(IReserve); + } else if (operation == "grant_predicate") { + function_unit->addSupportedOperation(IGrantPredicate); + } else if (operation == "grant_once") { + function_unit->addSupportedOperation(IGrantOnce); + } else if (operation == "grant_always") { + function_unit->addSupportedOperation(IGrantAlways); + } + // Constant operations. + else if (operation == "constant") { + function_unit->addSupportedOperation(IConstant); + } +} + +// Creates a function unit for a specific operation. +// Maps YAML operation names to OperationKind enum values and creates appropriate function units. +void createFunctionUnitForOperation(Tile *tile, const std::string &operation, int function_unit_id) { + auto function_unit = std::make_unique(function_unit_id); + + // Configures all supported operations using the unified function. + configureSupportedOperations(function_unit.get(), operation); + + // TODO: Adds support for unknown operations with warning instead of silent failure. + // Such support would help users identify typos in their YAML configuration. + + tile->addFunctionUnit(std::move(function_unit)); +} + +// Configures tile function units based on operations. +void configureTileFunctionUnits(Tile *tile, const std::vector &operations, bool clear_existing = true) { + // Configures function units based on YAML operations specification. + // If clear_existing is true, this replaces any existing function units with the specified ones. + + if (clear_existing) { + tile->clearFunctionUnits(); + } + + int function_unit_id = 0; + for (const auto &operation : operations) { + createFunctionUnitForOperation(tile, operation, function_unit_id++); + } +} + //===----------------------------------------------------------------------===// // Tile //===----------------------------------------------------------------------===// @@ -15,9 +144,6 @@ Tile::Tile(int id, int x, int y) { this->x = x; this->y = y; - // TODO: Add function units based on architecture specs. - // @Jackcuii, https://github.com/coredac/dataflow/issues/82. - addFunctionUnit(std::make_unique(0)); } int Tile::getId() const { return id; } @@ -34,6 +160,14 @@ void Tile::linkDstTile(Link *link, Tile *tile) { tile->in_links.insert(link); } +void Tile::unlinkDstTile(Link *link, Tile *tile) { + assert(tile && "Cannot unlink from a null tile"); + dst_tiles.erase(tile); + out_links.erase(link); + tile->src_tiles.erase(this); + tile->in_links.erase(link); +} + const std::set &Tile::getDstTiles() const { return dst_tiles; } const std::set &Tile::getSrcTiles() const { return src_tiles; } @@ -48,9 +182,9 @@ void Tile::addRegisterFileCluster(RegisterFileCluster *register_file_cluster) { llvm::errs() << "Warning: Overwriting existing register file cluster (" << this->register_file_cluster->getId() << ") in Tile " << this->id << "\n"; + // Removes the old register file cluster before adding the new one. + delete this->register_file_cluster; } - assert(this->register_file_cluster == nullptr && - "Register file cluster already exists"); this->register_file_cluster = register_file_cluster; register_file_cluster->setTile(this); } @@ -61,19 +195,23 @@ const RegisterFileCluster *Tile::getRegisterFileCluster() const { const std::vector Tile::getRegisterFiles() const { std::vector all_register_files; - for (const auto &[id, file] : - this->register_file_cluster->getRegisterFiles()) { - all_register_files.push_back(file); + if (this->register_file_cluster) { + for (const auto &[id, file] : + this->register_file_cluster->getRegisterFiles()) { + all_register_files.push_back(file); + } } return all_register_files; } const std::vector Tile::getRegisters() const { std::vector all_registers; - for (const auto &[reg_file_id, reg_file] : - this->register_file_cluster->getRegisterFiles()) { - for (const auto &[reg_id, reg] : reg_file->getRegisters()) { - all_registers.push_back(reg); + if (this->register_file_cluster) { + for (const auto &[reg_file_id, reg_file] : + this->register_file_cluster->getRegisterFiles()) { + for (const auto &[reg_id, reg] : reg_file->getRegisters()) { + all_registers.push_back(reg); + } } } return all_registers; @@ -185,90 +323,312 @@ RegisterFileCluster::getRegisterFiles() const { // Architecture //===----------------------------------------------------------------------===// -Architecture::Architecture(int width, int height) { - this->width = width; - this->height = height; - const int num_tiles = width * height; - - // Initializes tiles. - tile_storage.reserve(num_tiles); +// Initializes tiles in the architecture. +void Architecture::initializeTiles(int width, int height) { for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { const int id = y * width + x; - // const int id = x * width + y; auto tile = std::make_unique(id, x, y); id_to_tile[id] = tile.get(); coord_to_tile[{x, y}] = tile.get(); - tile_storage.push_back(std::move(tile)); + tile_storage[id] = std::move(tile); } } +} - // Initializes register file cluster for each tile. +// Creates register file cluster for a tile. +void Architecture::createRegisterFileCluster(Tile *tile, int num_registers, int ®_id) { + const int k_num_regs_per_regfile = 8; // Keep this fixed for now. + const int k_num_regfiles_per_cluster = num_registers / k_num_regs_per_regfile; + + RegisterFileCluster *register_file_cluster = new RegisterFileCluster(tile->getId()); + + // Creates registers as a register file. + for (int file_idx = 0; file_idx < k_num_regfiles_per_cluster; ++file_idx) { + RegisterFile *register_file = new RegisterFile(file_idx); + for (int reg_idx = 0; reg_idx < k_num_regs_per_regfile; ++reg_idx) { + Register *reg = new Register(reg_id++); + register_file->addRegister(reg); + } + register_file_cluster->addRegisterFile(register_file); + } + + tile->addRegisterFileCluster(register_file_cluster); +} + +// Configures default tile settings. +void Architecture::configureDefaultTileSettings(const TileDefaults& tile_defaults) { int reg_id = 0; for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { - // Gets the tile by coordinates. Tile *tile = getTile(x, y); - const int kNUM_REGS_PER_REGFILE = 8; - const int kNUM_REGFILES_PER_CLUSTER = 4; - - // Assembles register files into a cluster. - RegisterFileCluster *register_file_cluster = - new RegisterFileCluster(y * width + x); - - // Creates registers as a register file. - // FIXME: We have to assign different IDs due to the hash function - // cannot distinguish between different register files.. - for (int file_idx = 0; file_idx < kNUM_REGFILES_PER_CLUSTER; ++file_idx) { - RegisterFile *register_file = new RegisterFile(file_idx); - for (int reg_idx = 0; reg_idx < kNUM_REGS_PER_REGFILE; ++reg_idx) { - Register *reg = new Register(reg_id++); - register_file->addRegister(reg); - } - register_file_cluster->addRegisterFile(register_file); - } + + // Creates register file cluster with default capacity. + createRegisterFileCluster(tile, tile_defaults.num_registers, reg_id); + + // Configures function units based on tile_defaults.operations. + configureTileFunctionUnits(tile, tile_defaults.operations); + + // Sets default ports for the tile. + tile->setPorts(tile_defaults.default_ports); + } + } +} + +// Recreates register file cluster with new capacity. +void Architecture::recreateRegisterFileCluster(Tile *tile, int num_registers) { + const int k_num_regs_per_regfile = 8; // Keep this fixed for now. + const int k_num_regfiles_per_cluster = num_registers / k_num_regs_per_regfile; + + // Removes existing register file cluster. + if (tile->getRegisterFileCluster()) { + delete tile->getRegisterFileCluster(); + } + + // Creates new register file cluster with override capacity. + RegisterFileCluster *new_register_file_cluster = + new RegisterFileCluster(tile->getId()); + + // Creates registers with new capacity. + int reg_id = tile->getId() * 1000; // Use tile ID as base to avoid conflicts. + for (int file_idx = 0; file_idx < k_num_regfiles_per_cluster; ++file_idx) { + RegisterFile *register_file = new RegisterFile(file_idx); + for (int reg_idx = 0; reg_idx < k_num_regs_per_regfile; ++reg_idx) { + Register *reg = new Register(reg_id++); + register_file->addRegister(reg); + } + new_register_file_cluster->addRegisterFile(register_file); + } + + // Adds new register file cluster to the tile. + tile->addRegisterFileCluster(new_register_file_cluster); +} - // Adds register file cluster to the tile. - tile->addRegisterFileCluster(register_file_cluster); - llvm::errs() << "Tile (" << x << ", " << y - << ") added register file cluster with ID: " - << register_file_cluster->getId() << "\n"; +// Applies tile overrides to modify specific tiles. +void Architecture::applyTileOverrides(const std::vector& tile_overrides) { + for (const auto &override : tile_overrides) { + Tile *tile = nullptr; + if (override.id >= 0) { + tile = getTile(override.id); + } else if (override.x >= 0 && override.y >= 0) { + tile = getTile(override.x, override.y); + } + + if (tile) { + // Overrides operations if specified. + if (!override.operations.empty()) { + configureTileFunctionUnits(tile, override.operations, true); + } + + // Overrides num_registers if specified. + if (override.num_registers > 0) { + recreateRegisterFileCluster(tile, override.num_registers); + } + + // Overrides ports if specified. + if (!override.ports.empty()) { + tile->setPorts(override.ports); + } + + // Overrides memory capacity if specified. + if (override.memory.capacity > 0) { + tile->setMemoryCapacity(override.memory.capacity); + } } } +} + +// Creates a single link between two tiles. +void Architecture::createSingleLink(int &link_id, Tile *src_tile, Tile *dst_tile, + const LinkDefaults& link_defaults) { + auto link = std::make_unique(link_id); + link->setLatency(link_defaults.latency); + link->setBandwidth(link_defaults.bandwidth); + link->connect(src_tile, dst_tile); + link_storage[link_id] = std::move(link); + link_id++; +} - // TODO: Model topology based on the architecture specs. - // https://github.com/coredac/dataflow/issues/52. +// Creates links between tiles based on topology. +void Architecture::createLinks(const LinkDefaults& link_defaults, BaseTopology base_topology) { int link_id = 0; + + switch (base_topology) { + case BaseTopology::MESH: + createMeshLinks(link_id, link_defaults); + break; + case BaseTopology::KING_MESH: + createKingMeshLinks(link_id, link_defaults); + break; + case BaseTopology::RING: + createRingLinks(link_id, link_defaults); + break; + default: + // Defaults to mesh if unknown topology. + createMeshLinks(link_id, link_defaults); + break; + } +} + +// Checks if a tile is on the boundary of the architecture. +bool isOnBoundary(int x, int y, int width, int height) { + return (x == 0 || x == width - 1 || y == 0 || y == height - 1); +} + +// Creates a link if the destination tile exists within bounds. +void Architecture::createLinkIfValid(int &link_id, Tile *src_tile, int dst_x, int dst_y, + const LinkDefaults& link_defaults) { + if (dst_x >= 0 && dst_x < width && dst_y >= 0 && dst_y < height) { + createSingleLink(link_id, src_tile, getTile(dst_x, dst_y), link_defaults); + } +} + +// Creates 4-connected mesh links (N, S, W, E). +void Architecture::createMeshLinks(int &link_id, const LinkDefaults& link_defaults) { for (int j = 0; j < height; ++j) { for (int i = 0; i < width; ++i) { - // Gets the tile by coordinates. Tile *tile = getTile(i, j); - // Creates links to neighboring tiles. - if (i > 0) { - auto link_towards_left = std::make_unique(link_id++); - link_towards_left->connect(tile, getTile(i - 1, j)); - link_storage.push_back(std::move(link_towards_left)); - } - if (i < width - 1) { - auto link_towards_right = std::make_unique(link_id++); - link_towards_right->connect(tile, getTile(i + 1, j)); - link_storage.push_back(std::move(link_towards_right)); + // Creates links to neighboring tiles with default properties. + createLinkIfValid(link_id, tile, i - 1, j, link_defaults); // West + createLinkIfValid(link_id, tile, i + 1, j, link_defaults); // East + createLinkIfValid(link_id, tile, i, j - 1, link_defaults); // South + createLinkIfValid(link_id, tile, i, j + 1, link_defaults); // North + } + } +} + +// Creates 8-connected king mesh links (N, S, W, E, NE, NW, SE, SW). +void Architecture::createKingMeshLinks(int &link_id, const LinkDefaults& link_defaults) { + for (int j = 0; j < height; ++j) { + for (int i = 0; i < width; ++i) { + Tile *tile = getTile(i, j); + + // Creates 4-connected links (N, S, W, E). + createLinkIfValid(link_id, tile, i - 1, j, link_defaults); // West + createLinkIfValid(link_id, tile, i + 1, j, link_defaults); // East + createLinkIfValid(link_id, tile, i, j - 1, link_defaults); // South + createLinkIfValid(link_id, tile, i, j + 1, link_defaults); // North + + // Creates diagonal links for king mesh (NE, NW, SE, SW). + createLinkIfValid(link_id, tile, i - 1, j - 1, link_defaults); // Southwest + createLinkIfValid(link_id, tile, i + 1, j - 1, link_defaults); // Southeast + createLinkIfValid(link_id, tile, i - 1, j + 1, link_defaults); // Northwest + createLinkIfValid(link_id, tile, i + 1, j + 1, link_defaults); // Northeast + } + } +} + +// Creates ring topology links (only outer boundary connections). +void Architecture::createRingLinks(int &link_id, const LinkDefaults& link_defaults) { + // Connects tiles on the outer boundary only. + for (int j = 0; j < height; ++j) { + for (int i = 0; i < width; ++i) { + Tile *tile = getTile(i, j); + + // Checks if tile is on the boundary. + if (isOnBoundary(i, j, width, height)) { + // Creates connections only to adjacent boundary tiles. + createLinkIfValid(link_id, tile, i - 1, j, link_defaults); // West + createLinkIfValid(link_id, tile, i + 1, j, link_defaults); // East + createLinkIfValid(link_id, tile, i, j - 1, link_defaults); // South + createLinkIfValid(link_id, tile, i, j + 1, link_defaults); // North } - if (j > 0) { - auto link_towards_down = std::make_unique(link_id++); - link_towards_down->connect(tile, getTile(i, j - 1)); - link_storage.push_back(std::move(link_towards_down)); + } + } +} + +// Checks if a link already exists between two tiles. +bool Architecture::linkExists(Tile *src_tile, Tile *dst_tile) { + for (const auto &[id, link] : link_storage) { + if (link && link->getSrcTile() == src_tile && link->getDstTile() == dst_tile) { + return true; + } + } + return false; +} + +// Applies link overrides to create, modify, or remove links. +void Architecture::applyLinkOverrides(const std::vector& link_overrides) { + int next_link_id = link_storage.empty() ? 0 : link_storage.rbegin()->first + 1; // Starts from the next available ID. + + for (const auto &override : link_overrides) { + // Handles existing link modifications/removals by ID. + if (override.id >= 0 && link_storage.find(override.id) != link_storage.end()) { + Link *link = link_storage[override.id].get(); + if (link) { + if (override.latency > 0) { + link->setLatency(override.latency); + } + + if (override.bandwidth > 0) { + link->setBandwidth(override.bandwidth); + } + + if (!override.existence) { + removeLink(override.id); + } } - if (j < height - 1) { - auto link_towards_up = std::make_unique(link_id++); - link_towards_up->connect(tile, getTile(i, j + 1)); - link_storage.push_back(std::move(link_towards_up)); + } + // Handles link creation/removal by tile IDs. + else if (override.src_tile_id >= 0 && override.dst_tile_id >= 0) { + Tile *src_tile = getTile(override.src_tile_id); + Tile *dst_tile = getTile(override.dst_tile_id); + + if (src_tile && dst_tile) { + bool link_already_exists = linkExists(src_tile, dst_tile); + + if (override.existence && !link_already_exists) { + // Creates new link. + auto link = std::make_unique(next_link_id++); + + // Sets link properties. + if (override.latency > 0) { + link->setLatency(override.latency); + } + if (override.bandwidth > 0) { + link->setBandwidth(override.bandwidth); + } + + // Connects the tiles. + link->connect(src_tile, dst_tile); + link_storage[next_link_id] = std::move(link); + next_link_id++; + } else if (!override.existence && link_already_exists) { + // Removes existing link. + for (auto it = link_storage.begin(); it != link_storage.end(); ++it) { + if (it->second && + it->second->getSrcTile() == src_tile && + it->second->getDstTile() == dst_tile) { + removeLink(it->first); + break; + } + } + } } } } } +// Main constructor - handles all cases internally. +Architecture::Architecture(int width, int height, + const TileDefaults& tile_defaults, + const std::vector& tile_overrides, + const LinkDefaults& link_defaults, + const std::vector& link_overrides, + BaseTopology base_topology) { + this->width = width; + this->height = height; + + // Initializes architecture components using helper methods. + initializeTiles(width, height); + configureDefaultTileSettings(tile_defaults); + applyTileOverrides(tile_overrides); + createLinks(link_defaults, base_topology); + applyLinkOverrides(link_overrides); +} + + Tile *Architecture::getTile(int id) { auto it = id_to_tile.find(id); assert(it != id_to_tile.end() && "Tile with given ID not found"); @@ -283,8 +643,11 @@ Tile *Architecture::getTile(int x, int y) { std::vector Architecture::getAllTiles() const { std::vector result; - for (auto &tile : tile_storage) - result.push_back(tile.get()); + for (const auto &[id, tile] : tile_storage) { + if (tile) { + result.push_back(tile.get()); + } + } return result; } @@ -292,10 +655,62 @@ int Architecture::getNumTiles() const { return static_cast(id_to_tile.size()); } +// Removes a tile from the architecture. +void Architecture::removeTile(int tile_id) { + auto it = tile_storage.find(tile_id); + if (it == tile_storage.end() || !it->second) { + return; // Tile not found or already removed. + } + + Tile *tile = it->second.get(); + + // Removes all links connected to this tile. + std::vector links_to_remove; + for (const auto &[link_id, link] : link_storage) { + if (link && (link->getSrcTile() == tile || link->getDstTile() == tile)) { + links_to_remove.push_back(link_id); + } + } + + for (int link_id : links_to_remove) { + removeLink(link_id); + } + + // Removes tile from coordinate mapping. + coord_to_tile.erase({tile->getX(), tile->getY()}); + + // Removes tile from ID mapping. + id_to_tile.erase(tile_id); + + // Removes tile from storage. + tile_storage.erase(it); +} + std::vector Architecture::getAllLinks() const { std::vector all_links; - for (const auto &link : link_storage) { - all_links.push_back(link.get()); + for (const auto &[id, link] : link_storage) { + if (link) { + all_links.push_back(link.get()); + } } return all_links; } + +void Architecture::removeLink(int link_id) { + auto it = link_storage.find(link_id); + if (it == link_storage.end() || !it->second) { + return; + } + + Link *link = it->second.get(); + Tile *src_tile = link->getSrcTile(); + Tile *dst_tile = link->getDstTile(); + + if (src_tile && dst_tile) { + // Removes the link from both tiles' connection sets. + src_tile->unlinkDstTile(link, dst_tile); + } + + // Removes the link from storage. + link_storage.erase(it); +} diff --git a/lib/NeuraDialect/Mapping/mapping_util.cpp b/lib/NeuraDialect/Mapping/mapping_util.cpp index a7d2749a..0d59baf6 100644 --- a/lib/NeuraDialect/Mapping/mapping_util.cpp +++ b/lib/NeuraDialect/Mapping/mapping_util.cpp @@ -17,16 +17,67 @@ using namespace mlir::neura; namespace mlir { namespace neura { OperationKind getOperationKindFromMlirOp(Operation *op) { - if (isa(op)) - return IAdd; - if (isa(op)) - return IMul; - if (isa(op)) - return FAdd; - if (isa(op)) - return FMul; - // TODO: Complete the list here. - // @Jackcuii, https://github.com/coredac/dataflow/issues/82. + // Integer arithmetic operations + if (isa(op)) return IAdd; + if (isa(op)) return ISub; + if (isa(op)) return IMul; + if (isa(op)) return IDiv; + if (isa(op)) return IRem; + + // Floating-point arithmetic operations + if (isa(op)) return FAdd; + if (isa(op)) return FSub; + if (isa(op)) return FMul; + if (isa(op)) return FDiv; + + // Memory operations + if (isa(op)) return ILoad; + if (isa(op)) return IStore; + if (isa(op)) return ILoadIndexed; + if (isa(op)) return IStoreIndexed; + if (isa(op)) return IAlloca; + + // Logical operations + if (isa(op)) return IOr; + if (isa(op)) return INot; + if (isa(op)) return ICmp; + if (isa(op)) return FCmp; + if (isa(op)) return ISel; + + // Type conversion operations + if (isa(op)) return ICast; + if (isa(op)) return ISExt; + if (isa(op)) return IZExt; + if (isa(op)) return IShl; + + // Vector operations + if (isa(op)) return VFMul; + + // Fused operations + if (isa(op)) return FAddFAdd; + if (isa(op)) return FMulFAdd; + + // Control flow operations + if (isa(op)) return IReturn; + if (isa(op)) return IPhi; + + // Data movement operations + if (isa(op)) return IDataMov; + if (isa(op)) return ICtrlMov; + + // Predicate operations + if (isa(op)) return IReserve; + if (isa(op)) return IGrantPredicate; + if (isa(op)) return IGrantOnce; + if (isa(op)) return IGrantAlways; + + // Loop control operations + if (isa(op)) return ILoopControl; + + // Constant operations + if (isa(op)) return IConstant; + + // Default fallback return IAdd; } diff --git a/lib/NeuraDialect/Transforms/GenerateCodePass.cpp b/lib/NeuraDialect/Transforms/GenerateCodePass.cpp index 88868849..a573f9ad 100644 --- a/lib/NeuraDialect/Transforms/GenerateCodePass.cpp +++ b/lib/NeuraDialect/Transforms/GenerateCodePass.cpp @@ -18,6 +18,7 @@ #include #include "NeuraDialect/Architecture/Architecture.h" +#include "NeuraDialect/Architecture/ArchitectureSpec.h" #include "NeuraDialect/NeuraOps.h" using namespace mlir; @@ -185,7 +186,11 @@ struct Topology { static Topology getTopologyFromArchitecture(int columns, int rows) { Topology topo; - mlir::neura::Architecture arch(columns, rows); + mlir::neura::Architecture arch(columns, rows, mlir::neura::TileDefaults{}, + std::vector{}, + mlir::neura::LinkDefaults{}, + std::vector{}, + mlir::neura::BaseTopology::MESH); for (auto *tile : arch.getAllTiles()) { topo.tile_location[tile->getId()] = {tile->getX(), tile->getY()}; diff --git a/lib/NeuraDialect/Transforms/MapToAcceleratorPass.cpp b/lib/NeuraDialect/Transforms/MapToAcceleratorPass.cpp index 7e11d696..7cb27601 100644 --- a/lib/NeuraDialect/Transforms/MapToAcceleratorPass.cpp +++ b/lib/NeuraDialect/Transforms/MapToAcceleratorPass.cpp @@ -1,7 +1,9 @@ #include #include +#include #include "NeuraDialect/Architecture/Architecture.h" +#include "NeuraDialect/Architecture/ArchitectureSpec.h" #include "NeuraDialect/Mapping/HeuristicMapping/HeuristicMapping.h" #include "NeuraDialect/Mapping/MappingState.h" #include "NeuraDialect/Mapping/mapping_util.h" @@ -15,6 +17,9 @@ #include "mlir/Pass/Pass.h" #include "mlir/Transforms/GreedyPatternRewriteDriver.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/YAMLParser.h" #include "llvm/Support/raw_ostream.h" using namespace mlir; @@ -23,6 +28,604 @@ using namespace mlir::neura; #define GEN_PASS_DEF_MAPTOACCELERATOR #include "NeuraDialect/NeuraPasses.h.inc" +// Use the TileOverride from ArchitectureSpec.h + +// Helper function to parse tile defaults. +bool parseTileDefaults(llvm::yaml::MappingNode *tile_defaults_map, mlir::neura::TileDefaults &tile_defaults) { + for (auto &key_value_pair : *tile_defaults_map) { + auto *key_node = llvm::dyn_cast_or_null(key_value_pair.getKey()); + if (!key_node) continue; + + llvm::SmallString<64> key_string; + llvm::StringRef key_ref = key_node->getValue(key_string); + + if (key_ref == "num_registers") { + auto *value_node = llvm::dyn_cast_or_null(key_value_pair.getValue()); + if (value_node) { + llvm::SmallString<64> value_string; + llvm::StringRef value_ref = value_node->getValue(value_string); + long long temp_value = 0; + if (!value_ref.getAsInteger(10, temp_value)) { + tile_defaults.num_registers = static_cast(temp_value); + } + } + } else if (key_ref == "operations") { + auto *value_node = llvm::dyn_cast_or_null(key_value_pair.getValue()); + if (value_node) { + tile_defaults.operations.clear(); + for (auto &operation_node : *value_node) { + auto *operation_scalar = llvm::dyn_cast_or_null(&operation_node); + if (operation_scalar) { + llvm::SmallString<64> operation_string; + llvm::StringRef operation_ref = operation_scalar->getValue(operation_string); + tile_defaults.operations.push_back(operation_ref.str()); + } + } + } + } else if (key_ref == "default_ports") { + auto *value_node = llvm::dyn_cast_or_null(key_value_pair.getValue()); + if (value_node) { + tile_defaults.default_ports.clear(); + for (auto &port_node : *value_node) { + auto *port_scalar = llvm::dyn_cast_or_null(&port_node); + if (port_scalar) { + llvm::SmallString<64> port_string; + llvm::StringRef port_ref = port_scalar->getValue(port_string); + tile_defaults.default_ports.push_back(port_ref.str()); + } + } + } + } + } + return true; +} + +// Helper function to parse tile override coordinates and ID. +void parseTileOverrideCoordinates(llvm::yaml::MappingNode *overrideMap, mlir::neura::TileOverride &override) { + for (auto &keyValuePair : *overrideMap) { + auto *keyNode = llvm::dyn_cast_or_null(keyValuePair.getKey()); + if (!keyNode) continue; + + llvm::SmallString<64> keyString; + llvm::StringRef keyRef = keyNode->getValue(keyString); + + if (keyRef == "id") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + llvm::SmallString<64> valueString; + llvm::StringRef valueRef = valueNode->getValue(valueString); + long long tempValue = 0; + if (!valueRef.getAsInteger(10, tempValue)) { + override.id = static_cast(tempValue); + } + } + } else if (keyRef == "x") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + llvm::SmallString<64> valueString; + llvm::StringRef valueRef = valueNode->getValue(valueString); + long long tempValue = 0; + if (!valueRef.getAsInteger(10, tempValue)) { + override.x = static_cast(tempValue); + } + } + } else if (keyRef == "y") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + llvm::SmallString<64> valueString; + llvm::StringRef valueRef = valueNode->getValue(valueString); + long long tempValue = 0; + if (!valueRef.getAsInteger(10, tempValue)) { + override.y = static_cast(tempValue); + } + } + } + } +} + +// Helper function to parse tile override operations and registers. +void parseTileOverrideOperations(llvm::yaml::MappingNode *overrideMap, mlir::neura::TileOverride &override) { + for (auto &keyValuePair : *overrideMap) { + auto *keyNode = llvm::dyn_cast_or_null(keyValuePair.getKey()); + if (!keyNode) continue; + + llvm::SmallString<64> keyString; + llvm::StringRef keyRef = keyNode->getValue(keyString); + + if (keyRef == "operations") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + override.operations.clear(); + for (auto &operationNode : *valueNode) { + auto *operationScalar = llvm::dyn_cast_or_null(&operationNode); + if (operationScalar) { + llvm::SmallString<64> operationString; + llvm::StringRef operationRef = operationScalar->getValue(operationString); + override.operations.push_back(operationRef.str()); + } + } + } + } else if (keyRef == "num_registers") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + llvm::SmallString<64> valueString; + llvm::StringRef valueRef = valueNode->getValue(valueString); + long long tempValue = 0; + if (!valueRef.getAsInteger(10, tempValue)) { + override.num_registers = static_cast(tempValue); + } + } + } + } +} + +// Helper function to parse tile override ports and memory. +void parseTileOverridePortsAndMemory(llvm::yaml::MappingNode *overrideMap, mlir::neura::TileOverride &override) { + for (auto &keyValuePair : *overrideMap) { + auto *keyNode = llvm::dyn_cast_or_null(keyValuePair.getKey()); + if (!keyNode) continue; + + llvm::SmallString<64> keyString; + llvm::StringRef keyRef = keyNode->getValue(keyString); + + if (keyRef == "ports") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + override.ports.clear(); + for (auto &portNode : *valueNode) { + auto *portScalar = llvm::dyn_cast_or_null(&portNode); + if (portScalar) { + llvm::SmallString<64> portString; + llvm::StringRef portRef = portScalar->getValue(portString); + override.ports.push_back(portRef.str()); + } + } + } + } else if (keyRef == "memory") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + for (auto &memoryKeyValuePair : *valueNode) { + auto *memoryKeyNode = llvm::dyn_cast_or_null(memoryKeyValuePair.getKey()); + if (!memoryKeyNode) continue; + + llvm::SmallString<64> memoryKeyString; + llvm::StringRef memoryKeyRef = memoryKeyNode->getValue(memoryKeyString); + + if (memoryKeyRef == "capacity") { + auto *memoryValueNode = llvm::dyn_cast_or_null(memoryKeyValuePair.getValue()); + if (memoryValueNode) { + llvm::SmallString<64> memoryValueString; + llvm::StringRef memoryValueRef = memoryValueNode->getValue(memoryValueString); + long long tempValue = 0; + if (!memoryValueRef.getAsInteger(10, tempValue)) { + override.memory.capacity = static_cast(tempValue); + } + } + } + } + } + } + } +} + +// Helper function to parse a single tile override. +void parseSingleTileOverride(llvm::yaml::MappingNode *overrideMap, mlir::neura::TileOverride &override) { + for (auto &keyValuePair : *overrideMap) { + auto *keyNode = llvm::dyn_cast_or_null(keyValuePair.getKey()); + if (!keyNode) continue; + + llvm::SmallString<64> keyString; + llvm::StringRef keyRef = keyNode->getValue(keyString); + + if (keyRef == "id") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + llvm::SmallString<64> valueString; + llvm::StringRef valueRef = valueNode->getValue(valueString); + long long tempValue = 0; + if (!valueRef.getAsInteger(10, tempValue)) { + override.id = static_cast(tempValue); + } + } + } else if (keyRef == "x") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + llvm::SmallString<64> valueString; + llvm::StringRef valueRef = valueNode->getValue(valueString); + long long tempValue = 0; + if (!valueRef.getAsInteger(10, tempValue)) { + override.x = static_cast(tempValue); + } + } + } else if (keyRef == "y") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + llvm::SmallString<64> valueString; + llvm::StringRef valueRef = valueNode->getValue(valueString); + long long tempValue = 0; + if (!valueRef.getAsInteger(10, tempValue)) { + override.y = static_cast(tempValue); + } + } + } else if (keyRef == "operations") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + override.operations.clear(); + for (auto &operationNode : *valueNode) { + auto *operationScalar = llvm::dyn_cast_or_null(&operationNode); + if (operationScalar) { + llvm::SmallString<64> operationString; + llvm::StringRef operationRef = operationScalar->getValue(operationString); + override.operations.push_back(operationRef.str()); + } + } + } + } else if (keyRef == "num_registers") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + llvm::SmallString<64> valueString; + llvm::StringRef valueRef = valueNode->getValue(valueString); + long long tempValue = 0; + if (!valueRef.getAsInteger(10, tempValue)) { + override.num_registers = static_cast(tempValue); + } + } + } else if (keyRef == "ports") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + override.ports.clear(); + for (auto &portNode : *valueNode) { + auto *portScalar = llvm::dyn_cast_or_null(&portNode); + if (portScalar) { + llvm::SmallString<64> portString; + llvm::StringRef portRef = portScalar->getValue(portString); + override.ports.push_back(portRef.str()); + } + } + } + } else if (keyRef == "memory") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + for (auto &memoryKeyValuePair : *valueNode) { + auto *memoryKeyNode = llvm::dyn_cast_or_null(memoryKeyValuePair.getKey()); + if (!memoryKeyNode) continue; + + llvm::SmallString<64> memoryKeyString; + llvm::StringRef memoryKeyRef = memoryKeyNode->getValue(memoryKeyString); + + if (memoryKeyRef == "capacity") { + auto *memoryValueNode = llvm::dyn_cast_or_null(memoryKeyValuePair.getValue()); + if (memoryValueNode) { + llvm::SmallString<64> memoryValueString; + llvm::StringRef memoryValueRef = memoryValueNode->getValue(memoryValueString); + long long tempValue = 0; + if (!memoryValueRef.getAsInteger(10, tempValue)) { + override.memory.capacity = static_cast(tempValue); + } + } + } + } + } + } + } +} + +// Helper function to parse tile overrides. +bool parseTileOverrides(llvm::yaml::SequenceNode *tile_overrides_seq, std::vector &tile_overrides) { + for (auto &override_node : *tile_overrides_seq) { + auto *override_map = llvm::dyn_cast_or_null(&override_node); + if (!override_map) continue; + + mlir::neura::TileOverride override; + parseSingleTileOverride(override_map, override); + tile_overrides.push_back(override); + } + return true; +} + +// Helper function to parse link defaults. +bool parseLinkDefaults(llvm::yaml::MappingNode *link_defaults_map, mlir::neura::LinkDefaults &link_defaults) { + for (auto &key_value_pair : *link_defaults_map) { + auto *key_node = llvm::dyn_cast_or_null(key_value_pair.getKey()); + if (!key_node) continue; + + llvm::SmallString<64> key_string; + llvm::StringRef key_ref = key_node->getValue(key_string); + + if (key_ref == "latency") { + auto *value_node = llvm::dyn_cast_or_null(key_value_pair.getValue()); + if (value_node) { + llvm::SmallString<64> value_string; + llvm::StringRef value_ref = value_node->getValue(value_string); + long long temp_value = 0; + if (!value_ref.getAsInteger(10, temp_value)) { + link_defaults.latency = static_cast(temp_value); + } + } + } else if (key_ref == "bandwidth") { + auto *value_node = llvm::dyn_cast_or_null(key_value_pair.getValue()); + if (value_node) { + llvm::SmallString<64> value_string; + llvm::StringRef value_ref = value_node->getValue(value_string); + long long temp_value = 0; + if (!value_ref.getAsInteger(10, temp_value)) { + link_defaults.bandwidth = static_cast(temp_value); + } + } + } + } + return true; +} + +// Helper function to parse link override properties. +void parseLinkOverrideProperties(llvm::yaml::MappingNode *overrideMap, mlir::neura::LinkOverride &override) { + for (auto &keyValuePair : *overrideMap) { + auto *keyNode = llvm::dyn_cast_or_null(keyValuePair.getKey()); + if (!keyNode) continue; + + llvm::SmallString<64> keyString; + llvm::StringRef keyRef = keyNode->getValue(keyString); + + if (keyRef == "id") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + llvm::SmallString<64> valueString; + llvm::StringRef valueRef = valueNode->getValue(valueString); + long long tempValue = 0; + if (!valueRef.getAsInteger(10, tempValue)) { + override.id = static_cast(tempValue); + } + } + } else if (keyRef == "latency") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + llvm::SmallString<64> valueString; + llvm::StringRef valueRef = valueNode->getValue(valueString); + long long tempValue = 0; + if (!valueRef.getAsInteger(10, tempValue)) { + override.latency = static_cast(tempValue); + } + } + } else if (keyRef == "bandwidth") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + llvm::SmallString<64> valueString; + llvm::StringRef valueRef = valueNode->getValue(valueString); + long long tempValue = 0; + if (!valueRef.getAsInteger(10, tempValue)) { + override.bandwidth = static_cast(tempValue); + } + } + } + } +} + +// Helper function to parse link override tile IDs and existence. +void parseLinkOverrideTilesAndExistence(llvm::yaml::MappingNode *overrideMap, mlir::neura::LinkOverride &override) { + for (auto &keyValuePair : *overrideMap) { + auto *keyNode = llvm::dyn_cast_or_null(keyValuePair.getKey()); + if (!keyNode) continue; + + llvm::SmallString<64> keyString; + llvm::StringRef keyRef = keyNode->getValue(keyString); + + if (keyRef == "src_tile_id") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + llvm::SmallString<64> valueString; + llvm::StringRef valueRef = valueNode->getValue(valueString); + long long tempValue = 0; + if (!valueRef.getAsInteger(10, tempValue)) { + override.src_tile_id = static_cast(tempValue); + } + } + } else if (keyRef == "dst_tile_id") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + llvm::SmallString<64> valueString; + llvm::StringRef valueRef = valueNode->getValue(valueString); + long long tempValue = 0; + if (!valueRef.getAsInteger(10, tempValue)) { + override.dst_tile_id = static_cast(tempValue); + } + } + } else if (keyRef == "existence") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + llvm::SmallString<64> valueString; + llvm::StringRef valueRef = valueNode->getValue(valueString); + override.existence = (valueRef == "true" || valueRef == "True" || valueRef == "1"); + } + } + } +} + +// Helper function to parse a single link override. +void parseSingleLinkOverride(llvm::yaml::MappingNode *overrideMap, mlir::neura::LinkOverride &override) { + for (auto &keyValuePair : *overrideMap) { + auto *keyNode = llvm::dyn_cast_or_null(keyValuePair.getKey()); + if (!keyNode) continue; + + llvm::SmallString<64> keyString; + llvm::StringRef keyRef = keyNode->getValue(keyString); + + if (keyRef == "id") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + llvm::SmallString<64> valueString; + llvm::StringRef valueRef = valueNode->getValue(valueString); + long long tempValue = 0; + if (!valueRef.getAsInteger(10, tempValue)) { + override.id = static_cast(tempValue); + } + } + } else if (keyRef == "latency") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + llvm::SmallString<64> valueString; + llvm::StringRef valueRef = valueNode->getValue(valueString); + long long tempValue = 0; + if (!valueRef.getAsInteger(10, tempValue)) { + override.latency = static_cast(tempValue); + } + } + } else if (keyRef == "bandwidth") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + llvm::SmallString<64> valueString; + llvm::StringRef valueRef = valueNode->getValue(valueString); + long long tempValue = 0; + if (!valueRef.getAsInteger(10, tempValue)) { + override.bandwidth = static_cast(tempValue); + } + } + } else if (keyRef == "src_tile_id") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + llvm::SmallString<64> valueString; + llvm::StringRef valueRef = valueNode->getValue(valueString); + long long tempValue = 0; + if (!valueRef.getAsInteger(10, tempValue)) { + override.src_tile_id = static_cast(tempValue); + } + } + } else if (keyRef == "dst_tile_id") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + llvm::SmallString<64> valueString; + llvm::StringRef valueRef = valueNode->getValue(valueString); + long long tempValue = 0; + if (!valueRef.getAsInteger(10, tempValue)) { + override.dst_tile_id = static_cast(tempValue); + } + } + } else if (keyRef == "existence") { + auto *valueNode = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (valueNode) { + llvm::SmallString<64> valueString; + llvm::StringRef valueRef = valueNode->getValue(valueString); + override.existence = (valueRef == "true" || valueRef == "True" || valueRef == "1"); + } + } + } +} + +// Helper function to parse link overrides. +bool parseLinkOverrides(llvm::yaml::SequenceNode *link_overrides_seq, std::vector &link_overrides) { + for (auto &override_node : *link_overrides_seq) { + auto *override_map = llvm::dyn_cast_or_null(&override_node); + if (!override_map) continue; + + mlir::neura::LinkOverride override; + parseSingleLinkOverride(override_map, override); + link_overrides.push_back(override); + } + return true; +} + +// Helper function to parse topology string to BaseTopology enum +mlir::neura::BaseTopology parseTopologyString(const std::string& topology_str) { + if (topology_str == "mesh") { + return mlir::neura::BaseTopology::MESH; + } else if (topology_str == "king_mesh" || topology_str == "king mesh") { + return mlir::neura::BaseTopology::KING_MESH; + } else if (topology_str == "ring") { + return mlir::neura::BaseTopology::RING; + } else { + // Default to mesh if unknown topology + return mlir::neura::BaseTopology::MESH; + } +} + +// Helper function to parse architecture YAML configuration. +bool parseArchitectureYAML(llvm::yaml::Document &doc, int &width, int &height, + mlir::neura::TileDefaults &tile_defaults, + std::vector &tile_overrides, + mlir::neura::LinkDefaults &link_defaults, + std::vector &link_overrides, + mlir::neura::BaseTopology &base_topology) { + auto *root = doc.getRoot(); + if (!root) { + llvm::errs() << "[MapToAcceleratorPass] Empty YAML document\n"; + return false; + } + + auto *rootMap = llvm::dyn_cast(root); + if (!rootMap) { + llvm::errs() << "[MapToAcceleratorPass] YAML root is not a mapping\n"; + return false; + } + + // Iterate root mapping ONCE; find 'architecture' and 'tile_defaults'. + for (auto &keyValuePair : *rootMap) { + auto *keyNode = llvm::dyn_cast_or_null(keyValuePair.getKey()); + if (!keyNode) continue; + + llvm::SmallString<64> keyString; + llvm::StringRef keyRef = keyNode->getValue(keyString); + + if (keyRef == "architecture") { + auto *architectureMap = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (!architectureMap) continue; + + // Iterate architecture mapping ONCE; read width/height in the same pass. + for (auto &architectureKeyValuePair : *architectureMap) { + auto *architectureKeyNode = llvm::dyn_cast_or_null(architectureKeyValuePair.getKey()); + if (!architectureKeyNode) continue; + + llvm::SmallString<64> architectureKeyString; + llvm::StringRef architectureKeyRef = architectureKeyNode->getValue(architectureKeyString); + if (architectureKeyRef != "width" && architectureKeyRef != "height") continue; + + auto *architectureValueNode = llvm::dyn_cast_or_null(architectureKeyValuePair.getValue()); + if (!architectureValueNode) continue; + + llvm::SmallString<64> architectureValueString; + llvm::StringRef architectureValueRef = architectureValueNode->getValue(architectureValueString); + long long tempValue = 0; + if (!architectureValueRef.getAsInteger(10, tempValue)) { + if (architectureKeyRef == "width") width = static_cast(tempValue); + if (architectureKeyRef == "height") height = static_cast(tempValue); + } + } + } else if (keyRef == "tile_defaults") { + auto *tile_defaults_map = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (tile_defaults_map) { + parseTileDefaults(tile_defaults_map, tile_defaults); + } + } else if (keyRef == "tile_overrides") { + auto *tile_overrides_seq = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (tile_overrides_seq) { + parseTileOverrides(tile_overrides_seq, tile_overrides); + } + } else if (keyRef == "link_defaults") { + auto *link_defaults_map = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (link_defaults_map) { + parseLinkDefaults(link_defaults_map, link_defaults); + } + } else if (keyRef == "link_overrides") { + auto *link_overrides_seq = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (link_overrides_seq) { + parseLinkOverrides(link_overrides_seq, link_overrides); + } + } else if (keyRef == "base_topology") { + auto *topology_node = llvm::dyn_cast_or_null(keyValuePair.getValue()); + if (topology_node) { + llvm::SmallString<64> topology_string; + llvm::StringRef topology_ref = topology_node->getValue(topology_string); + base_topology = parseTopologyString(topology_ref.str()); + } + } + } + + if (width <= 0 || height <= 0) { + width = -1; + height = -1; + } + + return true; +} + namespace { struct MapToAcceleratorPass : public PassWrapper> { @@ -132,6 +735,51 @@ struct MapToAcceleratorPass return; } + // Handle architecture specification file + std::string architecture_spec_file = mlir::neura::getArchitectureSpecFile(); + int yaml_width = -1; + int yaml_height = -1; + mlir::neura::TileDefaults yaml_tile_defaults; + std::vector tile_overrides; + mlir::neura::LinkDefaults yaml_link_defaults; + std::vector link_overrides; + mlir::neura::BaseTopology base_topology = mlir::neura::BaseTopology::MESH; // Default to mesh + if (!architecture_spec_file.empty()) { + + // Use LLVM YAML parser to validate the YAML syntax (no mapping yet) + llvm::ErrorOr> bufferOrErr = + llvm::MemoryBuffer::getFile(architecture_spec_file); + if (!bufferOrErr) { + llvm::errs() << "[MapToAcceleratorPass] Failed to open architecture specification file: " + << architecture_spec_file << "\n"; + return; + } + + llvm::SourceMgr sm; + sm.AddNewSourceBuffer(std::move(*bufferOrErr), llvm::SMLoc()); + llvm::yaml::Stream yamlStream(sm.getMemoryBuffer(sm.getMainFileID())->getBuffer(), sm); + + bool parseFailed = false; + llvm::yaml::Document &firstDoc = *yamlStream.begin(); + (void)firstDoc; // ensure document is created + if (yamlStream.failed()) { + parseFailed = true; + } + + if (parseFailed) { + llvm::errs() << "[MapToAcceleratorPass] YAML parse error in: " + << architecture_spec_file << "\n"; + return; + } + + // Parse YAML configuration + if (!parseArchitectureYAML(firstDoc, yaml_width, yaml_height, yaml_tile_defaults, tile_overrides, yaml_link_defaults, link_overrides, base_topology)) { + return; + } + + } else { + } + module.walk([&](func::FuncOp func) { // Skips functions not targeting the neura accelerator. auto accel_attr = func->getAttrOfType("accelerator"); @@ -188,8 +836,12 @@ struct MapToAcceleratorPass rec_mii = 1; // No recurrence cycles found, set MII to 1. } - // AcceleratorConfig config{/*numTiles=*/8}; // Example - Architecture architecture(4, 4); + // Construct architecture from YAML configuration + int arch_w = (yaml_width > 0 ? yaml_width : 4); + int arch_h = (yaml_height > 0 ? yaml_height : 4); + + // Always use full constructor with YAML configuration + Architecture architecture(arch_w, arch_h, yaml_tile_defaults, tile_overrides, yaml_link_defaults, link_overrides, base_topology); int res_mii = calculateResMii(func, architecture); const int possibleMinII = std::max(rec_mii, res_mii); @@ -232,8 +884,6 @@ struct MapToAcceleratorPass if (mapping_strategy->map(sorted_ops_with_alap_levels, critical_ops, architecture, mapping_state)) { // success - llvm::errs() << "[MapToAcceleratorPass] Successfully mapped function " - << func.getName() << "' with II = " << ii << "\n"; mapping_state.dumpOpToLocs(); // logs to stderr mapping_state.encodeMappingState(); diff --git a/test/arch_spec/README.md b/test/arch_spec/README.md new file mode 100644 index 00000000..9741f984 --- /dev/null +++ b/test/arch_spec/README.md @@ -0,0 +1,66 @@ +# Test Architecture Specification + +This directory contains a specialized 4x4 CGRA (Coarse-Grained Reconfigurable Array) architecture specification designed specifically for testing purposes. + +## Overview + +The `architecture.yaml` file defines a simplified 4x4 NeuraCGRA architecture that provides a consistent testing environment for various test suites including: + +- `code_gen` - Code generation tests +- `mapping_quality` - Mapping quality evaluation tests +- `neura` - Neura dialect tests +- `controflow_fuse` - Control flow fusion tests + +## Architecture Configuration + +### Key Features + +- **Array Size**: 4x4 CGRA (16 processing elements) +- **Registers**: 32 registers per tile (4 register files × 8 registers each) +- **Operations**: Comprehensive set of operations including arithmetic, logical, control flow, and data movement operations +- **Connectivity**: Standard mesh topology +- **No Overrides**: Clean configuration without link or tile overrides for consistent testing + +### Register Allocation + +Each tile has 32 registers allocated as follows: +- Tile 0: registers 0-31 +- Tile 1: registers 32-63 +- Tile 2: registers 64-95 +- And so on... + +This allocation scheme ensures predictable register numbering in test outputs. + +## Usage + +To use this architecture specification in your tests, add the following option to your `mlir-neura-opt` command: + +```bash +--architecture-spec=arch_spec/architecture.yaml +``` + +### Example + +```bash +mlir-neura-opt input.mlir \ + --assign-accelerator \ + --lower-llvm-to-neura \ + --map-to-accelerator="mapping-strategy=heuristic" \ + --architecture-spec=arch_spec/architecture.yaml \ + --generate-code +``` + +## Path Considerations + +When using this architecture specification from different test directories, use the appropriate relative path: + +- From `test/`: `arch_spec/architecture.yaml` +- From `test/code_gen/`: `../arch_spec/architecture.yaml` +- From `test/neura/ctrl/`: `../../arch_spec/architecture.yaml` + +## Benefits + +1. **Consistency**: All tests use the same 4x4 architecture specification +2. **Predictability**: Fixed register allocation ensures stable test outputs +3. **Simplicity**: No complex overrides or custom configurations +4. **Portability**: Works across different test environments and CI systems diff --git a/include/ArchitectureSpec/architecture.yaml b/test/arch_spec/arch_spec_example.yaml similarity index 67% rename from include/ArchitectureSpec/architecture.yaml rename to test/arch_spec/arch_spec_example.yaml index 804db97a..4c780302 100644 --- a/include/ArchitectureSpec/architecture.yaml +++ b/test/arch_spec/arch_spec_example.yaml @@ -1,19 +1,19 @@ architecture: name: "NeuraCGRA" version: "1.0" - width: 4 - height: 4 + width: 8 + height: 8 tile_defaults: - num_registers: 64 + num_registers: 128 default_ports: ["N", "S", "W", "E"] - operations: ["add", "mul", "sub"] # default ALU instructions + operations: ["add", "mul", "sub", "div", "rem", "fadd", "fmul", "fsub", "fdiv", "or", "not", "icmp", "fcmp", "sel", "cast", "sext", "zext", "shl", "vfmul", "fadd_fadd", "fmul_fadd", "data_mov", "ctrl_mov", "reserve", "grant_predicate", "grant_once", "grant_always", "loop_control", "phi", "constant"] # comprehensive operation set link_defaults: latency: 1 bandwidth: 32 -connectivity: "mesh" +base_topology: "mesh" link_overrides: - id: 1 @@ -30,6 +30,12 @@ link_overrides: dst_tile_id: 8 existence: true + - latency: 3 + bandwidth: 32 + src_tile_id: 0 + dst_tile_id: 1 + existence: true + tile_overrides: - id: 0 x: 0 @@ -62,7 +68,7 @@ tile_overrides: ports: ["E", "W"] extensions: - crossbar: false + placeholder: false simulator: execution_model: "serial" diff --git a/test/arch_spec/architecture.yaml b/test/arch_spec/architecture.yaml new file mode 100644 index 00000000..7ea99852 --- /dev/null +++ b/test/arch_spec/architecture.yaml @@ -0,0 +1,39 @@ +architecture: + name: "NeuraCGRA" + version: "1.0" + width: 4 + height: 4 + +tile_defaults: + num_registers: 32 + default_ports: ["N", "S", "W", "E"] + operations: ["add", "mul", "sub", "div", "rem", "fadd", "fmul", "fsub", "fdiv", "or", "not", "icmp", "fcmp", "sel", "cast", "sext", "zext", "shl", "vfmul", "fadd_fadd", "fmul_fadd", "data_mov", "ctrl_mov", "reserve", "grant_predicate", "grant_once", "grant_always", "loop_control", "phi", "constant", "load", "store", "return", "load_indexed", "store_indexed", "alloca"] + +link_defaults: + latency: 1 + bandwidth: 32 + +base_topology: "mesh" + +link_overrides: + +tile_overrides: + +extensions: + crossbar: false + +simulator: + execution_model: "serial" + + logging: + enabled: true + file: "output.log" + + driver: + name: "Driver" + frequency: "1GHz" + + device: + name: "Device" + frequency: "1GHz" + bind_to_architecture: true diff --git a/test/c2llvm2mlir/nested_loop/test.mlir b/test/c2llvm2mlir/nested_loop/test.mlir index 1c4b7b32..72e11cbe 100644 --- a/test/c2llvm2mlir/nested_loop/test.mlir +++ b/test/c2llvm2mlir/nested_loop/test.mlir @@ -16,7 +16,8 @@ // RUN: --transform-ctrl-to-data-flow \ // RUN: --fold-constant \ // RUN: --insert-data-mov \ -// RUN: --map-to-accelerator="mapping-strategy=heuristic backtrack-config=simple" %t-kernel.mlir | FileCheck %s --check-prefix=CHECK-LLVM2NEURA-MAP +// RUN: --map-to-accelerator="mapping-strategy=heuristic backtrack-config=simple" \ +// RUN: --architecture-spec=../../arch_spec/architecture.yaml %t-kernel.mlir | FileCheck %s --check-prefix=CHECK-LLVM2NEURA-MAP // CHECK-LLVM2NEURA: accelerator = "neura" // CHECK-LLVM2NEURA: %25 = neura.alloca %24 : !neura.data -> !neura.data diff --git a/test/code_gen/test_code_generate.mlir b/test/code_gen/test_code_generate.mlir index e88516e2..d1524f5e 100644 --- a/test/code_gen/test_code_generate.mlir +++ b/test/code_gen/test_code_generate.mlir @@ -6,6 +6,7 @@ // RUN: --transform-ctrl-to-data-flow \ // RUN: --insert-data-mov \ // RUN: --map-to-accelerator="mapping-strategy=heuristic" \ +// RUN: --architecture-spec=../arch_spec/architecture.yaml \ // RUN: --generate-code -o %t-mapping.mlir // RUN: FileCheck %s --input-file=%t-mapping.mlir -check-prefix=MAPPING // RUN: FileCheck %s --input-file=tmp-generated-instructions.yaml --check-prefix=YAML diff --git a/test/controflow_fuse/perfect_nested/perfect_nested.mlir b/test/controflow_fuse/perfect_nested/perfect_nested.mlir index 3f66227e..5f5d228b 100644 --- a/test/controflow_fuse/perfect_nested/perfect_nested.mlir +++ b/test/controflow_fuse/perfect_nested/perfect_nested.mlir @@ -42,6 +42,7 @@ // RUN: --fold-constant \ // RUN: --insert-data-mov \ // RUN: --map-to-accelerator="mapping-strategy=heuristic backtrack-config=customized" \ +// RUN: --architecture-spec=../../arch_spec/architecture.yaml \ // RUN: | FileCheck %s -check-prefix=MAPPING module attributes {} { diff --git a/test/controflow_fuse/simple_loop/simple_loop.mlir b/test/controflow_fuse/simple_loop/simple_loop.mlir index 979954f6..f6099e32 100644 --- a/test/controflow_fuse/simple_loop/simple_loop.mlir +++ b/test/controflow_fuse/simple_loop/simple_loop.mlir @@ -62,7 +62,8 @@ // RUN: --fuse-loop-control \ // RUN: --fold-constant \ // RUN: --insert-data-mov \ -// RUN: --map-to-accelerator="mapping-strategy=heuristic backtrack-config=customized" | FileCheck %s -check-prefix=FUSE-MAPPING +// RUN: --map-to-accelerator="mapping-strategy=heuristic backtrack-config=customized" \ +// RUN: --architecture-spec=../../arch_spec/architecture.yaml | FileCheck %s -check-prefix=FUSE-MAPPING module attributes {} { func.func @_Z11simple_loopPiS_(%arg0: memref, %arg1: memref) attributes {llvm.linkage = #llvm.linkage} { diff --git a/test/controflow_fuse/simple_loop_reduction/simple_loop_reduction.mlir b/test/controflow_fuse/simple_loop_reduction/simple_loop_reduction.mlir index 8d4b4e5c..ea074696 100644 --- a/test/controflow_fuse/simple_loop_reduction/simple_loop_reduction.mlir +++ b/test/controflow_fuse/simple_loop_reduction/simple_loop_reduction.mlir @@ -63,7 +63,8 @@ // RUN: --fuse-loop-control \ // RUN: --fold-constant \ // RUN: --insert-data-mov \ -// RUN: --map-to-accelerator="mapping-strategy=heuristic backtrack-config=customized" | FileCheck %s -check-prefix=FUSE-MAPPING +// RUN: --map-to-accelerator="mapping-strategy=heuristic backtrack-config=customized" \ +// RUN: --architecture-spec=../../arch_spec/architecture.yaml | FileCheck %s -check-prefix=FUSE-MAPPING module attributes {} { func.func @_Z10simpleloopv() -> i32 attributes {llvm.linkage = #llvm.linkage} { diff --git a/test/mapping_quality/branch_for.mlir b/test/mapping_quality/branch_for.mlir index d9cb1c9f..f2dd20dc 100644 --- a/test/mapping_quality/branch_for.mlir +++ b/test/mapping_quality/branch_for.mlir @@ -51,6 +51,7 @@ // RUN: --fold-constant \ // RUN: --insert-data-mov \ // RUN: --map-to-accelerator="mapping-strategy=heuristic backtrack-config=customized" \ +// RUN: --architecture-spec=../arch_spec/architecture.yaml \ // RUN: -o %t-mapping.mlir // RUN: FileCheck %s --input-file=%t-mapping.mlir -check-prefix=MAPPING @@ -63,7 +64,8 @@ // RUN: --fold-constant \ // RUN: --insert-data-mov \ // RUN: --map-to-accelerator="mapping-strategy=heuristic backtrack-config=customized" \ -// RUN: --generate-code +// RUN: --architecture-spec=../arch_spec/architecture.yaml \ +// RUN: --generate-code // RUN: FileCheck %s --input-file=tmp-generated-instructions.yaml -check-prefix=YAML // RUN: FileCheck %s --input-file=tmp-generated-instructions.asm --check-prefix=ASM diff --git a/test/mapping_quality/tiny_loop.mlir b/test/mapping_quality/tiny_loop.mlir index 2be8c77e..6795a176 100644 --- a/test/mapping_quality/tiny_loop.mlir +++ b/test/mapping_quality/tiny_loop.mlir @@ -25,6 +25,7 @@ // RUN: --fold-constant \ // RUN: --insert-data-mov \ // RUN: --map-to-accelerator="mapping-strategy=heuristic mapping-mode=spatial-only backtrack-config=customized=4,3" \ +// RUN: --architecture-spec=../arch_spec/architecture.yaml \ // RUN: | FileCheck %s -check-prefix=SPATIAL // RUN: mlir-neura-opt %t-llvm.mlir \ @@ -43,6 +44,7 @@ // RUN: --fold-constant \ // RUN: --insert-data-mov \ // RUN: --map-to-accelerator="mapping-strategy=heuristic mapping-mode=spatial-temporal backtrack-config=customized=4,4" \ +// RUN: --architecture-spec=../arch_spec/architecture.yaml \ // RUN: | FileCheck %s -check-prefix=SPATIAL-TEMPORAL module { diff --git a/test/neura/ctrl/branch_for.mlir b/test/neura/ctrl/branch_for.mlir index 9af41c51..a772575f 100644 --- a/test/neura/ctrl/branch_for.mlir +++ b/test/neura/ctrl/branch_for.mlir @@ -51,6 +51,7 @@ // RUN: --fold-constant \ // RUN: --insert-data-mov \ // RUN: --map-to-accelerator="mapping-strategy=heuristic backtrack-config=simple" \ +// RUN: --architecture-spec=../../arch_spec/architecture.yaml \ // RUN: | FileCheck %s -check-prefix=MAPPING // RUN: mlir-neura-opt %s \ @@ -63,6 +64,7 @@ // RUN: --fold-constant \ // RUN: --insert-data-mov \ // RUN: --map-to-accelerator="mapping-strategy=heuristic backtrack-config=simple" \ +// RUN: --architecture-spec=../../arch_spec/architecture.yaml \ // RUN: --generate-code // RUN: FileCheck %s --input-file=tmp-generated-instructions.yaml -check-prefix=YAML // RUN: FileCheck %s --input-file=tmp-generated-instructions.asm --check-prefix=ASM diff --git a/test/neura/fusion/test.mlir b/test/neura/fusion/test.mlir index 9db09a38..a603445d 100644 --- a/test/neura/fusion/test.mlir +++ b/test/neura/fusion/test.mlir @@ -1,6 +1,6 @@ # RUN: clang++ -S -emit-llvm -O3 -fno-unroll-loops -fno-vectorize -o %t-kernel.ll kernel.cpp # RUN: mlir-translate --import-llvm %t-kernel.ll -o %t-kernel.mlir -# RUN: mlir-neura-opt --assign-accelerator \ +# RUN: mlir-neura-opt --architecture-spec=%S/../../arch_spec/architecture.yaml --assign-accelerator \ # RUN: --lower-llvm-to-neura \ # RUN: --canonicalize-live-in \ # RUN: --leverage-predicated-value \ @@ -11,7 +11,7 @@ # RUN: --view-op-graph \ # RUN: --insert-data-mov %t-kernel.mlir | FileCheck %s --check-prefix=CHECK-FUSED -# RUN: mlir-neura-opt --assign-accelerator \ +# RUN: mlir-neura-opt --architecture-spec=%S/../../arch_spec/architecture.yaml --assign-accelerator \ # RUN: --lower-llvm-to-neura \ # RUN: --canonicalize-live-in \ # RUN: --leverage-predicated-value \ diff --git a/tools/mlir-neura-opt/mlir-neura-opt.cpp b/tools/mlir-neura-opt/mlir-neura-opt.cpp index 9dab542d..8969fa56 100644 --- a/tools/mlir-neura-opt/mlir-neura-opt.cpp +++ b/tools/mlir-neura-opt/mlir-neura-opt.cpp @@ -7,12 +7,51 @@ #include "mlir/Support/FileUtilities.h" #include "mlir/Support/LogicalResult.h" #include "mlir/Tools/mlir-opt/MlirOptMain.h" +#include "llvm/Support/CommandLine.h" #include "Conversion/ConversionPasses.h" #include "NeuraDialect/NeuraDialect.h" #include "NeuraDialect/NeuraPasses.h" +#include "NeuraDialect/Architecture/ArchitectureSpec.h" + +// Global variable to store architecture spec file path +static std::string architecture_spec_file; +static mlir::neura::TileDefaults tile_defaults; + +// Function to get the architecture spec file path +std::string mlir::neura::getArchitectureSpecFile() { + return architecture_spec_file; +} + +// Function to get tile defaults configuration +mlir::neura::TileDefaults mlir::neura::getTileDefaults() { + return tile_defaults; +} int main(int argc, char **argv) { + // Manually scan and strip --architecture-spec from argv, keep others for MlirOptMain. + std::vector forwarded_args; + forwarded_args.reserve(argc); + forwarded_args.push_back(argv[0]); + for (int i = 1; i < argc; ++i) { + llvm::StringRef arg_ref(argv[i]); + if (arg_ref == "--architecture-spec") { + if (i + 1 < argc) { + architecture_spec_file = argv[i + 1]; + ++i; // skip value + continue; + } + } else if (arg_ref.starts_with("--architecture-spec=")) { + architecture_spec_file = arg_ref.substr(strlen("--architecture-spec=")).str(); + continue; + } + forwarded_args.push_back(argv[i]); + } + + + int new_argc = static_cast(forwarded_args.size()); + char **new_argv = forwarded_args.data(); + // Registers MLIR dialects. mlir::DialectRegistry registry; registry.insert(); @@ -26,7 +65,15 @@ int main(int argc, char **argv) { mlir::registerPasses(); mlir::registerViewOpGraphPass(); + // Print architecture spec file info + if (!architecture_spec_file.empty()) { + llvm::errs() << "[mlir-neura-opt] Architecture specification file: " + << architecture_spec_file << "\n"; + } else { + llvm::errs() << "[mlir-neura-opt] No architecture specification file provided, using default configuration\n"; + } + // Runs the MLIR optimizer. return mlir::asMainReturnCode( - mlir::MlirOptMain(argc, argv, "Neura Dialect Optimizer", registry)); + mlir::MlirOptMain(new_argc, new_argv, "Neura Dialect Optimizer", registry)); }