Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
6bd454d
gemma4e: ship the FLMGEMM artifacts and the instruction-stream loader
andrej Sep 12, 2026
65ad1dc
plugins: let a shared library override individual model operations
andrej Sep 14, 2026
bdd05f5
AutoModel: load operator plugins for every model, not just gemma4e
andrej Sep 14, 2026
b6caa17
plugins: drop the lm_head role, which no engine declares
andrej Sep 14, 2026
bddb4d0
flm_gemm: dequantize on the device, and make the weight source a mode
andrej Sep 15, 2026
31ba1ac
plugins: a README that leads with what the example plugin does
andrej Sep 15, 2026
c75fdb8
plugins: draw the chart from bench3.sh output instead of literals
andrej Sep 16, 2026
f0d2df2
flm_gemm: rebuild the dequant streams against the simplified sequence
andrej Sep 16, 2026
246654e
flm_gemm: pick up the current GEMM, and refuse an incomplete artifact…
andrej Sep 16, 2026
746a8b8
plugins: one README, not two
andrej Sep 17, 2026
6a1b0a6
plugins: one copy of each tool, and a self-contained reproduction
andrej Sep 17, 2026
eff1f10
plugins: name the configurations rather than rank them
andrej Sep 17, 2026
30a9183
add accuracy remark
andrej Sep 17, 2026
3a15582
plugins: rename the example to iron_gemm
andrej Sep 17, 2026
0f95075
plugins: drop the registry API nothing calls, and one duplicated index
andrej Sep 17, 2026
b2c62ca
plugins: let each model name its own operations
andrej Sep 17, 2026
2d2eb2a
iron_gemm: serve Gemma4 E4B
andrej Sep 17, 2026
6d6dfa3
gemma4e: rebuild the engine against the rebased operator declarations
andrej Sep 18, 2026
f8c2e49
plugins: document the mechanism, and a plugin that is only a plugin
andrej Sep 18, 2026
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: 10 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -898,3 +898,13 @@ install(FILES model_info.json DESTINATION "${FLM_SHARE_DESTINATION}")
# xclbins, which are loaded by shared libraries need to be in location
# relative to the executable, so we install them relative to the binary.
install(DIRECTORY xclbins DESTINATION "${FLM_SHARE_DESTINATION}")

# ———————————————————————————————————————————————
# Operator plugins
# ———————————————————————————————————————————————
# Reference overrides, built on the public API only. Off by default because they
# need artifacts and packed weights that are produced out of tree.
option(FLM_BUILD_PLUGINS "Build the reference operator plugins" OFF)
if(FLM_BUILD_PLUGINS)
add_subdirectory(plugins/iron_gemm)
endif()
23 changes: 23 additions & 0 deletions src/common/AutoModel/automodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,29 @@
/// \note This is a source file for the auto_model class

#include "AutoModel/automodel.hpp"
#include "flm_plugin.hpp"


void AutoModel::_load_operator_plugins() {
flm::op_registry* ops = this->lm_engine ? this->lm_engine->ops() : nullptr;
if (ops == nullptr) return;
const std::string xclbin_path = utils::path_join(
this->lm_config->exec_path, "xclbins", this->lm_config->model_name);
flm::plugin_context ctx{
ops,
this->npu.get(),
this->model_path.c_str(),
xclbin_path.c_str(),
};
flm::load_plugins_from_env(ctx);
}


void AutoModel::_load_engine_weights() {
this->_load_operator_plugins();
this->lm_engine->load_weights(*this->q4nx);
this->q4nx.reset();
}


AutoModel::AutoModel(flm_rt::device* npu_device_inst, std::string current_model) {
Expand Down
6 changes: 1 addition & 5 deletions src/common/AutoModel/modeling_gemma3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@ void Gemma3::load_model(std::string model_path, json model_info, int default_con
this->q4nx = std::make_unique<Q4NX>(this->model_path);
// model_type == gemma
this->lm_engine = std::make_unique<gemma_npu>(*this->lm_config, this->npu.get(), this->MAX_L);

this->lm_engine->load_weights(*this->q4nx);

//free the q4nx
this->q4nx.reset();
this->_load_engine_weights();
this->lm_engine->clear_context();
this->setup_tokenizer(model_path);
this->sampler.reset();
Expand Down
6 changes: 1 addition & 5 deletions src/common/AutoModel/modeling_gemma3_text.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@ void Gemma3_Text_Only::load_model(std::string model_path, json model_info, int d
this->q4nx = std::make_unique<Q4NX>(this->model_path);
// model_type == gemma_text_only
this->lm_engine = std::make_unique<gemma_text_npu>(*this->lm_config, this->npu.get(), this->MAX_L);

this->lm_engine->load_weights(*this->q4nx);

//free the q4nx
this->q4nx.reset();
this->_load_engine_weights();
this->lm_engine->clear_context();
this->setup_tokenizer(model_path);
this->sampler.reset();
Expand Down
5 changes: 1 addition & 4 deletions src/common/AutoModel/modeling_gemma4_12b.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -409,10 +409,7 @@ void Gemma4_12B::load_model(std::string model_path, json model_info, int default

this->q4nx = std::make_unique<Q4NX>(this->model_path);
this->lm_engine = std::make_unique<gemma4_12b_npu>(*this->lm_config, this->npu.get(), this->MAX_L);

this->lm_engine->load_weights(*this->q4nx);
// free the q4nx
this->q4nx.reset();
this->_load_engine_weights();
// The soft token budget is a preprocessing constant (processor_config.json
// image_seq_length / image_processor.max_soft_tokens), read by the engine
// along with the rest of the image front end parameters.
Expand Down
5 changes: 1 addition & 4 deletions src/common/AutoModel/modeling_gemma4e.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -654,10 +654,7 @@ void Gemma4e::load_model(std::string model_path, json model_info, int default_co

this->q4nx = std::make_unique<Q4NX>(this->model_path);
this->create_engine();

this->lm_engine->load_weights(*this->q4nx);
//free the q4nx
this->q4nx.reset();
this->_load_engine_weights();
this->lm_engine->clear_context();
this->setup_tokenizer(model_path);
this->sampler.reset();
Expand Down
3 changes: 1 addition & 2 deletions src/common/AutoModel/modeling_gpt_oss.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ void GPT_OSS::load_model(std::string model_path, json model_info, int default_co
this->_shared_load_model(model_path, model_info, default_context_length, enable_preemption);
this->q4nx = std::make_unique<Q4NX>(this->model_path);
this->lm_engine = std::make_unique<gpt_oss_npu>(*this->lm_config, this->npu.get(), this->MAX_L);
this->lm_engine->load_weights(*this->q4nx);
this->q4nx.reset();
this->_load_engine_weights();
this->tokenizer = std::make_unique<Tokenizer>(model_path);

this->setup_tokenizer(model_path);
Expand Down
6 changes: 1 addition & 5 deletions src/common/AutoModel/modeling_hunyuan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,7 @@ void Hunyuan::load_model(std::string model_path, json model_info, int default_co

this->q4nx = std::make_unique<Q4NX>(this->model_path);
this->lm_engine = std::make_unique<hunyuan_npu>(*this->lm_config, this->npu.get(), this->MAX_L);

this->lm_engine->load_weights(*this->q4nx);

// free the mmap'd weights immediately
this->q4nx.reset();
this->_load_engine_weights();

this->lm_engine->clear_context();
this->setup_tokenizer(model_path);
Expand Down
12 changes: 2 additions & 10 deletions src/common/AutoModel/modeling_lfm2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@ void LFM2::load_model(std::string model_path, json model_info, int default_conte
this->q4nx = std::make_unique<Q4NX>(this->model_path);
// model_type == llama
this->lm_engine = std::make_unique<lfm2_npu>(*this->lm_config, this->npu.get(), this->MAX_L);

this->lm_engine->load_weights(*this->q4nx);

//free the q4nx
this->q4nx.reset();
this->_load_engine_weights();
this->lm_engine->clear_context();
this->setup_tokenizer(model_path);
this->sampler.reset();
Expand Down Expand Up @@ -273,11 +269,7 @@ void LFM2_5_TK::load_model(std::string model_path, json model_info, int default_
this->q4nx = std::make_unique<Q4NX>(this->model_path);
// model_type == llama
this->lm_engine = std::make_unique<lfm2_npu>(*this->lm_config, this->npu.get(), this->MAX_L);

this->lm_engine->load_weights(*this->q4nx);

//free the q4nx
this->q4nx.reset();
this->_load_engine_weights();
this->lm_engine->clear_context();
this->setup_tokenizer(model_path);
this->sampler.reset();
Expand Down
12 changes: 2 additions & 10 deletions src/common/AutoModel/modeling_llama3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@ void Llama3::load_model(std::string model_path, json model_info, int default_con
this->q4nx = std::make_unique<Q4NX>(this->model_path);
// model_type == llama
this->lm_engine = std::make_unique<llama_npu>(*this->lm_config, this->npu.get(), this->MAX_L);

this->lm_engine->load_weights(*this->q4nx);

//free the q4nx
this->q4nx.reset();
this->_load_engine_weights();

this->lm_engine->clear_context();
this->setup_tokenizer(model_path);
Expand Down Expand Up @@ -98,11 +94,7 @@ void DeepSeek_r1_8b::load_model(std::string model_path, json model_info, int def
this->q4nx = std::make_unique<Q4NX>(this->model_path);
// model_type == llama
this->lm_engine = std::make_unique<llama_npu>(*this->lm_config, this->npu.get(), this->MAX_L);

this->lm_engine->load_weights(*this->q4nx);

//free the q4nx
this->q4nx.reset();
this->_load_engine_weights();
this->lm_engine->clear_context();
this->setup_tokenizer(model_path);
this->sampler.reset();
Expand Down
6 changes: 1 addition & 5 deletions src/common/AutoModel/modeling_nanbeige.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@ void Nanbeige::load_model(std::string model_path, json model_info, int default_c
this->q4nx = std::make_unique<Q4NX>(this->model_path);
// model_type == nanbeige
this->lm_engine = std::make_unique<nanbeige_npu>(*this->lm_config, this->npu.get(), this->MAX_L);

this->lm_engine->load_weights(*this->q4nx);

//free the q4nx
this->q4nx.reset();
this->_load_engine_weights();

this->lm_engine->clear_context();
this->setup_tokenizer(model_path);
Expand Down
5 changes: 1 addition & 4 deletions src/common/AutoModel/modeling_phi4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@ void Phi4::load_model(std::string model_path, json model_info, int default_conte
this->q4nx = std::make_unique<Q4NX>(this->model_path);
// model_type == phi4
this->lm_engine = std::make_unique<phi4_npu>(*this->lm_config, this->npu.get(), this->MAX_L);
this->lm_engine->load_weights(*this->q4nx);

//free the q4nx
this->q4nx.reset();
this->_load_engine_weights();

this->lm_engine->clear_context();
this->setup_tokenizer(model_path);
Expand Down
6 changes: 1 addition & 5 deletions src/common/AutoModel/modeling_qwen2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@ void Qwen2::load_model(std::string model_path, json model_info, int default_cont

// lm_config->get<std::string>("model_type", "") == qwen2
this->lm_engine = std::make_unique<qwen2_npu>(*this->lm_config, this->npu.get(), this->MAX_L);

this->lm_engine->load_weights(*this->q4nx);

//free the q4nx
this->q4nx.reset();
this->_load_engine_weights();
this->lm_engine->clear_context();
this->setup_tokenizer(model_path);
this->sampler.reset();
Expand Down
5 changes: 1 addition & 4 deletions src/common/AutoModel/modeling_qwen2vl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ void Qwen2VL::load_model(std::string model_path, json model_info, int default_co
this->q4nx = std::make_unique<Q4NX>(this->model_path);
// lm_config->get<std::string>("model_type", "") == qwen2
this->lm_engine = std::make_unique<qwen2vl_npu>(*this->lm_config, this->npu.get(), this->MAX_L);

this->lm_engine->load_weights(*this->q4nx);
//free the q4nx
this->q4nx.reset();
this->_load_engine_weights();
this->lm_engine->clear_context();
this->setup_tokenizer(model_path);
this->sampler.reset();
Expand Down
24 changes: 4 additions & 20 deletions src/common/AutoModel/modeling_qwen3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@ void Qwen3::load_model(std::string model_path, json model_info, int default_cont
this->q4nx = std::make_unique<Q4NX>(this->model_path);
// lm_config->get<std::string>("model_type", "") == qwen3
this->lm_engine = std::make_unique<qwen3_npu>(*this->lm_config, this->npu.get(), this->MAX_L);

this->lm_engine->load_weights(*this->q4nx);

//free the q4nx
this->q4nx.reset();
this->_load_engine_weights();
this->lm_engine->clear_context();
this->setup_tokenizer(model_path);
this->sampler.reset();
Expand Down Expand Up @@ -316,11 +312,7 @@ void Qwen3_IT::load_model(std::string model_path, json model_info, int default_c

// lm_config->get<std::string>("model_type", "") == qwen3
this->lm_engine = std::make_unique<qwen3_npu>(*this->lm_config, this->npu.get(), this->MAX_L);

this->lm_engine->load_weights(*this->q4nx);

//free the q4nx
this->q4nx.reset();
this->_load_engine_weights();
this->lm_engine->clear_context();
this->setup_tokenizer(model_path);
this->sampler.reset();
Expand Down Expand Up @@ -466,11 +458,7 @@ void Qwen3_TK::load_model(std::string model_path, json model_info, int default_c

// lm_config->get<std::string>("model_type", "") == qwen3
this->lm_engine = std::make_unique<qwen3_npu>(*this->lm_config, this->npu.get(), this->MAX_L);

this->lm_engine->load_weights(*this->q4nx);

//free the q4nx
this->q4nx.reset();
this->_load_engine_weights();
this->lm_engine->clear_context();
this->setup_tokenizer(model_path);
this->sampler.reset();
Expand Down Expand Up @@ -687,11 +675,7 @@ void DeepSeek_r1_0528_8b::load_model(std::string model_path, json model_info, in
this->q4nx = std::make_unique<Q4NX>(this->model_path);
// model_type == llama
this->lm_engine = std::make_unique<qwen3_npu>(*this->lm_config, this->npu.get(), this->MAX_L);

this->lm_engine->load_weights(*this->q4nx);

//free the q4nx
this->q4nx.reset();
this->_load_engine_weights();
this->lm_engine->clear_context();
this->setup_tokenizer(model_path);
this->sampler.reset();
Expand Down
5 changes: 1 addition & 4 deletions src/common/AutoModel/modeling_qwen3_5vl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ void Qwen3_5VL::load_model(std::string model_path, json model_info, int default_
this->q4nx = std::make_unique<Q4NX>(this->model_path);
// lm_config->get<std::string>("model_type", "") == qwen3
this->lm_engine = std::make_unique<qwen3_5vl_npu>(*this->lm_config, this->npu.get(), this->MAX_L);

this->lm_engine->load_weights(*this->q4nx);
//free the q4nx
this->q4nx.reset();
this->_load_engine_weights();
this->lm_engine->clear_context();
this->setup_tokenizer(model_path);
this->sampler.reset();
Expand Down
5 changes: 1 addition & 4 deletions src/common/AutoModel/modeling_qwen3_6_moe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ void Qwen3_6_MOE::load_model(std::string model_path, json model_info, int defaul
this->q4nx = std::make_unique<Q4NX>(this->model_path);
// lm_config->get<std::string>("model_type", "") == qwen3
this->lm_engine = std::make_unique<qwen3_6_moe_npu>(*this->lm_config, this->npu.get(), this->MAX_L);

this->lm_engine->load_weights(*this->q4nx);
//free the q4nx
this->q4nx.reset();
this->_load_engine_weights();
this->lm_engine->clear_context();
this->setup_tokenizer(model_path);
this->sampler.reset();
Expand Down
5 changes: 1 addition & 4 deletions src/common/AutoModel/modeling_qwen3vl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@ void Qwen3VL::load_model(std::string model_path, json model_info, int default_co
this->q4nx = std::make_unique<Q4NX>(this->model_path);
// lm_config->get<std::string>("model_type", "") == qwen3
this->create_engine();

this->lm_engine->load_weights(*this->q4nx);
//free the q4nx
this->q4nx.reset();
this->_load_engine_weights();
this->lm_engine->clear_context();
this->setup_tokenizer(model_path);
this->sampler.reset();
Expand Down
11 changes: 11 additions & 0 deletions src/include/AutoModel/automodel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,17 @@ class AutoModel {


void _shared_load_model(std::string model_path, json model_info, int default_context_length = -1, bool enable_preemption = false);

/// \brief Give the plugins named in FLM_PLUGIN a chance to override operations
/// \note Call once the engine exists and before its weights are loaded: the
/// engine declares its operations in its constructor, and an override
/// must be in place before anything dispatches through it.
void _load_operator_plugins();

/// \brief Bring the engine up: load plugins, read the weights, release the file
/// \note Every model calls this immediately after constructing its engine.
/// Keeping it in one place is what makes a plugin apply to all of them.
void _load_engine_weights();
nlohmann::json _shared_setup_tokenizer(std::string model_path);

/// \brief Insert tokens into the model
Expand Down
9 changes: 9 additions & 0 deletions src/include/causal_lm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,21 @@
#include "utils/utils.hpp"
#include "buffer.hpp"

class npu_app;
namespace flm {
template <typename App> class op_registry_t;
}

/// \brief causal_lm class
class causal_lm {
public:
causal_lm(){}
virtual ~causal_lm(){}

/// \brief The operations of this engine that a plugin may override
/// \return nullptr if the engine declares none
virtual flm::op_registry_t<npu_app>* ops() { return nullptr; }

/// \brief forward the causal_lm
/// \param ids the ids
/// \return the output
Expand Down
Loading
Loading