Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions tools/hls-fuzzer/AST.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,9 @@ class Expression {
/// Returns the type of the given expression.
ScalarType getType() const;

template <typename From>
friend struct llvm::simplify_type;

private:
std::shared_ptr<const Variant> expression;
};
Expand Down Expand Up @@ -883,4 +886,14 @@ struct llvm::simplify_type<dynamatic::ast::ReturnType> {
}
};

template <>
struct llvm::simplify_type<dynamatic::ast::Expression> {
using SimpleType = const dynamatic::ast::Expression::Variant;

static SimpleType &
getSimplifiedValue(const dynamatic::ast::Expression &expression) {
return *expression.expression;
}
};

#endif
8 changes: 3 additions & 5 deletions tools/hls-fuzzer/BasicCGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -534,11 +534,7 @@ gen::BasicCGenerator::generateArrayAssignmentStatement(
},
/*index=*/
[&](OpaqueContext &&context) {
auto [expression, outputContext] =
generateExpression(std::move(context));
expression = safeCastAsNeeded(
/*to=*/ast::PrimitiveType::UInt32, std::move(expression));
return std::pair(std::move(expression), std::move(outputContext));
return generateExpression(std::move(context));
},
/*value=*/
[&](OpaqueContext &&context) {
Expand All @@ -547,6 +543,8 @@ gen::BasicCGenerator::generateArrayAssignmentStatement(
/*constructor=*/
[&](ast::ArrayParameter &&param, ast::Expression &&index,
ast::Expression &&value) {
index = safeCastAsNeeded(
/*to=*/ast::PrimitiveType::UInt32, std::move(index));
index = ast::BinaryExpression{std::move(index),
ast::BinaryExpression::BitAnd,
ast::Constant{static_cast<std::uint32_t>(
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 @@ -30,6 +30,8 @@ add_llvm_executable(hls-fuzzer
targets/BitwidthOptimizationsTarget.cpp
targets/BitwidthTypeSystem.cpp
targets/DynamaticTypeSystem.cpp
targets/LSQNoDepTypeSystem.cpp
targets/LSQOptimizationsTarget.cpp
targets/RandomCTarget.cpp
targets/TargetUtils.cpp
)
Expand Down
10 changes: 10 additions & 0 deletions tools/hls-fuzzer/oracles/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
add_llvm_executable(hls-fuzzer-check-bitwidth
check-bitwidth.cpp
PARTIAL_SOURCES_INTENDED
)
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)

add_llvm_executable(hls-fuzzer-check-no-lsq
check-no-lsq.cpp
PARTIAL_SOURCES_INTENDED
)
llvm_update_compile_flags(hls-fuzzer-check-no-lsq)
target_link_libraries(hls-fuzzer-check-no-lsq PRIVATE DynamaticHandshake MLIRParser)

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

#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"

using namespace mlir;
using namespace dynamatic;

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

StringRef mlirFile = argv[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, arith::ArithDialect>();
MLIRContext context(registry);
ParserConfig config(&context);
OwningOpRef<Operation *> module =
parseSourceFileForTool(sourceMgr, config, true);

WalkResult result = module->walk([&](handshake::LSQOp) {
llvm::errs() << "IR must not contain an LSQ\n";
return WalkResult::interrupt();
});
if (result.wasInterrupted())
return -1;

return 0;
}
4 changes: 1 addition & 3 deletions tools/hls-fuzzer/targets/DynamaticTypeSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ struct DynamaticTypingContext {
/// Expression must be of an integer type.
IntegerRequired,
MAX_VALUE = IntegerRequired,
} constraint;
} constraint = Unconstrained;
};

/// Custom type system that avoids expressions that dynamatic is known not to
Expand All @@ -27,8 +27,6 @@ struct DynamaticTypingContext {
class DynamaticTypeSystem final
: public TypeSystem<DynamaticTypingContext, DynamaticTypeSystem> {
public:
explicit DynamaticTypeSystem(Randomly &) {}

TransferFnArray<ast::Function> getFunctionTransferFns() override {
return {
/*return type=*/copyFromInput<ast::Function>(),
Expand Down
34 changes: 34 additions & 0 deletions tools/hls-fuzzer/targets/LSQNoDepTypeSystem.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "LSQNoDepTypeSystem.h"

dynamatic::gen::TransferFnArray<dynamatic::ast::ArrayReadExpression> dynamatic::
gen::detail::LSQNoDepTypeSystemInner::getArrayReadExpressionTransferFns() {
return {
copyFromInput<ast::ArrayReadExpression>(),
TransferFn<ast::ArrayReadExpression, INPUT_DEPENDENCY>(
[](LSQNoDepContext context) {
context.inArrayReadIndexExpression = true;
return context;
}),
copyInputToOutput<ast::ArrayReadExpression>(),
};
}

dynamatic::gen::TransferFnArray<dynamatic::ast::ArrayAssignmentStatement>
dynamatic::gen::detail::LSQNoDepTypeSystemInner::
getArrayAssignmentStatementTransferFns() {
return {
copyFromInput<ast::ArrayAssignmentStatement>(),
copyFromInput<ast::ArrayAssignmentStatement>(),
TransferFn<ast::ArrayAssignmentStatement,
ast::ArrayAssignmentStatement::ARRAY,
ast::ArrayAssignmentStatement::INDEX>(
[](const LSQNoDepContext &, const ast::ArrayParameter &parameter,
const LSQNoDepContext &, const ast::Expression &index) {
return LSQNoDepContext{&parameter,
// Successful cast guaranteed by every other
// kind of expression being discarded.
&llvm::cast<ast::Variable>(index)};
}),
copyInputToOutput<ast::ArrayAssignmentStatement>(),
};
}
131 changes: 131 additions & 0 deletions tools/hls-fuzzer/targets/LSQNoDepTypeSystem.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#ifndef DYNAMATIC_HLS_FUZZER_TARGETS_LSQNODEPTYPESYSTEM
#define DYNAMATIC_HLS_FUZZER_TARGETS_LSQNODEPTYPESYSTEM

#include "DynamaticTypeSystem.h"
#include "hls-fuzzer/ConjunctionTypeSystem.h"
#include "hls-fuzzer/LimitTypeSystem.h"
#include "hls-fuzzer/TypeSystem.h"

namespace dynamatic::gen {

struct LSQNoDepContext {
/// The array parameter + index variable that is being written to in the
/// current statement, or nullptr if not.
const ast::ArrayParameter *arrayWritten = nullptr;
const ast::Variable *indexVariable = nullptr;
/// True iff the context is within the sub-tree of the indexing expression of
/// an array read expression.
bool inArrayReadIndexExpression = false;
};

namespace detail {

/// Subtype system used for the LSQ related logic.
class LSQNoDepTypeSystemInner final
: public TypeSystem<LSQNoDepContext, LSQNoDepTypeSystemInner> {
public:
static bool discardReturnType(const ast::ReturnType &returnType,
const LSQNoDepContext &) {
// Force a void return function.
return returnType != ast::ReturnType{ast::VoidType{}};
}

static bool discardArrayReadExpression(const LSQNoDepContext &context) {
return !context.arrayWritten || !context.indexVariable ||
context.inArrayReadIndexExpression;
}

TransferFnArray<ast::ArrayReadExpression>
getArrayReadExpressionTransferFns() override;

TransferFnArray<ast::ArrayAssignmentStatement>
getArrayAssignmentStatementTransferFns() override;

static bool discardStructuredForStatement(const LSQNoDepContext &) {
// Only array assignment statements should be allowed.
return true;
}

static bool discardBinaryExpression(ast::BinaryExpression::Op,
const LSQNoDepContext &context) {
return context.inArrayReadIndexExpression || !context.indexVariable;
}

static bool discardUnaryExpression(ast::UnaryExpression::Op,
const LSQNoDepContext &context) {
return context.inArrayReadIndexExpression || !context.indexVariable;
}

static bool
discardExistingScalarParameter(const ast::ScalarParameter &parameter,
const LSQNoDepContext &context) {
// In an indexing expression, the scalar parameter name must match the index
// variable.
if (!context.inArrayReadIndexExpression)
return false;

return parameter.getName() != context.indexVariable->name;
}

static bool discardFreshScalarParameter(const LSQNoDepContext &context) {
return context.inArrayReadIndexExpression;
}

static bool
discardExistingArrayParameter(const ast::ArrayParameter &parameter,
const LSQNoDepContext &context) {
if (!context.arrayWritten)
return false;

return parameter.getName() != context.arrayWritten->getName();
}

static bool discardFreshArrayParameter(const LSQNoDepContext &context) {
return context.arrayWritten;
}

static bool discardCastExpression(const LSQNoDepContext &context) {
return context.inArrayReadIndexExpression || !context.indexVariable;
}

static bool discardConditionalExpression(const LSQNoDepContext &context) {
return context.inArrayReadIndexExpression || !context.indexVariable;
}

std::optional<ast::Constant> discardConstant(const ast::Constant &constant,
const LSQNoDepContext &context) {
if (context.inArrayReadIndexExpression || !context.indexVariable)
return std::nullopt;

return TypeSystem::discardConstant(constant, context);
}
};

} // namespace detail

/// Type system used to generate code that requires no LSQ to enforce ordering
/// constraints of memory.
/// Concretely, this means that:
/// * For any Write-after-Read (WAR), the write operation must be data dependent
/// on the read, guaranteed not to alias OR have a control dependency on the
/// read.
/// * For any Write-after-Write (WAW) or Read-after-Write (RAW) the operations
/// must not alias.
///
/// The current implementation only ever generates a single WAR construct where
/// the write is data dependent or control dependent on the read.
class LSQNoDepTypeSystem final
: public ConjunctionTypeSystemBase<LSQNoDepTypeSystem,
detail::LSQNoDepTypeSystemInner,
DynamaticTypeSystem, LimitTypeSystem> {
public:
explicit LSQNoDepTypeSystem()
: ConjunctionTypeSystemBase(detail::LSQNoDepTypeSystemInner(),
DynamaticTypeSystem(),
LimitTypeSystem(/*maxExpressionDepth=*/8,
/*maxTotalStatements=*/1)) {}
};

} // namespace dynamatic::gen

#endif
53 changes: 53 additions & 0 deletions tools/hls-fuzzer/targets/LSQOptimizationsTarget.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#include "LSQOptimizationsTarget.h"

#include "LSQNoDepTypeSystem.h"
#include "TargetUtils.h"
#include "hls-fuzzer/BasicCGenerator.h"
#include "hls-fuzzer/TargetRegistry.h"

namespace {
REGISTER_TARGET("lsq-elision", dynamatic::LSQOptimizationsTarget);
} // namespace

using namespace dynamatic;

namespace {
class LSQOptimizationsWorker : public AbstractWorker {
public:
explicit LSQOptimizationsWorker(const Options &options, Randomly &&random)
: AbstractWorker(options, std::move(random)) {}

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

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

} // namespace

std::unique_ptr<AbstractWorker>
LSQOptimizationsTarget::createWorker(const Options &options,
Randomly randomly) const {
return std::make_unique<LSQOptimizationsWorker>(options, std::move(randomly));
}

void LSQOptimizationsWorker::generate(llvm::raw_ostream &os,
llvm::StringRef functionName) {
gen::LSQNoDepTypeSystem typeSystem;
gen::BasicCGenerator generator(random, typeSystem);
generator.generate(os, functionName);
}

constexpr std::string_view ORACLE_EXECUTABLE = "hls-fuzzer-check-no-lsq";
constexpr std::string_view COMPILATION_IR_OUTPUT =
"./out/comp/handshake_export.mlir";

AbstractWorker::VerificationResult
LSQOptimizationsWorker::verify(const std::filesystem::path &sourceFile) const {
return performNonFunctionalTesting(
sourceFile, options.dynamaticExecutablePath,
(std::filesystem::path(options.executablePath).parent_path() /
ORACLE_EXECUTABLE)
.string(),
{COMPILATION_IR_OUTPUT});
}
16 changes: 16 additions & 0 deletions tools/hls-fuzzer/targets/LSQOptimizationsTarget.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef DYNAMATIC_HLS_FUZZER_TARGETS_LSQOPTIMIZATIONSTARGET
#define DYNAMATIC_HLS_FUZZER_TARGETS_LSQOPTIMIZATIONSTARGET

#include "hls-fuzzer/AbstractTarget.h"

namespace dynamatic {

class LSQOptimizationsTarget : public AbstractTarget {
public:
std::unique_ptr<AbstractWorker>
createWorker(const Options &options, Randomly randomly) const override;
};

} // namespace dynamatic

#endif
11 changes: 2 additions & 9 deletions tools/hls-fuzzer/targets/RandomCTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,8 @@ RandomCTarget::createWorker(const Options &options, Randomly randomly) const {
void RandomCWorker::generate(llvm::raw_ostream &os,
llvm::StringRef functionName) {
gen::ConjunctionTypeSystem<gen::DynamaticTypeSystem, gen::LimitTypeSystem>
dynamaticTypeSystem{gen::DynamaticTypeSystem(random),
gen::LimitTypeSystem()};
gen::BasicCGenerator generator(
random, dynamaticTypeSystem,
/*entryContext=*/
{
{gen::DynamaticTypingContext::Unconstrained},
{},
});
dynamaticTypeSystem{gen::DynamaticTypeSystem(), gen::LimitTypeSystem()};
gen::BasicCGenerator generator(random, dynamaticTypeSystem);
generator.generate(os, functionName);
}

Expand Down
Loading