Skip to content

Commit

Permalink
[handshake-sim] JSON config for exec models (#11)
Browse files Browse the repository at this point in the history
This commits adds support for providing a configuration file as well as
overrides to choose the set of operation execution models used during an
execution of the Handshake simulator. The JSON simply specifies a
mapping between operation name and name of the execution model to use
for it, while the --change-model command-line flag allows one to enforce
the use of specific execution models regardless of the configuration
present in the JSON.

At this point this is just preparatory work, as the configuration is
not used during simulation.
  • Loading branch information
Polymeth authored Jul 28, 2023
1 parent 8832426 commit 27e7040
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 34 deletions.
17 changes: 17 additions & 0 deletions experimental/data/handshake-simulator-configuration.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"handshake.fork":"defaultFork",
"handshake.merge":"defaultMerge",
"handshake.control_merge":"defaultControlMerge",
"handshake.mux":"defaultMux",
"handshake.br":"defaultBranch",
"handshake.cond_br":"defaultConditionalMerge",
"handshake.sink":"defaultSink",
"handshake.constant":"defaultConstant",
"handshake.buffer":"defaultBuffer",
"handshake.load":"defaultDynamaticLoad",
"handshake.store":"defaultDynamaticStore",
"handshake.return":"defaultDynamaticReturn",
"handshake.end":"defaultEnd",
"handshake.memory_controller":"defaultMemoryController"
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//===- Simulation.h - Handshake MLIR Operations ---------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains functions used to execute a restricted form of the
// standard dialect, and the handshake dialect.
//
//===----------------------------------------------------------------------===//

#ifndef CIRCT_DIALECT_HANDSHAKE_SIMULATION_H
#define CIRCT_DIALECT_HANDSHAKE_SIMULATION_H

#include "circt/Support/JSON.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/MLIRContext.h"
#include "llvm/ADT/StringMap.h"
#include <string>

namespace dynamatic {
namespace experimental {

bool simulate(llvm::StringRef toplevelFunction,
llvm::ArrayRef<std::string> inputArgs,
mlir::OwningOpRef<mlir::ModuleOp> &module,
mlir::MLIRContext &context,
llvm::StringMap<std::string> &config);
} // namespace dynamatic
} // namespace experimental


#endif
58 changes: 32 additions & 26 deletions experimental/tools/handshake-simulator/Simulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,21 @@
//
//===----------------------------------------------------------------------===//

#include <list>

#include "experimental/tools/handshake-simulator/Simulation.h"
#include "circt/Dialect/Handshake/HandshakeOps.h"
#include "circt/Dialect/Handshake/Simulation.h"
#include "circt/Support/JSON.h"
#include "mlir/Dialect/Arith/IR/Arith.h"
#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/MemRef/IR/MemRef.h"
#include "mlir/IR/BuiltinTypes.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/TypeSwitch.h"
#include "llvm/Support/Debug.h"

#include <list>

#define DEBUG_TYPE "runner"

#define INDEX_WIDTH 32
Expand All @@ -34,8 +36,8 @@ STATISTIC(simulatedTime, "Simulated Time");
using namespace llvm;
using namespace mlir;

namespace circt {
namespace handshake {
namespace dynamatic {
namespace experimental {

//===----------------------------------------------------------------------===//
// Utility functions
Expand Down Expand Up @@ -223,8 +225,8 @@ class HandshakeExecuter {
std::vector<std::vector<Any>> &store,
std::vector<double> &storeTimes);

/// Entry point for handshake::FuncOp top-level functions
HandshakeExecuter(handshake::FuncOp &func,
/// Entry point for circt::handshake::FuncOp top-level functions
HandshakeExecuter(circt::handshake::FuncOp &func,
llvm::DenseMap<mlir::Value, Any> &valueMap,
llvm::DenseMap<mlir::Value, double> &timeMap,
std::vector<Any> &results, std::vector<double> &resultTimes,
Expand Down Expand Up @@ -281,11 +283,11 @@ class HandshakeExecuter {
LogicalResult execute(mlir::cf::CondBranchOp, std::vector<Any> &,
std::vector<Any> &);
LogicalResult execute(func::ReturnOp, std::vector<Any> &, std::vector<Any> &);
LogicalResult execute(handshake::ReturnOp, std::vector<Any> &,
LogicalResult execute(circt::handshake::ReturnOp, std::vector<Any> &,
std::vector<Any> &);
LogicalResult execute(mlir::CallOpInterface, std::vector<Any> &,
std::vector<Any> &);
LogicalResult execute(handshake::InstanceOp, std::vector<Any> &,
LogicalResult execute(circt::handshake::InstanceOp, std::vector<Any> &,
std::vector<Any> &);

private:
Expand Down Expand Up @@ -574,7 +576,7 @@ LogicalResult HandshakeExecuter::execute(func::ReturnOp op,
return success();
}

LogicalResult HandshakeExecuter::execute(handshake::ReturnOp op,
LogicalResult HandshakeExecuter::execute(circt::handshake::ReturnOp op,
std::vector<Any> &in,
std::vector<Any> &) {
for (unsigned i = 0; i < results.size(); ++i) {
Expand Down Expand Up @@ -619,15 +621,15 @@ LogicalResult HandshakeExecuter::execute(mlir::CallOpInterface callOp,
return success();
}

LogicalResult HandshakeExecuter::execute(handshake::InstanceOp instanceOp,
LogicalResult HandshakeExecuter::execute(circt::handshake::InstanceOp instanceOp,
std::vector<Any> &in,
std::vector<Any> &out) {
// Execute the instance op and create associations in the current
// scope's value and time maps for the returned values.

if (auto funcSym = instanceOp->getAttr("module").cast<SymbolRefAttr>()) {
if (handshake::FuncOp func =
(*module)->lookupSymbol<handshake::FuncOp>(funcSym)) {
if (circt::handshake::FuncOp func =
(*module)->lookupSymbol<circt::handshake::FuncOp>(funcSym)) {
/// Prepare an InstanceOp for execution by creating a valueMap
/// containing associations between the arguments provided to the
/// intanceOp - available in the enclosing scope value map - and the
Expand Down Expand Up @@ -765,7 +767,7 @@ HandshakeExecuter::HandshakeExecuter(
}

HandshakeExecuter::HandshakeExecuter(
handshake::FuncOp &func, llvm::DenseMap<mlir::Value, Any> &valueMap,
circt::handshake::FuncOp &func, llvm::DenseMap<mlir::Value, Any> &valueMap,
llvm::DenseMap<mlir::Value, double> &timeMap, std::vector<Any> &results,
std::vector<double> &resultTimes, std::vector<std::vector<Any>> &store,
std::vector<double> &storeTimes, mlir::OwningOpRef<mlir::ModuleOp> &module)
Expand All @@ -783,13 +785,13 @@ HandshakeExecuter::HandshakeExecuter(

// Pre-allocate memory
func.walk([&](Operation *op) {
if (auto handshakeMemoryOp = dyn_cast<handshake::MemoryOpInterface>(op))
if (auto handshakeMemoryOp = dyn_cast<circt::handshake::MemoryOpInterface>(op))
if (!handshakeMemoryOp.allocateMemory(memoryMap, store, storeTimes))
llvm_unreachable("Memory op does not have unique ID!\n");
});

// Initialize the value map for buffers with initial values.
for (auto bufferOp : func.getOps<handshake::BufferOp>()) {
for (auto bufferOp : func.getOps<circt::handshake::BufferOp>()) {
if (bufferOp.getInitValues().has_value()) {
auto initValues = bufferOp.getInitValueArray();
assert(initValues.size() == 1 &&
Expand Down Expand Up @@ -823,7 +825,10 @@ HandshakeExecuter::HandshakeExecuter(
// TODO : Choose wether or not the ExecutableOP comes from the interface
// or from a user-specified model
// Execute handshake ops through ExecutableOpInterface
if (auto handshakeOp = dyn_cast<handshake::ExecutableOpInterface>(op)) {



if (auto handshakeOp = dyn_cast<circt::handshake::ExecutableOpInterface>(op)) {
std::vector<mlir::Value> scheduleList;
if (!handshakeOp.tryExecute(valueMap, memoryMap, timeMap, store,
scheduleList))
Expand Down Expand Up @@ -879,11 +884,11 @@ HandshakeExecuter::HandshakeExecuter(
mlir::arith::DivSIOp, mlir::arith::DivUIOp,
mlir::arith::DivFOp, mlir::arith::IndexCastOp,
mlir::arith::ExtSIOp, mlir::arith::ExtUIOp,
mlir::arith::XOrIOp, handshake::InstanceOp>([&](auto op) {
mlir::arith::XOrIOp, circt::handshake::InstanceOp>([&](auto op) {
strat = ExecuteStrategy::Default;
return execute(op, inValues, outValues);
})
.Case<handshake::ReturnOp>([&](auto op) {
.Case<circt::handshake::ReturnOp>([&](auto op) {
strat = ExecuteStrategy::Return;
return execute(op, inValues, outValues);
})
Expand Down Expand Up @@ -916,7 +921,8 @@ HandshakeExecuter::HandshakeExecuter(
//===----------------------------------------------------------------------===//

bool simulate(StringRef toplevelFunction, ArrayRef<std::string> inputArgs,
mlir::OwningOpRef<mlir::ModuleOp> &module, mlir::MLIRContext &) {
mlir::OwningOpRef<mlir::ModuleOp> &module, mlir::MLIRContext &,
llvm::StringMap<std::string> &config) {
// The store associates each allocation in the program
// (represented by a int) with a vector of values which can be
// accessed by it. Currently values are assumed to be an integer.
Expand Down Expand Up @@ -957,8 +963,8 @@ bool simulate(StringRef toplevelFunction, ArrayRef<std::string> inputArgs,
realInputs = inputs;
outputs = toplevel.getNumResults();
realOutputs = outputs;
} else if (handshake::FuncOp toplevel =
module->lookupSymbol<handshake::FuncOp>(toplevelFunction)) {
} else if (circt::handshake::FuncOp toplevel =
module->lookupSymbol<circt::handshake::FuncOp>(toplevelFunction)) {
ftype = toplevel.getFunctionType();
mlir::Block &entryBlock = toplevel.getBody().front();
blockArgs = entryBlock.getArguments();
Expand Down Expand Up @@ -1023,8 +1029,8 @@ bool simulate(StringRef toplevelFunction, ArrayRef<std::string> inputArgs,
succeeded = HandshakeExecuter(toplevel, valueMap, timeMap, results,
resultTimes, store, storeTimes)
.succeeded();
} else if (handshake::FuncOp toplevel =
module->lookupSymbol<handshake::FuncOp>(toplevelFunction)) {
} else if (circt::handshake::FuncOp toplevel =
module->lookupSymbol<circt::handshake::FuncOp>(toplevelFunction)) {
succeeded = HandshakeExecuter(toplevel, valueMap, timeMap, results,
resultTimes, store, storeTimes, module)
.succeeded();
Expand Down Expand Up @@ -1063,5 +1069,5 @@ bool simulate(StringRef toplevelFunction, ArrayRef<std::string> inputArgs,
return 0;
}

} // namespace handshake
} // namespace circt
} // namespace experimental
} // namespace dynamatic
94 changes: 86 additions & 8 deletions experimental/tools/handshake-simulator/handshake-simulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,31 @@
//
//===----------------------------------------------------------------------===//

#include "circt/Dialect/Handshake/HandshakeOps.h"
#include "circt/Support/JSON.h"
#include "circt/Support/Version.h"
#include "experimental/tools/handshake-simulator/Simulation.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
#include "mlir/Dialect/MemRef/IR/MemRef.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/MLIRContext.h"
#include "mlir/Parser/Parser.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/SourceMgr.h"
#include "circt/Dialect/Handshake/HandshakeOps.h"
#include "circt/Dialect/Handshake/Simulation.h"

#include <fstream>

#define DEFAULT_CONFIG_PATH \
"../experimental/data/handshake-simulator-configuration.json"

using namespace llvm;
using namespace mlir;
using namespace circt;

static cl::OptionCategory mainCategory("Application options");
static cl::OptionCategory configCategory("Configuration options");

static cl::opt<std::string> inputFileName(cl::Positional,
cl::desc("<input file>"),
Expand All @@ -41,6 +49,20 @@ static cl::opt<std::string>
cl::desc("The top-level function to execute"),
cl::init("main"), cl::cat(mainCategory));

static cl::list<std::string>
modelConfiguration("change-model",
cl::desc("Change execution model function "
"(--change-model <op name> <struct name>).\n"
"This does not affect the configuration file."),
cl::multi_val(2), cl::ZeroOrMore, cl::Optional,
cl::cat(configCategory));

static cl::opt<std::string>
jsonSelection("config",
cl::desc("Change the configuration file path.\n"
"Must be a relative path."),
cl::Optional, cl::cat(configCategory));

int main(int argc, char **argv) {
InitLLVM y(argc, argv);

Expand All @@ -56,8 +78,63 @@ int main(int argc, char **argv) {
"results are returned on stdout.\n"
"Memref types are specified as a comma-separated list of values.\n");

auto file_or_err = MemoryBuffer::getFileOrSTDIN(inputFileName.c_str());
if (std::error_code error = file_or_err.getError()) {
// Change JSON path if needed
std::string configPath = DEFAULT_CONFIG_PATH;
if (jsonSelection.getNumOccurrences() == 1) {
configPath = jsonSelection.getValue();
errs() << " configuration file changed to " << configPath << "\n";
}

// Load JSON model configuration
std::ifstream f;
f.open(configPath);

std::stringstream buffer;
buffer << f.rdbuf();
std::string jsonStr = buffer.str();
f.close();

auto jsonConfig = llvm::json::parse(StringRef(jsonStr));

if (!jsonConfig) {
errs() << "Configuration JSON could not be parsed"
<< "\n";
return 1;
}

if (!jsonConfig->getAsObject()) {
errs() << "Configuration JSON is not a valid JSON"
<< "\n";
return 1;
}

// JSON to map conversion
llvm::StringMap<std::string> modelConfigMap;
for (auto item : *jsonConfig->getAsObject()) {
modelConfigMap.insert(std::make_pair(
item.getFirst().str(), item.getSecond().getAsString().value().str()));
}

// Change the model configuration if the command is entered,
// without changing the JSON file
size_t nbChangedPair = modelConfiguration.getNumOccurrences() * 2;
if (nbChangedPair > 0) {
for (size_t i = 0; i < nbChangedPair; i += 2) {
std::string opToChange = modelConfiguration[i];
std::string modelName = modelConfiguration[i + 1];
if (!modelConfigMap.count(opToChange)) {
errs() << opToChange << " operation could not be found";
return 1;
}

modelConfigMap[opToChange] = modelName;
outs() << opToChange << " execution model changed to '" << modelName
<< "'\n";
}
}

auto fileOrErr = MemoryBuffer::getFileOrSTDIN(inputFileName.c_str());
if (std::error_code error = fileOrErr.getError()) {
errs() << argv[0] << ": could not open input file '" << inputFileName
<< "': " << error.message() << "\n";
return 1;
Expand All @@ -73,10 +150,10 @@ int main(int argc, char **argv) {
// cases.
context.allowUnregisteredDialects();

SourceMgr source_mgr;
source_mgr.AddNewSourceBuffer(std::move(*file_or_err), SMLoc());
SourceMgr sourceMgr;
sourceMgr.AddNewSourceBuffer(std::move(*fileOrErr), SMLoc());
mlir::OwningOpRef<mlir::ModuleOp> module(
mlir::parseSourceFile<ModuleOp>(source_mgr, &context));
mlir::parseSourceFile<ModuleOp>(sourceMgr, &context));
if (!module)
return 1;

Expand All @@ -88,5 +165,6 @@ int main(int argc, char **argv) {
return 1;
}

return handshake::simulate(toplevelFunction, inputArgs, module, context);
return dynamatic::experimental::simulate(toplevelFunction, inputArgs, module,
context, modelConfigMap);
}

0 comments on commit 27e7040

Please sign in to comment.