#include "automemcpy/RandomFunctionGenerator.h"
#include <llvm/ADT/StringRef.h>
#include <llvm/Support/raw_ostream.h>
#include <optional>
#include <set>
namespace llvm {
namespace automemcpy {
static constexpr int kMaxIndividualSize = 4;
static constexpr int kMaxOverlapSize = 256;
static constexpr int kLoopMinIter = 3;
static constexpr int kAlignedLoopMinIter = 2;
static constexpr int kLoopBlockSize[] = {16, 32, 64};
static constexpr int kLoopAlignments[] = {16, 32, 64};
static constexpr int kAnchors[] = {0, 1, 2, 4, 8, 16, 32, 48,
64, 96, 128, 256, 512, 1024, kMaxSize};
static constexpr bool kDisableLoop = false;
static constexpr bool kDisableAlignedLoop = false;
static constexpr bool kDisableAccelerator = false;
static constexpr bool kExploreAlignmentArg = true;
static constexpr int kFunctionTypes[] = {
(int)FunctionType::MEMCPY,
(int)FunctionType::MEMCMP,
(int)FunctionType::MEMSET,
(int)FunctionType::BZERO,
};
static constexpr int kElementClasses[] = {
(int)ElementTypeClass::NATIVE,
};
RandomFunctionGenerator::RandomFunctionGenerator()
: Solver(Context), Type(Context.int_const("Type")),
ContiguousBegin(Context.int_const("ContiguousBegin")),
ContiguousEnd(Context.int_const("ContiguousEnd")),
OverlapBegin(Context.int_const("OverlapBegin")),
OverlapEnd(Context.int_const("OverlapEnd")),
LoopBegin(Context.int_const("LoopBegin")),
LoopEnd(Context.int_const("LoopEnd")),
LoopBlockSize(Context.int_const("LoopBlockSize")),
AlignedLoopBegin(Context.int_const("AlignedLoopBegin")),
AlignedLoopEnd(Context.int_const("AlignedLoopEnd")),
AlignedLoopBlockSize(Context.int_const("AlignedLoopBlockSize")),
AlignedAlignment(Context.int_const("AlignedAlignment")),
AlignedArg(Context.int_const("AlignedArg")),
AcceleratorBegin(Context.int_const("AcceleratorBegin")),
AcceleratorEnd(Context.int_const("AcceleratorEnd")),
ElementClass(Context.int_const("ElementClass")) {
Solver.add(inSetConstraint(Type, kFunctionTypes));
addBoundsAndAnchors(ContiguousBegin, ContiguousEnd);
addBoundsAndAnchors(OverlapBegin, OverlapEnd);
addBoundsAndAnchors(LoopBegin, LoopEnd);
addBoundsAndAnchors(AlignedLoopBegin, AlignedLoopEnd);
addBoundsAndAnchors(AcceleratorBegin, AcceleratorEnd);
Solver.add(ContiguousEnd == OverlapBegin);
Solver.add(OverlapEnd == LoopBegin);
Solver.add(LoopEnd == AlignedLoopBegin);
Solver.add(AlignedLoopEnd == AcceleratorBegin);
Solver.add(ContiguousBegin == 0);
Solver.add(AcceleratorEnd == kMaxSize);
Solver.add(ContiguousEnd <= kMaxIndividualSize + 1);
Solver.add(OverlapEnd <= kMaxOverlapSize + 1);
Solver.add(OverlapBegin == OverlapEnd || OverlapBegin >= 2);
addLoopConstraints(LoopBegin, LoopEnd, LoopBlockSize, kLoopMinIter);
addLoopConstraints(AlignedLoopBegin, AlignedLoopEnd, AlignedLoopBlockSize,
kAlignedLoopMinIter);
Solver.add(inSetConstraint(AlignedAlignment, kLoopAlignments));
Solver.add(AlignedLoopBegin == AlignedLoopEnd || AlignedLoopBegin >= 64);
Solver.add(AlignedLoopBlockSize >= AlignedAlignment);
Solver.add(AlignedLoopBlockSize >= LoopBlockSize);
z3::expr IsMemcpy = Type == (int)FunctionType::MEMCPY;
z3::expr ExploreAlignment = IsMemcpy && kExploreAlignmentArg;
Solver.add(
(ExploreAlignment &&
inSetConstraint(AlignedArg, {(int)AlignArg::_1, (int)AlignArg::_2})) ||
(!ExploreAlignment && AlignedArg == (int)AlignArg::_1));
Solver.add(IsMemcpy ||
(AcceleratorBegin ==
AcceleratorEnd));
Solver.add(inSetConstraint(ElementClass, kElementClasses));
if (kDisableLoop)
Solver.add(LoopBegin == LoopEnd);
if (kDisableAlignedLoop)
Solver.add(AlignedLoopBegin == AlignedLoopEnd);
if (kDisableAccelerator)
Solver.add(AcceleratorBegin == AcceleratorEnd);
}
static std::optional<SizeSpan> AsSizeSpan(size_t Begin, size_t End) {
if (Begin == End)
return std::nullopt;
SizeSpan SS;
SS.Begin = Begin;
SS.End = End;
return SS;
}
template <typename Region>
static std::optional<Region> As(size_t Begin, size_t End) {
if (auto Span = AsSizeSpan(Begin, End)) {
Region Output;
Output.Span = *Span;
return Output;
}
return std::nullopt;
}
static std::optional<Loop> AsLoop(size_t Begin, size_t End, size_t BlockSize) {
if (auto Span = AsSizeSpan(Begin, End)) {
Loop Output;
Output.Span = *Span;
Output.BlockSize = BlockSize;
return Output;
}
return std::nullopt;
}
static std::optional<AlignedLoop> AsAlignedLoop(size_t Begin, size_t End,
size_t BlockSize,
size_t Alignment,
AlignArg AlignTo) {
if (auto Loop = AsLoop(Begin, End, BlockSize)) {
AlignedLoop Output;
Output.Loop = *Loop;
Output.Alignment = Alignment;
Output.AlignTo = AlignTo;
return Output;
}
return std::nullopt;
}
std::optional<FunctionDescriptor> RandomFunctionGenerator::next() {
if (Solver.check() != z3::sat)
return {};
z3::model m = Solver.get_model();
const auto E = [&m](z3::expr &V) -> int {
return m.eval(V).get_numeral_int();
};
FunctionDescriptor R;
R.Type = FunctionType(E(Type));
R.Contiguous = As<Contiguous>(E(ContiguousBegin), E(ContiguousEnd));
R.Overlap = As<Overlap>(E(OverlapBegin), E(OverlapEnd));
R.Loop = AsLoop(E(LoopBegin), E(LoopEnd), E(LoopBlockSize));
R.AlignedLoop = AsAlignedLoop(E(AlignedLoopBegin), E(AlignedLoopEnd),
E(AlignedLoopBlockSize), E(AlignedAlignment),
AlignArg(E(AlignedArg)));
R.Accelerator = As<Accelerator>(E(AcceleratorBegin), E(AcceleratorEnd));
R.ElementClass = ElementTypeClass(E(ElementClass));
z3::expr CurrentLayout =
(Type == E(Type)) && (ContiguousBegin == E(ContiguousBegin)) &&
(ContiguousEnd == E(ContiguousEnd)) &&
(OverlapBegin == E(OverlapBegin)) && (OverlapEnd == E(OverlapEnd)) &&
(LoopBegin == E(LoopBegin)) && (LoopEnd == E(LoopEnd)) &&
(LoopBlockSize == E(LoopBlockSize)) &&
(AlignedLoopBegin == E(AlignedLoopBegin)) &&
(AlignedLoopEnd == E(AlignedLoopEnd)) &&
(AlignedLoopBlockSize == E(AlignedLoopBlockSize)) &&
(AlignedAlignment == E(AlignedAlignment)) &&
(AlignedArg == E(AlignedArg)) &&
(AcceleratorBegin == E(AcceleratorBegin)) &&
(AcceleratorEnd == E(AcceleratorEnd)) &&
(ElementClass == E(ElementClass));
Solver.add(!CurrentLayout);
return R;
}
z3::expr RandomFunctionGenerator::inSetConstraint(z3::expr &Variable,
ArrayRef<int> Values) const {
z3::expr_vector Args(Variable.ctx());
for (int Value : Values)
Args.push_back(Variable == Value);
return z3::mk_or(Args);
}
void RandomFunctionGenerator::addBoundsAndAnchors(z3::expr &Begin,
z3::expr &End) {
Solver.add(inSetConstraint(Begin, kAnchors));
Solver.add(inSetConstraint(End, kAnchors));
Solver.add(Begin >= 0);
Solver.add(Begin <= End);
Solver.add(End <= kMaxSize);
}
void RandomFunctionGenerator::addLoopConstraints(const z3::expr &LoopBegin,
const z3::expr &LoopEnd,
z3::expr &LoopBlockSize,
int LoopMinIter) {
Solver.add(inSetConstraint(LoopBlockSize, kLoopBlockSize));
Solver.add(LoopBegin == LoopEnd ||
(LoopBegin > (LoopMinIter * LoopBlockSize)));
}
}
}