Halideでスタティックライブラリを作る・使う

Halideは画像処理記述用のDSLです。
どういったものかは[公式ページ](https://halide-lang.org/)をご覧いただくか、福嶋先生がある程度の[日本語資料](https://qiita.com/fukushima1981/items/fa3537234e19baffc761)を投稿されていますので、そちらを参照ください。

HalideではJITコンパイルによる動作だけではなく、記述したFuncを静的ライブラリとしてAOTコンパイルし、既存のプログラムに組み込むことも可能です。

# 基本的な作り方と使い方

```gen_staticlib.cpp
#include <Halide.h>

void Gen_StaticLib()
{
    using namespace Halide;

    ImageParam input(type_of<uint8_t>(), 3);  // x,y,ch 3次元
    Param<float> mul;

    Func brighter;
    Var x, y, ch;
    brighter(x, y, ch) = cast<uint8_t>(min(input(x, y, ch) * mul, 255));
    brighter.parallel(y);

    brighter.compile_to_static_library(
        "brighter",     // .lib のファイル名、brighter.lib になる
        { input, mul },      // 入力パラメータ、関数引数順になる
        "brighter"      // 関数名
    );    
}
```

実行するとbrighter.hとbrighter.libができているので、既存のプログラムにリンクして使うだけです。
入力、出力はHalideBuffer.h中のHalide::Runtime::Bufferを使えばOK。
Halide.dllは必要ありません。

```use_staticlib.cpp
#include <HalideBuffer.h>
#include "brighter.h"

void Use_StaticLib()
{
    using namespace Halide;

    Runtime::Buffer<uint8_t> src(640, 480, 3);
    Runtime::Buffer<uint8_t> dst(640, 480, 3);
    
    /* 画像読み込みコードは省略 */

    brighter(src, 1.5f, dst);    // 出力は生成時のパラメータの最後につく
}
```
# クロスコンパイル
compile_to_static_libraryには4番目の引数があり、OSやCPUアーキテクチャ、SIMDの有無などを設定できます。
指定しない場合は、AOTコンパイル時の環境が自動的に使用されます。

```gen_staticlib.cpp
#include <Halide.h>

void Gen_StaticLib()
{
    using namespace Halide;

    ImageParam input(type_of<uint8_t>(), 3);

    Func brighter;
    Var x, y, ch;
    brighter(x, y, ch) = cast<uint8_t>(min(input(x, y, ch) * 1.5f, 255));
    brighter.parallel(y);

    // 動作環境の指定
    Target env;
    env.os = Target::OS::Windows;
    env.arch = Target::Arch::X86;
    env.bits = 64;
    env.set_feature(Target::Feature::AVX2);    // vectorizeしていないからあまり意味がないが…

    brighter.compile_to_static_library(
        "brighter", 
        { input }, 
        "brighter", 
        env          //ここに指定
    );    
}
```

# 生成した複数のスタティックライブラリをリンクするとき
1つの.libファイルに複数のFuncを含めることはできないようです。
処理ごとに.libも複数生成する必要があります。

```gen_static_lib_multi1.cpp
#include <Halide.h>

void Gen_StaticLib()
{
    using namespace Halide;

    ImageParam input(type_of<uint8_t>(), 2);
    Param<float> val;

    Func mul, add;
    Var x, y;

    mul(x, y) = cast<uint8_t>(min(input(x, y) * val, 255));
    add(x, y) = cast<uint8_t>(min(input(x, y) + val, 255));    
    
    mul.compile_to_static_library(
        "mul",        // mul.lib
        { input, val },
        "mul"
    );

    add.compile_to_static_library(
        "add",        // add.lib
        { input, val },
        "add"
    );
}
```

が、これらの.libを同時に使おうとするとリンクエラーになってしまいます。
どうやら指定なしだと.libにはHalideのランタイム関数も同時に含まれるようで、ランタイムの重複定義でコケるっぽいです。

リンクする.libのうちどれか一つだけにランタイムを持たせるように、Targetを設定してやると解決。

```gen_static_lib_multi1.cpp
#include <Halide.h>

void Gen_StaticLib()
{
    using namespace Halide;

   /* 略 */

    Target env, env_no_runtime;

    env.os = Target::OS::Windows;
    env.arch = Target::Arch::X86;
    env.bits = 64;
    env.set_feature(Target::Feature::AVX2);

    env_no_runtime  = env.with_feature(Target::Feature::NoRuntime);   // ランタイム生成を抑制
    
    mul.compile_to_static_library(
        "mul",        // mul.lib
        { input, val },
        "mul",
        env           // こちらにHalideランタイムが含まれる
    );

    add.compile_to_static_library(
        "add",        // add.lib
        { input, val },
        "add",
        env_no_runtime  //こちらはランタイムなし
    );
}
```
あるいは、ファイルが1つ増えますが、実際には呼びださない適当なFuncを作ってランタイムだけの.libを作るほうが管理が楽かもしれません。

```gen_static_lib_multi2.cpp
#include <Halide.h>

using namespace Halide;

void Gen_Halide_Runtime(Target& env)
{
    // 実際には呼ばない関数
    Func f;
    Var x;

    f(x) = x;

    f.compile_to_static_library(
        "Halide_Runtime",
        {},
        "DoNotCall",
        env.without_feature(Target::Feature::NoRuntime)    // ここだけNoRuntimeを外す -> ランタイムあり
    );
}

void Gen_Mul(Target& env)
{
    ImageParam input(type_of<uint8_t>(), 2);
    Param<float> val;

    Func mul;
    Var x, y;

    mul(x, y) = cast<uint8_t>(min(input(x, y) * val, 255));
    
    mul.compile_to_static_library(
        "mul",        // mul.lib
        { input, val },
        "mul",
        env           
    );
}

void Gen_Add(Target& env)
{
    ImageParam input(type_of<uint8_t>(), 2);
    Param<float> val;

    Func add;
    Var x, y;

    add(x, y) = cast<uint8_t>(min(input(x, y) + val, 255));
    
    add.compile_to_static_library(
        "add",        // add.lib
        { input, val },
        "add",
        env           
    );
}

int main()
{
    Target env;
    env.os = Target::OS::Windows;
    env.arch = Target::Arch::X86;
    env.bits = 64;
    env.set_features(
     { Target::Feature::AVX2,
       Target::Feature::NoRuntime }  // 基本はNoRuntimeにしておく
    );

   Gen_Halide_Runtime(env);   // ランタイムあり
   Gen_Mul(env);              // ランタイムなし
   Gen_Add(env);              // ランタイムなし
}
```