<commit_before>#include <iostream>
#include <sstream>
#include <mutex>

#include "LLVM_Headers.h"
#include "CodeGen_Hexagon.h"
#include "CodeGen_Internal.h"
#include "IROperator.h"
#include "IRMatch.h"
#include "IREquality.h"
#include "IRMutator.h"
#include "Target.h"
#include "Debug.h"
#include "Util.h"
#include "Simplify.h"
#include "IRPrinter.h"
#include "EliminateBoolVectors.h"
#include "HexagonOptimize.h"
#include "AlignLoads.h"
#include "CSE.h"
#include "LoopCarry.h"

namespace Halide {
namespace Internal {

using std::vector;
using std::string;

using namespace llvm;

// LLVM Hexagon HVX intrinsics are broken up into 64B and 128B versions, for example,
// llvm::Intrinsic::hexagon_V6_vaddh and llvm::Intrinsic::hexagon_V6_vaddh_128B. This
// macro selects the 64B or 128B mode depending on the value of is_128B. There's a
// further dirty hack here: these intrinsics aren't defined in LLVM older than 3.9. To
// avoid needing to #ifdef random patches of code, we just replace all LLVM intrinsics
// with not_intrinsic.
#ifdef WITH_HEXAGON

#define IPICK(is_128B, i64) (is_128B ? i64##_128B : i64)
#else
#define IPICK(is_128B, i64) (is_128B ? Intrinsic::not_intrinsic : Intrinsic::not_intrinsic)
#endif

CodeGen_Hexagon::CodeGen_Hexagon(Target t) : CodeGen_Posix(t) {
#if !(WITH_HEXAGON)
    user_error << "hexagon not enabled for this build of Halide.\n";
#endif
#if LLVM_VERSION < 50
    user_assert(!t.has_feature(Target::HVX_v62))
        << "llvm 5.0 or later is required for Hexagon v62.\n";
    user_assert(!t.has_feature(Target::HVX_v65))
        << "llvm 5.0 or later is required for Hexagon v65.\n";
    user_assert(!t.has_feature(Target::HVX_v66))
        << "llvm 5.0 or later is required for Hexagon v66.\n";
#endif
    user_assert(llvm_Hexagon_enabled) << "llvm build not configured with Hexagon target enabled.\n";
}

std::unique_ptr<llvm::Module> CodeGen_Hexagon::compile(const Module &module) {
    auto llvm_module = CodeGen_Posix::compile(module);

    // TODO: This should be set on the module itself, or some other
    // safer way to pass this through to the target specific lowering
    // passes. We set the option here (after the base class'
    // implementation of compile) because it is the last
    // Hexagon-specific code to run prior to invoking the target
    // specific lowering in LLVM, minimizing the chances of the wrong
    // flag being set for the wrong module.
    static std::once_flag set_options_once;
    std::call_once(set_options_once, []() {
        cl::ParseEnvironmentOptions("halide-hvx-be", "HALIDE_LLVM_ARGS",
                                    "Halide HVX internal compiler\n");

        std::vector<const char *> options = {
            "halide-hvx-be",
            // Don't put small objects into .data sections, it causes
            // issues with position independent code.
            "-hexagon-small-data-threshold=0"
        };
        cl::ParseCommandLineOptions(options.size(), options.data());
    });

    if (module.target().features_all_of({Halide::Target::HVX_128, Halide::Target::HVX_64})) {
        user_error << "Both HVX_64 and HVX_128 set at same time\n";
    }

    return llvm_module;
}

namespace {

// A piece of IR uses HVX if it contains any vector type producing IR
// nodes.
class UsesHvx : public IRVisitor {
private:
    using IRVisitor::visit;
    void visit(const Variable *op) {
        uses_hvx = uses_hvx || op->type.is_vector();
    }
    void visit(const Ramp *op) {
        uses_hvx = uses_hvx || op->type.is_vector();
    }
    void visit(const Broadcast *op) {
        uses_hvx = uses_hvx || op->lanes > 1;
    }
    void visit(const Call *op) {
        uses_hvx = uses_hvx || op->type.is_vector();
    }

public:
    bool uses_hvx = false;
};

bool uses_hvx(Stmt s) {
    UsesHvx uses;
    s.accept(&uses);
    return uses.uses_hvx;
}

// Check if the IR has a parallel for loop in it.
class HasParFor : public IRVisitor {
private:
    using IRVisitor::visit;
    void visit(const For *op) {
        if (op->for_type == ForType::Parallel) {
            has_par_for = true;
            return;
        }
        IRVisitor::visit(op);
    }
public:
    bool has_par_for = false;
};

// Return true if s has a parallel for loop in it.
bool has_par_for(Stmt s) {
    HasParFor h;
    s.accept(&h);
    return h.has_par_for;
}

Stmt call_halide_qurt_hvx_lock(const Target &target) {
    Expr hvx_mode = target.has_feature(Target::HVX_128) ? 128 : 64;
    Expr hvx_lock = Call::make(Int(32), "halide_qurt_hvx_lock", {hvx_mode}, Call::Extern);
    string hvx_lock_result_name = unique_name("hvx_lock_result");
    Expr hvx_lock_result_var = Variable::make(Int(32), hvx_lock_result_name);
    Stmt check_hvx_lock = LetStmt::make(hvx_lock_result_name, hvx_lock,
                                        AssertStmt::make(EQ::make(hvx_lock_result_var, 0), hvx_lock_result_var));
    return check_hvx_lock;
}

Stmt call_halide_qurt_hvx_unlock(const Target &target) {
    Expr hvx_unlock = Call::make(Int(32), "halide_qurt_hvx_unlock", {}, Call::Extern);
    string hvx_unlock_result_name = unique_name("hvx_unlock_result");
    Expr hvx_unlock_result_var = Variable::make(Int(32), hvx_unlock_result_name);
    Stmt check_hvx_unlock = LetStmt::make(hvx_unlock_result_name, hvx_unlock,
                                        AssertStmt::make(EQ::make(hvx_unlock_result_var, 0), hvx_unlock_result_var));
    return check_hvx_unlock;
}
// Wrap the stmt in a call to qurt_hvx_lock, calling qurt_hvx_unlock
// as a destructor if successful.
Stmt acquire_hvx_context(Stmt stmt, const Target &target) {
    // Modify the stmt to add a call to halide_qurt_hvx_lock, and
    // register a destructor to call halide_qurt_hvx_unlock.
    Stmt check_hvx_lock = call_halide_qurt_hvx_lock(target);
    Expr dummy_obj = reinterpret(Handle(), cast<uint64_t>(1));
    Expr hvx_unlock = Call::make(Int(32), Call::register_destructor,
                                 {Expr("halide_qurt_hvx_unlock_as_destructor"), dummy_obj}, Call::Intrinsic);

    stmt = Block::make(Evaluate::make(hvx_unlock), stmt);
    stmt = Block::make(check_hvx_lock, stmt);
    return stmt;
}
bool is_dense_ramp(Expr x) {
    const Ramp *r = x.as<Ramp>();
    if (!r) return false;

    return is_one(r->stride);
}

// In Hexagon, we assume that we can read one vector past the end of
// buffers. Using this assumption, this mutator replaces vector
// predicated dense loads with scalar predicated dense loads.
class SloppyUnpredicateLoads : public IRMutator2 {
    Expr visit(const Load *op) override {
        // Don't handle loads with without predicates, scalar predicates, or non-dense ramps.<commit_msg>Remove some functions and classes no longer needed in CodeGen_Hexagon.cpp<commit_after>#include <iostream>
#include <sstream>
#include <mutex>

#include "LLVM_Headers.h"
#include "CodeGen_Hexagon.h"
#include "CodeGen_Internal.h"
#include "IROperator.h"
#include "IRMatch.h"
#include "IREquality.h"
#include "IRMutator.h"
#include "Target.h"
#include "Debug.h"
#include "Util.h"
#include "Simplify.h"
#include "IRPrinter.h"
#include "EliminateBoolVectors.h"
#include "HexagonOptimize.h"
#include "AlignLoads.h"
#include "CSE.h"
#include "LoopCarry.h"

namespace Halide {
namespace Internal {

using std::vector;
using std::string;

using namespace llvm;

// LLVM Hexagon HVX intrinsics are broken up into 64B and 128B versions, for example,
// llvm::Intrinsic::hexagon_V6_vaddh and llvm::Intrinsic::hexagon_V6_vaddh_128B. This
// macro selects the 64B or 128B mode depending on the value of is_128B. There's a
// further dirty hack here: these intrinsics aren't defined in LLVM older than 3.9. To
// avoid needing to #ifdef random patches of code, we just replace all LLVM intrinsics
// with not_intrinsic.
#ifdef WITH_HEXAGON

#define IPICK(is_128B, i64) (is_128B ? i64##_128B : i64)
#else
#define IPICK(is_128B, i64) (is_128B ? Intrinsic::not_intrinsic : Intrinsic::not_intrinsic)
#endif

CodeGen_Hexagon::CodeGen_Hexagon(Target t) : CodeGen_Posix(t) {
#if !(WITH_HEXAGON)
    user_error << "hexagon not enabled for this build of Halide.\n";
#endif
#if LLVM_VERSION < 50
    user_assert(!t.has_feature(Target::HVX_v62))
        << "llvm 5.0 or later is required for Hexagon v62.\n";
    user_assert(!t.has_feature(Target::HVX_v65))
        << "llvm 5.0 or later is required for Hexagon v65.\n";
    user_assert(!t.has_feature(Target::HVX_v66))
        << "llvm 5.0 or later is required for Hexagon v66.\n";
#endif
    user_assert(llvm_Hexagon_enabled) << "llvm build not configured with Hexagon target enabled.\n";
}

std::unique_ptr<llvm::Module> CodeGen_Hexagon::compile(const Module &module) {
    auto llvm_module = CodeGen_Posix::compile(module);

    // TODO: This should be set on the module itself, or some other
    // safer way to pass this through to the target specific lowering
    // passes. We set the option here (after the base class'
    // implementation of compile) because it is the last
    // Hexagon-specific code to run prior to invoking the target
    // specific lowering in LLVM, minimizing the chances of the wrong
    // flag being set for the wrong module.
    static std::once_flag set_options_once;
    std::call_once(set_options_once, []() {
        cl::ParseEnvironmentOptions("halide-hvx-be", "HALIDE_LLVM_ARGS",
                                    "Halide HVX internal compiler\n");

        std::vector<const char *> options = {
            "halide-hvx-be",
            // Don't put small objects into .data sections, it causes
            // issues with position independent code.
            "-hexagon-small-data-threshold=0"
        };
        cl::ParseCommandLineOptions(options.size(), options.data());
    });

    if (module.target().features_all_of({Halide::Target::HVX_128, Halide::Target::HVX_64})) {
        user_error << "Both HVX_64 and HVX_128 set at same time\n";
    }

    return llvm_module;
}

namespace {

Stmt call_halide_qurt_hvx_lock(const Target &target) {
    Expr hvx_mode = target.has_feature(Target::HVX_128) ? 128 : 64;
    Expr hvx_lock = Call::make(Int(32), "halide_qurt_hvx_lock", {hvx_mode}, Call::Extern);
    string hvx_lock_result_name = unique_name("hvx_lock_result");
    Expr hvx_lock_result_var = Variable::make(Int(32), hvx_lock_result_name);
    Stmt check_hvx_lock = LetStmt::make(hvx_lock_result_name, hvx_lock,
                                        AssertStmt::make(EQ::make(hvx_lock_result_var, 0), hvx_lock_result_var));
    return check_hvx_lock;
}

// Wrap the stmt in a call to qurt_hvx_lock, calling qurt_hvx_unlock
// as a destructor if successful.
Stmt acquire_hvx_context(Stmt stmt, const Target &target) {
    // Modify the stmt to add a call to halide_qurt_hvx_lock, and
    // register a destructor to call halide_qurt_hvx_unlock.
    Stmt check_hvx_lock = call_halide_qurt_hvx_lock(target);
    Expr dummy_obj = reinterpret(Handle(), cast<uint64_t>(1));
    Expr hvx_unlock = Call::make(Int(32), Call::register_destructor,
                                 {Expr("halide_qurt_hvx_unlock_as_destructor"), dummy_obj}, Call::Intrinsic);

    stmt = Block::make(Evaluate::make(hvx_unlock), stmt);
    stmt = Block::make(check_hvx_lock, stmt);
    return stmt;
}
bool is_dense_ramp(Expr x) {
    const Ramp *r = x.as<Ramp>();
    if (!r) return false;

    return is_one(r->stride);
}

// In Hexagon, we assume that we can read one vector past the end of
// buffers. Using this assumption, this mutator replaces vector
// predicated dense loads with scalar predicated dense loads.
class SloppyUnpredicateLoads : public IRMutator2 {
    Expr visit(const Load *op) override {
        // Don't handle loads with without predicates, scalar predicates, or non-dense ramps.
{'fraction_non_alphanumeric': 0.11062777209143637, 'fraction_numerical': 0.016376663254861822, 'mean_word_length': 3.6398891966759, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 7, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '886601', 'n_tokens_mistral': 4211, 'n_tokens_neox': 3903, 'n_words': 1070}