From 19553d7dff0f69a4b6eb5a45dd6e933174fe9990 Mon Sep 17 00:00:00 2001 From: Matthias Kurtenacker Date: Thu, 13 Feb 2025 17:29:12 +0100 Subject: [PATCH] [CGRA] register vector width for defs and reuse them on loads. --- src/thorin/CMakeLists.txt | 2 + src/thorin/analyses/cgra_vector_width.cpp | 71 +++++++++++++ src/thorin/analyses/cgra_vector_width.h | 29 +++++ src/thorin/be/c/c.cpp | 124 +++++++--------------- 4 files changed, 142 insertions(+), 84 deletions(-) create mode 100644 src/thorin/analyses/cgra_vector_width.cpp create mode 100644 src/thorin/analyses/cgra_vector_width.h diff --git a/src/thorin/CMakeLists.txt b/src/thorin/CMakeLists.txt index b3335ff5b..e1c883f31 100644 --- a/src/thorin/CMakeLists.txt +++ b/src/thorin/CMakeLists.txt @@ -16,6 +16,8 @@ set(THORIN_SOURCES world.h analyses/cfg.cpp analyses/cfg.h + analyses/cgra_vector_width.cpp + analyses/cgra_vector_width.h analyses/domfrontier.cpp analyses/domfrontier.h analyses/domtree.cpp diff --git a/src/thorin/analyses/cgra_vector_width.cpp b/src/thorin/analyses/cgra_vector_width.cpp new file mode 100644 index 000000000..22ddb351d --- /dev/null +++ b/src/thorin/analyses/cgra_vector_width.cpp @@ -0,0 +1,71 @@ +#include "thorin/analyses/cgra_vector_width.h" + +#include + +namespace thorin { + +template +size_t CGRAVectorWidthAnalysis::get_width_for (const Continuation* cont) { + using DeviceApiSet = std::unordered_set; + DeviceApiSet irregular_apis = { "aie::vector::extract", "aie::zeros", "aie::store_v", "readincr_v_channel", "window_readincr_v_channel", "aie::load_v", "aie::sliding_"/*all sliding APIs*/, "srs"}; + + auto new_vector_size = base_size; + + for (auto use: cont->uses()) { + if (auto app = use->isa(); app && app->callee()->isa_nom()) { + auto callee = app->callee()->as_nom(); + if (callee->cc() == CC::Device) { + auto name = callee->name(); + if (name.find("aie::sliding_") != std::string::npos) + name = "aie::sliding_"; + if (irregular_apis.count(name)) { + if (app->num_args() > 1) { + // The first arg of all irregular APIs is the lane size + if (auto primtype = app->arg(1)->type()->isa()) { + if (primtype->primtype_tag() == PrimType_pu32) { + new_vector_size = app->arg(1)->as()->value().get_u32(); + } else { + world().WLOG("Lane size in {} must be an unsigned integer value to be effective", name); + } + } + } + } + } + } + } + + return new_vector_size; +} + +template +size_t CGRAVectorWidthAnalysis::get_width_for (const Def* def) { + if (auto width = def_2_size.lookup(def)) + return *width; + + size_t vector_width = base_size; + + for (auto use : def->uses()) { + if (auto cont = use->isa()) { + auto width = get_width_for(cont); + if (allow_scaling) { + if (width > vector_width) + vector_width = width; + } else { + assert(vector_width == base_size || vector_width == width); + vector_width = width; + } + } + } + + return vector_width; +} + +template +void CGRAVectorWidthAnalysis::register_width (const Def* def, size_t width) { + def_2_size.emplace(def, width); +} + +template class CGRAVectorWidthAnalysis; +//template class CGRAVectorWidthAnalysis; + +} diff --git a/src/thorin/analyses/cgra_vector_width.h b/src/thorin/analyses/cgra_vector_width.h new file mode 100644 index 000000000..b6f8ced47 --- /dev/null +++ b/src/thorin/analyses/cgra_vector_width.h @@ -0,0 +1,29 @@ +#ifndef THORIN_ANALYSES_CGRA_VECTOR_WIDTH_H +#define THORIN_ANALYSES_CGRA_VECTOR_WIDTH_H + +#include "thorin/world.h" +#include "thorin/continuation.h" + +namespace thorin { + +template +class CGRAVectorWidthAnalysis { +private: + World& world_; + size_t base_size; + DefMap def_2_size; + +public: + CGRAVectorWidthAnalysis(World& world, size_t base_size) : world_(world), base_size(base_size) {} + + World& world() const { return world_; }; + + size_t get_width_for(const Continuation* cont); + + size_t get_width_for(const Def* def); + void register_width(const Def* def, size_t width); +}; + +} + +#endif diff --git a/src/thorin/be/c/c.cpp b/src/thorin/be/c/c.cpp index ddc45c2f8..4d7d58849 100644 --- a/src/thorin/be/c/c.cpp +++ b/src/thorin/be/c/c.cpp @@ -5,6 +5,7 @@ #include "thorin/analyses/cfg.h" #include "thorin/analyses/schedule.h" #include "thorin/analyses/scope.h" +#include "thorin/analyses/cgra_vector_width.h" #include "thorin/transform/hls_dataflow.h" #include "thorin/be/emitter.h" #include "thorin/util/stream.h" @@ -161,7 +162,11 @@ class CCodeGen : public thorin::Emitter struct { bool hls = false; bool cgra_graph = false; } top_scope; //TODO: debug var should enable top debug point and add names to plio ports struct { bool sim_data = false; bool debug = false; int32_t iteration = -1;} options; + size_t vector_size_; + + std::unique_ptr> cgraVectorWidthAnalysis; + ContinuationMap builtin_funcs_; // OpenCL builtin functions }; @@ -1426,8 +1431,10 @@ auto CCodeGen::is_mask_type(const Type* type) { std::string CCodeGen::prepare(const Scope& scope) { auto cont = scope.entry(); - if (lang_ == Lang::CGRA && !cont->is_cgra_graph() && cont->is_exported()) + if (lang_ == Lang::CGRA && !cont->is_cgra_graph() && cont->is_exported()) { vector_size_ = get_vector_size(cont); + cgraVectorWidthAnalysis = std::unique_ptr>(new CGRAVectorWidthAnalysis(world_, vector_size_)); + } //TODO: for interface attr. 1) check codegen use, cont (seems not working) //2) use old2new to find old cont. then probably "using conts" then check for interface attr on that cont and then set for all new apps @@ -1579,48 +1586,14 @@ void CCodeGen::prepare(Continuation* cont, const std::string&) { reg_type = "aie::vector"; } - //TODO: This lambda should be implemented as an analysis thorin pass that sets a new attribute for the continuations - // Basicaly this new pass marks those continuations that have free variables belonging to the set of irregular_apis - // later on in the c-backend we can check for this attribute and adjust the vector size of params accordingly - // TODO:: all the the defs that use irregular defs also should be modifed - auto adjust_vector_size = [&] () { - - using DeviceApiSet = std::unordered_set; - auto new_vector_size = vector_size_; - DeviceApiSet irregular_apis = { "aie::vector::extract", "aie::zeros", "aie::store_v", "readincr_v_channel", "window_readincr_v_channel", - "aie::load_v", "aie::sliding_"/*all sliding APIs*/, "srs"}; - - for (auto use: cont->uses()) { - if (auto app = use->isa(); app && app->callee()->isa_nom()) { - auto callee = app->callee()->as_nom(); - if (callee->cc() == CC::Device) { - auto name = callee->name(); - if (name.find("aie::sliding_") != std::string::npos) - name = "aie::sliding_"; - if (irregular_apis.count(name)) { - if (app->num_args() > 1) { - // The first arg of all irregular APIs is the lane size - if (auto primtype = app->arg(1)->type()->isa()) { - if (primtype->primtype_tag() == PrimType_pu32) { - new_vector_size = app->arg(1)->as()->value().get_u32(); - } else { - world().WLOG("Lane size in {} must be an unsigned integer value to be effective", name); - } - } - - - } - } - - } - } - } - return new_vector_size; - }; + if (!is_mask_type(type) && !type->isa()) { + size_t vector_width = cgraVectorWidthAnalysis->get_width_for(cont); + cgraVectorWidthAnalysis->register_width(param, vector_width); + } param_type_str = is_mask_type(type) ? (reg_type + "<" + std::to_string(vector_size_)+ ">") : - (type->isa() ? (reg_type + "<" + convert(type->as()->elem_type()) + ", " + std::to_string(vector_size_) + ">") : - (reg_type + "<" + convert(param->type()) + ", " + std::to_string(adjust_vector_size()) + ">")); + (type->isa() ? (reg_type + "<" + convert(type->as()->elem_type()) + ", " + std::to_string(vector_size_) + " /*def_array*/>") : + (reg_type + "<" + convert(param->type()) + ", " + std::to_string(cgraVectorWidthAnalysis->get_width_for(cont)) + " /*parm*/>")); //TODO: we should also check if the array types are used in a read/write instrinsic } } @@ -2525,52 +2498,17 @@ std::string CCodeGen::emit_def(BB* bb, const Def* def) { auto t = convert(slot->alloced_type()); //TODO: check slot condition, as we need slots for CGRA kernels but not for cgra_graph if (!top_scope.cgra_graph) { - - //TODO: this lambda is duplicated with a minor change, it should be refactored as a normal (member) function - auto adjust_vector_size = [&] () { - - using DeviceApiSet = std::unordered_set; - auto new_vector_size = vector_size_; - DeviceApiSet irregular_apis = { "aie::vector::extract", "aie::zeros", "aie::store_v", "readincr_v_channel", "window_readincr_v_channel", - "aie::load_v", "aie::sliding_"/*all sliding APIs*/, "srs"}; - - for (auto use: bb->cont->uses()) { - if (auto app = use->isa(); app && app->callee()->isa_nom()) { - auto callee = app->callee()->as_nom(); - if (callee->cc() == CC::Device) { - auto name = callee->name(); - if (name.find("aie::sliding_") != std::string::npos) - name = "aie::sliding_"; - if (irregular_apis.count(name)) { - if (app->num_args() > 1) { - // The first arg of all irregular APIs is the lane size - if (auto primtype = app->arg(1)->type()->isa()) { - if (primtype->primtype_tag() == PrimType_pu32) { - new_vector_size = app->arg(1)->as()->value().get_u32(); - } else { - world().WLOG("Lane size in {} must be an unsigned integer value to be effective", name); - } - } - - - } - } - - } - } - } - return new_vector_size; - }; - - auto is_not_array = !(slot->alloced_type()->isa()); auto is_not_mem_op = !(slot->frame()->op(0)->as()->mem()->isa()); if (is_cgra_vector_kernel() && is_not_array && is_not_mem_op) { - func_impls_.fmt("aie::vector<{}, {}> {}_slot;\n", t, adjust_vector_size(), name); - func_impls_.fmt("aie::vector<{}, {}>* {} = &{}_slot;\n", t, adjust_vector_size(), name, name); + auto vector_width = cgraVectorWidthAnalysis->get_width_for(bb->cont); + cgraVectorWidthAnalysis->register_width(def, vector_width); + + func_impls_.fmt("aie::vector<{}, {}> {}_slot;\n", t, vector_width, name); + func_impls_.fmt("aie::vector<{}, {}>* {} = &{}_slot;\n", t, vector_width, name, name); } else if (is_cgra_vector_kernel() && is_accum_type(slot->alloced_type())) { - func_impls_.fmt("aie::accum<{}, {}> {}_slot;\n", t, adjust_vector_size(), name); - func_impls_.fmt("aie::accum<{}, {}>* {} = &{}_slot;\n", t, adjust_vector_size(), name, name); + func_impls_.fmt("aie::accum<{}, {}> {}_slot;\n", t, cgraVectorWidthAnalysis->get_width_for(bb->cont), name); + func_impls_.fmt("aie::accum<{}, {}>* {} = &{}_slot;\n", t, cgraVectorWidthAnalysis->get_width_for(bb->cont), name, name); } else { func_impls_.fmt("{} {}_slot;\n", t, name); func_impls_.fmt("{}* {} = &{}_slot;\n", t, name, name); @@ -2712,7 +2650,25 @@ std::string CCodeGen::emit_def(BB* bb, const Def* def) { //so that we can use it all around the codegen auto get_type = [&](const Type* type, const std::string& base) { std::string reg = is_accum_type(type) ? "aie::accum" : "aie::vector"; - return reg + "<" + base + ", " + std::to_string(vector_size_) + ">"; + auto vector_width = cgraVectorWidthAnalysis->get_width_for(bb->cont); + if (auto load = def->isa()) { + auto alt_width = cgraVectorWidthAnalysis->get_width_for(load->ptr()); + if (alt_width > vector_width) { + vector_width = alt_width; + } + } + if (auto extract = def->isa()) { + if (auto load = extract->agg()->isa()) { + auto alt_width = cgraVectorWidthAnalysis->get_width_for(load->ptr()); + if (alt_width > vector_width) { + vector_width = alt_width; + } + } + } + + cgraVectorWidthAnalysis->register_width(def, vector_width); + + return reg + "<" + base + ", " + std::to_string(vector_width) + " /*val*/>"; }; bool should_modify = false;