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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion tools/hls-fuzzer/AST.h
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,15 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
const ArrayParameter &parameter);

/// Tag type representing the 'void' type from C.
struct VoidType {};
struct VoidType {
friend bool operator==(const VoidType &lhs, const VoidType &rhs) {
return true;
}

friend bool operator!=(const VoidType &lhs, const VoidType &rhs) {
return !(lhs == rhs);
}
};

inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const VoidType &) {
return os << "void";
Expand Down
47 changes: 11 additions & 36 deletions tools/hls-fuzzer/BasicCGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -377,25 +377,11 @@ gen::BasicCGenerator::generateReturnType(const OpaqueContext &context) const {
"It must always be possible to generate a return type");
}

constexpr std::size_t MAX_STATEMENTS = 10;

std::vector<ast::Statement>
gen::BasicCGenerator::generateStatementList(const OpaqueContext &context) {
std::vector<ast::Statement> result;
// TODO: Type systems should have better control over the number of
// statements and in what order they are generated.
// Right now they are always generated last statement to first.
std::size_t numStatements = random.getInteger<std::size_t>(0, MAX_STATEMENTS);
result.reserve(numStatements);
for (std::size_t i = 0; i < numStatements; i++) {
std::optional<ast::Statement> maybeStat = generateStatement(context);
if (!maybeStat)
break;

result.push_back(std::move(*maybeStat));
}
std::reverse(result.begin(), result.end());
return result;
return typeSystem.generateStatementListOpaque(
context,
[=](const OpaqueContext &context) { return generateStatement(context); });
}

std::optional<ast::Statement>
Expand All @@ -406,25 +392,14 @@ gen::BasicCGenerator::generateStatement(const OpaqueContext &context) {
std::optional<ast::ArrayAssignmentStatement>
gen::BasicCGenerator::generateArrayAssignmentStatement(
const OpaqueContext &context) {
auto conclusion = typeSystem.checkArrayAssignmentStatementOpaque(context);
if (!conclusion)
return std::nullopt;

auto &&[param, index, value] = *conclusion;
std::optional<ast::ArrayParameter> parameter = generateArrayParameter(param);
if (!parameter)
return std::nullopt;

ast::Expression castAsNeeded = safeCastAsNeeded(ast::PrimitiveType::UInt32,
generateExpression(index, 0));
castAsNeeded = ast::BinaryExpression{
std::move(castAsNeeded), ast::BinaryExpression::BitAnd,
ast::Constant{static_cast<std::uint32_t>(parameter->getDimension() - 1)}};
return ast::ArrayAssignmentStatement{
parameter->getName().str(),
castAsNeeded,
generateExpression(value, 0),
};
return typeSystem.generateArrayAssignmentStatementOpaque(
context,
[=](const OpaqueContext &context) {
return generateArrayParameter(context);
},
[=](const OpaqueContext &context) {
return generateExpression(context, 0);
});
}

ast::Function gen::BasicCGenerator::generate(std::string_view functionName) {
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 @@ -29,6 +29,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
216 changes: 216 additions & 0 deletions tools/hls-fuzzer/ConjunctionTypeSystem.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
#ifndef HLS_FUZZER_CONJUNCTION_TYPE_SYSTEM
#define HLS_FUZZER_CONJUNCTION_TYPE_SYSTEM

#include "TypeSystem.h"
#include <tuple>

namespace dynamatic::gen {

/// Base class for creating type systems by combining multiple independent type
/// systems.
/// The derived class should be specified as the 'Self' parameter while the
/// subtype systems should be specified as 'SubTypeSystem' parameter.
///
/// The typing context of the type system is a tuple of all contexts of the
/// subtype systems. All 'check*' methods have a default implementation which
/// calls all 'check*' methods of all subtype systems.
/// If any of the methods return an empty optional, the AST node is discarded.
///
/// Note that 'generate*' methods of subtype systems do *not* compose and are
/// completely ignored.
/// These have to be reimplemented in the class deriving from
/// 'ConjunctionTypeSystem'.
template <typename Self, class... SubTypeSystem>
class ConjunctionTypeSystem
: public TypeSystem<std::tuple<typename SubTypeSystem::Context...>, Self> {
public:
using Base = TypeSystem<std::tuple<typename SubTypeSystem::Context...>, Self>;
using Context = typename Base::Context;

explicit ConjunctionTypeSystem(Randomly &randomly)
: Base(randomly), typeSystems(SubTypeSystem(randomly)...) {}

// Default implementations of all 'check*' methods follow.

ConclusionOf<ast::Function, Context> checkFunction(const Context &context) {
// Functions cannot be discarded hence the optional will never be empty.
return *combineChecks<ast::Function>(
[&](auto &&typeSystem, auto &&context) {
return typeSystem.checkFunction(context);
},
context);
}

std::optional<ConclusionOf<ast::BinaryExpression, Context>>
checkBinaryExpression(ast::BinaryExpression::Op op, const Context &context) {
return combineChecks<ast::BinaryExpression>(
[&](auto &&typeSystem, auto &&context) {
return typeSystem.checkBinaryExpression(op, context);
},
context);
}

std::optional<ConclusionOf<ast::CastExpression, Context>>
checkCastExpression(const Context &context) {
return combineChecks<ast::CastExpression>(
[&](auto &&typeSystem, auto &&context) {
return typeSystem.checkCastExpression(context);
},
context);
}

std::optional<ConclusionOf<ast::ConditionalExpression, Context>>
checkConditionalExpression(const Context &context) {
return combineChecks<ast::ConditionalExpression>(
[&](auto &&typeSystem, auto &&context) {
return typeSystem.checkConditionalExpression(context);
},
context);
}

std::optional<ConclusionOf<ast::Variable, Context>>
checkVariable(const Context &context) {
return combineChecks<ast::Variable>(
[&](auto &&typeSystem, auto &&context) {
return typeSystem.checkVariable(context);
},
context);
}

std::optional<ConclusionOf<ast::ScalarParameter, Context>>
checkScalarParameter(const ast::ScalarParameter &scalarParameter,
const Context &context) {
return combineChecks<ast::ScalarParameter>(
[&](auto &&typeSystem, auto &&context) {
return typeSystem.checkScalarParameter(scalarParameter, context);
},
context);
}

std::optional<ConclusionOf<ast::ArrayParameter, Context>>
checkArrayParameter(const ast::ArrayParameter &arrayParameter,
const Context &context) {
return combineChecks<ast::ArrayParameter>(
[&](auto &&typeSystem, auto &&context) {
return typeSystem.checkArrayParameter(arrayParameter, context);
},
context);
}

std::optional<ConclusionOf<ast::ScalarType, Context>>
checkScalarType(const ast::ScalarType &scalarType, const Context &context) {
return combineChecks<ast::ScalarType>(
[&](auto &&typeSystem, auto &&context) {
return typeSystem.checkScalarType(scalarType, context);
},
context);
}

std::optional<ConclusionOf<ast::ReturnType, Context>>
checkReturnType(const ast::ReturnType &returnType, const Context &context) {
return combineChecks<ast::ScalarType>(
[&](auto &&typeSystem, auto &&context) {
return typeSystem.checkReturnType(returnType, context);
},
context);
}

std::optional<ConclusionOf<ast::ArrayReadExpression, Context>>
checkArrayReadExpression(const Context &context) {
return combineChecks<ast::ArrayReadExpression>(
[&](auto &&typeSystem, auto &&context) {
return typeSystem.checkArrayReadExpression(context);
},
context);
}

std::optional<ConclusionOf<ast::ArrayAssignmentStatement, Context>>
checkArrayAssignmentStatement(const Context &context) {
return combineChecks<ast::ArrayAssignmentStatement>(
[&](auto &&typeSystem, auto &&context) {
return typeSystem.checkArrayAssignmentStatement(context);
},
context);
}

protected:
/// Convenience template method which combines the result of calling
/// 'checkCallback' with a specific subtype system and corresponding context
/// with 'context' as input context.
/// 'checkCallback' should be generic and allow any type as the first
/// typesystem parameter and any context as the second parameter.
/// 'ASTNode' is the node being type checked and whose conclusion type should
/// be returned.
///
/// Returns an empty optional if any of the subtype systems returns an empty
/// optional.
template <typename ASTNode, typename F>
std::optional<ConclusionOf<ASTNode, Context>>
combineChecks(F &&checkCallback, const Context &context) {
// Call the check method on every type system.
// Effectively does a 'zip' operation on both the 'typeSystems' tuple and
// the 'context' tuple.
auto subChecks = std::apply(
[&](auto... indices) {
return std::make_tuple(std::optional{
checkCallback(std::get<decltype(indices)::value>(typeSystems),
std::get<decltype(indices)::value>(context))}...);
},
getIndicesTuple());

// Check whether any of them are empty.
if (std::apply(
[](auto &&...optionals) {
return (false || ... || !optionals.has_value());
},
subChecks))
return std::nullopt;

if constexpr (std::is_same_v<ConclusionOf<ASTNode, Context>, Context>) {
// Conclusion type is just 'Context'. Construct it by dereferencing the
// optionals.
return std::apply(
[&](auto &&...subChecks) {
return std::make_tuple(std::move(*subChecks)...);
},
std::move(subChecks));
} else {
// Conclusion type is a tuple of 'Context's.
// We need to combine all the sub-conclusion types by creating tuples
// (Context's) for every element of the conclusion type.
return std::apply(
[&](auto &&...subChecks) {
return std::apply(
[&](auto... indices) {
return ConclusionOf<ASTNode, Context>{[&](auto index) {
// For the element at the given index in the conclusion
// type, create a Context from all the sub-conclusion types.
return std::make_tuple(
get<decltype(index)::value>(std::move(*subChecks))...);
}(indices)...};
},
// Indices for every element of the conclusion type.
getIndicesTuple(
std::make_index_sequence<
std::tuple_size_v<ConclusionOf<ASTNode, Context>>>{}));
},
std::move(subChecks));
}
}

std::tuple<SubTypeSystem...> typeSystems;

private:
template <std::size_t... is>
constexpr auto getIndicesTuple(std::index_sequence<is...>) {
return std::tuple{std::integral_constant<std::size_t, is>{}...};
}

constexpr auto getIndicesTuple() {
return getIndicesTuple(std::index_sequence_for<SubTypeSystem...>{});
}
};

} // namespace dynamatic::gen

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

dynamatic::gen::AbstractTypeSystem::~AbstractTypeSystem() {}

dynamatic::gen::NoopTypeSystem::~NoopTypeSystem() = default;
Loading