Program Listing for File BufferInfo.cpp

↰ Return to documentation for file (lib/Target/LaksaCommon/BufferInfo.cpp)

// Implementation of the buffer analysis shared by the laksa-hls-kria-driver
// translations
//
// @file
// @author     Jiahong Bi (jiahong.bi@tu-dresden.de)

#include "laksa-mlir/Target/LaksaCommon/BufferInfo.h"

#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/StringExtras.h"

using namespace mlir;
using namespace mlir::emithls;

static LogicalResult getElementBitWidth(Location loc, Type type, unsigned &bits)
{
    if (auto intTy = dyn_cast<IntegerType>(type)) {
        bits = intTy.getWidth();
        return success();
    }
    if (auto floatTy = dyn_cast<FloatType>(type)) {
        bits = floatTy.getIntOrFloatBitWidth();
        return success();
    }
    return emitError(
               loc,
               "unsupported pointer element type for buffer "
               "sizing: ")
           << type;
}

// Returns the number of times `op` executes, i.e. the product of the trip
// counts of every emithls.for enclosing it up to (but not including)
// `boundary`.
static int64_t countEnclosingIterations(Operation* op, Operation* boundary)
{
    int64_t count = 1;
    for (Operation* parent = op->getParentOp(); parent && parent != boundary;
         parent = parent->getParentOp())
        if (auto forOp = dyn_cast<ForOp>(parent)) count *= forOp.getTripCount();
    return count;
}

// Follows `pointer` through every array.ptr_read/array.ptr_write that
// touches it directly, and through every emithls.call that forwards it into
// a callee's argument, accumulating direction and size facts into `info`.
static LogicalResult analyzeBuffer(
    Value pointer,
    LaksaBufferInfo &info,
    llvm::SmallPtrSetImpl<Value> &visited)
{
    if (!visited.insert(pointer).second) return success();

    for (Operation* user : pointer.getUsers()) {
        if (auto readOp = dyn_cast<ArrayPointerReadOp>(user)) {
            if (readOp.getPointer() != pointer) continue;
            unsigned bits;
            if (failed(getElementBitWidth(
                    readOp.getLoc(),
                    readOp.getResult().getType(),
                    bits)))
                return failure();
            info.isReadByKernel = true;
            info.elementBits = bits;
            FuncOp parentFunc = readOp->getParentOfType<FuncOp>();
            info.numElements = std::max(
                info.numElements,
                countEnclosingIterations(readOp, parentFunc));
        } else if (auto writeOp = dyn_cast<ArrayPointerWriteOp>(user)) {
            if (writeOp.getPointer() != pointer) continue;
            unsigned bits;
            if (failed(getElementBitWidth(
                    writeOp.getLoc(),
                    writeOp.getValue().getType(),
                    bits)))
                return failure();
            info.isWrittenByKernel = true;
            info.elementBits = bits;
            FuncOp parentFunc = writeOp->getParentOfType<FuncOp>();
            info.numElements = std::max(
                info.numElements,
                countEnclosingIterations(writeOp, parentFunc));
        } else if (auto callOp = dyn_cast<CallOp>(user)) {
            auto moduleOp = callOp->getParentOfType<ModuleOp>();
            auto callee = moduleOp.lookupSymbol<FuncOp>(callOp.getCalleeAttr());
            if (!callee) continue;
            for (auto [idx, operand] :
                 llvm::enumerate(callOp.getArgOperands())) {
                if (operand != pointer) continue;
                if (failed(
                        analyzeBuffer(callee.getArgument(idx), info, visited)))
                    return failure();
            }
        }
    }
    return success();
}

FailureOr<FuncOp> emithls::findLaksaTopFunc(ModuleOp moduleOp)
{
    FuncOp topFunc = nullptr;
    auto result = moduleOp.walk([&](PragmaTopInterfaceOp topInterfaceOp) {
        FuncOp funcOp = topInterfaceOp->getParentOfType<FuncOp>();
        if (!funcOp || topFunc) return WalkResult::interrupt();
        topFunc = funcOp;
        return WalkResult::advance();
    });
    if (result.wasInterrupted() || !topFunc)
        return moduleOp->emitError(
            "expected exactly one function containing a top_interface "
            "pragma");
    return topFunc;
}

LogicalResult emithls::collectLaksaBufferInfos(
    FuncOp topFunc,
    SmallVectorImpl<LaksaBufferInfo> &buffers)
{
    buffers.assign(topFunc.getNumArguments(), LaksaBufferInfo{});
    for (auto [idx, info] : llvm::enumerate(buffers)) {
        llvm::SmallPtrSet<Value, 8> visited;
        if (failed(analyzeBuffer(topFunc.getArgument(idx), info, visited)))
            return failure();
        if (info.numElements == 0)
            return topFunc.emitError(
                       "could not determine buffer size for argument ")
                   << idx
                   << "; expected an array.ptr_read or array.ptr_write "
                      "reachable through it";
    }
    return success();
}

std::string emithls::getLaksaMacroPrefix(StringRef designName)
{
    std::string prefix = designName.str();
    for (char &c : prefix) c = llvm::toUpper(c);
    return prefix;
}