Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion tools/hls-fuzzer/AbstractWorker.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class AbstractWorker {
/// Creates a random C program that should be written to 'os'.
/// 'os' writes to a file called 'functionName.c'.
virtual void generate(llvm::raw_ostream &os,
llvm::StringRef functionName) const = 0;
llvm::StringRef functionName) = 0;

/// Result of verification.
enum VerificationResult {
Expand Down
2 changes: 2 additions & 0 deletions tools/hls-fuzzer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ target_link_libraries(hls-fuzzer PRIVATE MLIRSupport DynamaticHLSFuzzer)
target_include_directories(hls-fuzzer PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
# Also keep dynamatic up-to-date such that we are always testing the up-to-date verison.
add_dependencies(hls-fuzzer DynamaticHLSFuzzerOptsTableGen dynamatic)

add_subdirectory(oracles)
10 changes: 9 additions & 1 deletion tools/hls-fuzzer/Options.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,16 @@

namespace dynamatic {

enum class OracleKind {
Functional,
NonFunctional,
};

struct Options {
std::string dynamaticPath;
// Path of this executable.
std::string executablePath;
std::string dynamaticExecutablePath;
OracleKind kind = OracleKind::Functional;
};

} // namespace dynamatic
Expand Down
6 changes: 5 additions & 1 deletion tools/hls-fuzzer/OptionsParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,11 @@ dynamatic::OptionsParser::getPositionalArguments() const {
dynamatic::Options dynamatic::OptionsParser::apply(Options defaults) {
std::vector<std::string> arguments = getPositionalArguments();
if (arguments.size() == 1)
defaults.dynamaticPath = getPositionalArguments()[0];
defaults.dynamaticExecutablePath = getPositionalArguments()[0];

defaults.kind = args.hasFlag(OPT_functional, OPT_non_functional,
defaults.kind == OracleKind::Functional)
? OracleKind::Functional
: OracleKind::NonFunctional;
return defaults;
}
9 changes: 9 additions & 0 deletions tools/hls-fuzzer/Opts.td
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,12 @@ def num_threads : JoinedOrSeparate<["-"], "j">,
MetaVarName<"<num-threads>">,
HelpText<"Number of threads to use">;
def help : Flag<["--"], "help">, HelpText<"displays this help text">;

def grp_oracle : OptionGroup<"Oracle options">;

def functional : Flag<["--"], "functional">,
HelpText<"Switch to functional testing">,
Group<grp_oracle>;
def non_functional : Flag<["--"], "non-functional">,
HelpText<"Switch to non-functional testing">,
Group<grp_oracle>;
11 changes: 10 additions & 1 deletion tools/hls-fuzzer/hls-fuzzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "TargetRegistry.h"

#include "llvm/DebugInfo/LogicalView/Core/LVOptions.h"
#include "llvm/Support/FileSystem.h"

static std::atomic_bool quit = false;
static std::mutex errorMutex;
Expand Down Expand Up @@ -98,7 +99,15 @@ int main(int argc, char **argv) {
return -1;
}

auto options = optionsParser.apply(dynamatic::Options{});
dynamatic::Options defaults{};
#pragma clang diagnostic ignored "-Wmain"
defaults.executablePath = llvm::sys::fs::getMainExecutable(
argv[0], reinterpret_cast<void *>(&main));
defaults.dynamaticExecutablePath =
std::filesystem::path(defaults.executablePath).parent_path() /
"dynamatic";

auto options = optionsParser.apply(defaults);

std::optional<std::size_t> numThreads = optionsParser.getNumThreads();
if (!numThreads)
Expand Down
7 changes: 7 additions & 0 deletions tools/hls-fuzzer/oracles/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
add_llvm_executable(hls-fuzzer-check-bitwidth
check-bitwidth.cpp
)
llvm_update_compile_flags(hls-fuzzer-check-bitwidth)
target_link_libraries(hls-fuzzer-check-bitwidth PRIVATE DynamaticHandshake MLIRParser)

add_dependencies(hls-fuzzer hls-fuzzer-check-bitwidth)
69 changes: 69 additions & 0 deletions tools/hls-fuzzer/oracles/check-bitwidth.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@

#include "dynamatic/Dialect/Handshake/HandshakeDialect.h"
#include "mlir/Tools/ParseUtilities.h"

#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/raw_ostream.h"

#include <dynamatic/Dialect/Handshake/HandshakeOps.h>
#include <dynamatic/Dialect/Handshake/HandshakeTypes.h>

using namespace mlir;
using namespace dynamatic;

int main(int argc, char **argv) {
if (argc != 3) {
llvm::errs() << "expected exactly two arguments\n";
return -1;
}

StringRef mlirFile = argv[1];
StringRef bitwidthArg = argv[2];
std::uint32_t bitWidth;
if (bitwidthArg.getAsInteger(0, bitWidth)) {
llvm::errs() << "expected an integer as second argument\n";
return -1;
}

llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer =
llvm::MemoryBuffer::getFileOrSTDIN(mlirFile, true);
if (!buffer) {
llvm::errs() << "failed to open" << mlirFile << "\n";
return -1;
}

auto sourceMgr = std::make_shared<llvm::SourceMgr>();
sourceMgr->AddNewSourceBuffer(std::move(*buffer), SMLoc());
DialectRegistry registry;
registry.insert<handshake::HandshakeDialect>();
MLIRContext context(registry);
ParserConfig config(&context);
OwningOpRef<Operation *> module =
parseSourceFileForTool(sourceMgr, config, true);

WalkResult result = module->walk([&](Operation *op) {
for (Value iter : op->getResults()) {
auto channelType = dyn_cast<handshake::ChannelType>(iter.getType());
if (!channelType || !isa<IntegerType>(channelType.getDataType()))
continue;

// Results that feed into 'end' ops are allowed to use a higher bitwidth
// as it is required for interface conformance.
if (llvm::any_of(iter.getUsers(),
[](Operation *op) { return isa<handshake::EndOp>(op); }))
continue;

if (channelType.getDataBitWidth() > bitWidth) {
op->emitError("expected computation with at most a bitwidth of '")
<< bitWidth << "' rather than '" << channelType.getDataBitWidth()
<< "'";
return WalkResult::interrupt();
}
}
return WalkResult::advance();
});
if (result.wasInterrupted())
return -1;

return 0;
}
37 changes: 31 additions & 6 deletions tools/hls-fuzzer/targets/BitwidthOptimizationsTarget.cpp
Comment thread
zero9178 marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ class BitwidthOptimizationsGenerator : public AbstractWorker {
Randomly &&random)
: AbstractWorker(options, std::move(random)) {}

void generate(llvm::raw_ostream &os,
llvm::StringRef functionName) const override;
void generate(llvm::raw_ostream &os, llvm::StringRef functionName) override;

VerificationResult
verify(const std::filesystem::path &sourceFile) const override;

private:
std::uint8_t maxBitwidth;
};

} // namespace
Expand All @@ -38,10 +40,10 @@ BitwidthOptimizationsTarget::createWorker(const Options &options,
std::move(randomly));
}

void BitwidthOptimizationsGenerator::generate(
llvm::raw_ostream &os, llvm::StringRef functionName) const {
void BitwidthOptimizationsGenerator::generate(llvm::raw_ostream &os,
llvm::StringRef functionName) {
// Enforce a strict bitwidth requirement for the entire program.
auto maxBitwidth = random.getInteger<std::uint8_t>(1, 32);
maxBitwidth = random.getInteger<std::uint8_t>(1, 32);
gen::BitwidthTypeSystem bitwidthTypeSystem(maxBitwidth, random);
gen::BasicCGenerator generator(random, bitwidthTypeSystem,
/*entryContext=*/
Expand All @@ -60,5 +62,28 @@ void BitwidthOptimizationsGenerator::generate(

AbstractWorker::VerificationResult BitwidthOptimizationsGenerator::verify(
const std::filesystem::path &sourceFile) const {
return performDifferentialTesting(sourceFile, options.dynamaticPath);
switch (options.kind) {
case OracleKind::Functional:
return performDifferentialTesting(sourceFile,
options.dynamaticExecutablePath);
case OracleKind::NonFunctional:
break;
Comment thread
zero9178 marked this conversation as resolved.
Outdated
}

std::filesystem::path parentPath = sourceFile.parent_path();
std::string executeFile = (parentPath / "execute.sh").string();
llvm::cantFail(llvm::writeToOutput(
executeFile, [&](llvm::raw_ostream &os) -> llvm::Error {
os << "set -e\n";
outputDynamaticInvocation(os, sourceFile,
options.dynamaticExecutablePath, R"(
compile
)");
os << (std::filesystem::path(options.executablePath).parent_path() /
"hls-fuzzer-check-bitwidth")
<< " ./out/comp/handshake_export.mlir "
<< static_cast<unsigned>(maxBitwidth) << '\n';
return llvm::Error::success();
}));
return executeInWorkingDirectory(parentPath, "bash execute.sh");
}
2 changes: 2 additions & 0 deletions tools/hls-fuzzer/targets/BitwidthTypeSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ auto dynamatic::gen::BitwidthTypeSystem::checkBinaryExpression(
return std::nullopt;

case ast::BinaryExpression::ShiftRight:
// TODO: Figure out constraints here.
return std::nullopt;
case ast::BinaryExpression::Greater:
case ast::BinaryExpression::GreaterEqual:
case ast::BinaryExpression::Less:
Expand Down
8 changes: 4 additions & 4 deletions tools/hls-fuzzer/targets/RandomCTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ class RandomCWorker : public AbstractWorker {
explicit RandomCWorker(const Options &options, Randomly &&random)
: AbstractWorker(options, std::move(random)) {}

void generate(llvm::raw_ostream &os,
llvm::StringRef functionName) const override;
void generate(llvm::raw_ostream &os, llvm::StringRef functionName) override;

VerificationResult
verify(const std::filesystem::path &sourceFile) const override;
Expand All @@ -30,7 +29,7 @@ RandomCTarget::createWorker(const Options &options, Randomly randomly) const {
}

void RandomCWorker::generate(llvm::raw_ostream &os,
llvm::StringRef functionName) const {
llvm::StringRef functionName) {
gen::DynamaticTypeSystem dynamaticTypeSystem(random);
gen::BasicCGenerator generator(
random, dynamaticTypeSystem,
Expand All @@ -50,5 +49,6 @@ void RandomCWorker::generate(llvm::raw_ostream &os,

AbstractWorker::VerificationResult
RandomCWorker::verify(const std::filesystem::path &sourceFile) const {
return performDifferentialTesting(sourceFile, options.dynamaticPath);
return performDifferentialTesting(sourceFile,
options.dynamaticExecutablePath);
}
49 changes: 32 additions & 17 deletions tools/hls-fuzzer/targets/TargetUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,60 @@ dynamatic::performDifferentialTesting(const std::filesystem::path &sourceFile,
std::string executeFile = (parentPath / "execute.sh").string();
llvm::cantFail(llvm::writeToOutput(
executeFile, [&](llvm::raw_ostream &os) -> llvm::Error {
os << dynamaticPath << " --exit-on-failure <<EOF\n";
os << "set-dynamatic-path "
<< std::filesystem::path(dynamaticPath.str())
.parent_path()
.parent_path()
.string()
<< '\n';
os << "set-src " << sourceFile.filename().string();
os << R"(
outputDynamaticInvocation(os, sourceFile, dynamaticPath, R"(
compile
write-hdl
simulate
exit
EOF
)";
)");
return llvm::Error::success();
}));
return executeInWorkingDirectory(parentPath, "bash execute.sh");
}

void dynamatic::outputDynamaticInvocation(
llvm::raw_ostream &os, const std::filesystem::path &sourceFile,
llvm::StringRef dynamaticPath, llvm::StringRef script) {
// Compute the dynamatic home path by assuming it's a parent directory of the
// dynamatic executable and contains the scripts directory used to implement
// the various commands.
std::filesystem::path dynamaticSourceRoot = dynamaticPath.str();
while (!dynamaticSourceRoot.empty()) {
dynamaticSourceRoot = dynamaticSourceRoot.parent_path();
if (exists(dynamaticSourceRoot / "tools" / "dynamatic" / "scripts"))
break;
}

os << dynamaticPath << " --exit-on-failure <<EOF\n";
os << "set-dynamatic-path " << dynamaticSourceRoot.string() << '\n';
os << "set-src " << sourceFile.filename().string();
os << "\n" << script.trim() << "\nexit\nEOF\n";
}

dynamatic::AbstractWorker::VerificationResult
dynamatic::executeInWorkingDirectory(
const std::filesystem::path &workingDirectory,
llvm::StringRef bashCommand) {

// LLVM's process creation does not support changing the current working
// directory. We require this since dynamatic creates many of its artifacts
// in the working directory. Workaround this limitation using a wrapper
// script that performs a 'cd' to the directory it is contained in.
std::string executeCWDFile = (parentPath / "execute_cwd.sh").string();
std::string executeCWDFile = (workingDirectory / "execute_cwd.sh").string();
llvm::cantFail(llvm::writeToOutput(
executeCWDFile, [&](llvm::raw_ostream &os) -> llvm::Error {
os << R"a(SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
cd $SCRIPT_DIR && bash )a"
<< executeFile
cd $SCRIPT_DIR && )a"
<< bashCommand
// Canonicalize all error exists to exit code 1, even if dynamatic
// crashed with e.g. SIGSEGV. We need this to differentiate between
// bash exiting with a signal and dynamatic exiting with a signal.
<< "|| exit 1\n";
<< " || exit 1\n";
return llvm::Error::success();
}));

int exitCode = llvm::sys::ExecuteAndWait(
"/usr/bin/bash", {"bash", executeCWDFile}, /*Env=*/std::nullopt,
/*Redirects=*/{"", "", ""});

switch (exitCode) {
// Normal exit.
case 0:
Expand Down
19 changes: 19 additions & 0 deletions tools/hls-fuzzer/targets/TargetUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,25 @@ AbstractWorker::VerificationResult
performDifferentialTesting(const std::filesystem::path &sourceFile,
llvm::StringRef dynamaticPath);

/// Outputs a bash commandline to 'os' that invokes dynamatic and executes the
/// given 'script'.
/// 'sourceFile' is the source file to be compiled while 'dynamaticPath' refers
/// to the path to the dynamatic executable.
/// 'script' can assume that the source file and dynamatic home are already
/// set.
void outputDynamaticInvocation(llvm::raw_ostream &os,
const std::filesystem::path &sourceFile,
llvm::StringRef dynamaticPath,
llvm::StringRef script);

/// Executes a given 'bashCommand' in 'workingDirectory' by creating and
/// executing a script in 'workingDirectory'.
/// Normalizes the return code of the bash command to map failures to
/// 'VerificationResult::Bug'.
AbstractWorker::VerificationResult
executeInWorkingDirectory(const std::filesystem::path &workingDirectory,
llvm::StringRef bashCommand);

} // namespace dynamatic

#endif
Loading