From d729e0e7179fa39e8de3419aeeff98cc8a270c2b Mon Sep 17 00:00:00 2001 From: Bhargav Annem Date: Fri, 23 Oct 2026 19:43:32 -0700 Subject: [PATCH 1/2] feat: precomputed symbols --- src/bin/libint/dg.cc | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/bin/libint/dg.cc b/src/bin/libint/dg.cc index eded6a54f..38736c946 100644 --- a/src/bin/libint/dg.cc +++ b/src/bin/libint/dg.cc @@ -37,6 +37,7 @@ #include #include #include +#include #include using namespace std; @@ -1034,6 +1035,13 @@ std::string declare_function(const std::shared_ptr& context, } // namespace libint2 namespace { +std::string precomputed_symbol(const std::shared_ptr& context, + const std::string& label) { + const std::string name = context->label_to_name(label); + if (context->cparams()->max_vector_length() == 1) return "hv_" + name; + return "inteval->" + name + "[vi]"; +} + template std::shared_ptr to_memoryblks(Container& vertices) { std::shared_ptr result(new MemBlockSet); @@ -1205,6 +1213,21 @@ void DirectedGraph::generate_code( def << context->macro_endif(); } + if (context->cparams()->max_vector_length() == 1) { + std::set declared; + for (const auto& v : stack_) { + const ver_ptr& vptr = vertex_ptr(v); + if (!vptr->precomputed() || !vptr->symbol_set() || + vptr->symbol() != precomputed_symbol(context, vptr->label()) || + !declared.insert(vptr->symbol()).second) + continue; + def << context->decldef( + context->const_modifier() + context->type_name(), + vptr->symbol(), + "inteval->" + context->label_to_name(vptr->label()) + "[0]"); + } + } + // now print out the code for this graph print_def(context, def, dims, args); @@ -1528,10 +1551,7 @@ void DirectedGraph::assign_symbols( // test if the vertex is precomputed runtime quantity, like a geometric // parameter if ((vptr)->precomputed()) { - std::string symbol("inteval->"); - symbol += context->label_to_name((vptr)->label()); - symbol += "[vi]"; - (vptr)->set_symbol(symbol); + (vptr)->set_symbol(precomputed_symbol(context, (vptr)->label())); continue; } From 200b83e02b3457c9a1f76427ef28589134fe5a19 Mon Sep 17 00:00:00 2001 From: Eduard Valeyev Date: Sun, 25 Oct 2026 07:50:10 -0400 Subject: [PATCH 2/2] DirectedGraph: record hoisted precomputed quantities in assign_symbols generate_code() used to rediscover the hoisted inteval loads by scanning stack_ and comparing each vertex's symbol against a recomputed "hv_" name. assign_symbols() now records each hoisted local together with the inteval field it is loaded from, and generate_code() declares exactly that list. Vertices that refer to another vertex are not hoisted, since their symbol comes from the referred vertex. The generated library is byte-identical to that of the previous commit. --- src/bin/libint/dg.cc | 46 +++++++++++++++++++++++--------------------- src/bin/libint/dg.h | 10 ++++++++-- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/bin/libint/dg.cc b/src/bin/libint/dg.cc index 38736c946..f94443f4a 100644 --- a/src/bin/libint/dg.cc +++ b/src/bin/libint/dg.cc @@ -428,6 +428,7 @@ void DirectedGraph::reset() { targets_.clear(); first_to_compute_.reset(); func_names_.clear(); + hoisted_precomputed_.clear(); } /// Apply a strategy to all vertices not yet computed (i.e. which do not have @@ -1035,13 +1036,6 @@ std::string declare_function(const std::shared_ptr& context, } // namespace libint2 namespace { -std::string precomputed_symbol(const std::shared_ptr& context, - const std::string& label) { - const std::string name = context->label_to_name(label); - if (context->cparams()->max_vector_length() == 1) return "hv_" + name; - return "inteval->" + name + "[vi]"; -} - template std::shared_ptr to_memoryblks(Container& vertices) { std::shared_ptr result(new MemBlockSet); @@ -1213,20 +1207,11 @@ void DirectedGraph::generate_code( def << context->macro_endif(); } - if (context->cparams()->max_vector_length() == 1) { - std::set declared; - for (const auto& v : stack_) { - const ver_ptr& vptr = vertex_ptr(v); - if (!vptr->precomputed() || !vptr->symbol_set() || - vptr->symbol() != precomputed_symbol(context, vptr->label()) || - !declared.insert(vptr->symbol()).second) - continue; - def << context->decldef( - context->const_modifier() + context->type_name(), - vptr->symbol(), - "inteval->" + context->label_to_name(vptr->label()) + "[0]"); - } - } + // load the precomputed quantities that assign_symbols() hoisted + for (const auto& h : hoisted_precomputed_) + def << context->decldef( + context->const_modifier() + context->type_name(), h.first, + h.second); // now print out the code for this graph print_def(context, def, dims, args); @@ -1525,6 +1510,8 @@ void DirectedGraph::assign_symbols( } // then process all other symbols, EXCEPT operators + hoisted_precomputed_.clear(); + std::set hoisted_names; for (iter v = stack_.begin(); v != stack_.end(); ++v) { const ver_ptr& vptr = vertex_ptr(*v); #if DEBUG @@ -1551,7 +1538,22 @@ void DirectedGraph::assign_symbols( // test if the vertex is precomputed runtime quantity, like a geometric // parameter if ((vptr)->precomputed()) { - (vptr)->set_symbol(precomputed_symbol(context, (vptr)->label())); + const std::string field = + "inteval->" + context->label_to_name((vptr)->label()); + // in scalar code load each such quantity from inteval once, into a + // local declared by generate_code(): stores to the stack may alias + // inteval's fields, so the compiler cannot hoist these loads itself. + // a vertex that refers to another takes its symbol from the referred + // vertex, so do not hoist it + if (context->cparams()->max_vector_length() == 1 && + !(vptr)->refers_to_another()) { + const std::string local = + "hv_" + context->label_to_name((vptr)->label()); + (vptr)->set_symbol(local); + if (hoisted_names.insert(local).second) + hoisted_precomputed_.emplace_back(local, field + "[0]"); + } else + (vptr)->set_symbol(field + "[vi]"); continue; } diff --git a/src/bin/libint/dg.h b/src/bin/libint/dg.h index 0f85abc8f..3bb8fa90e 100644 --- a/src/bin/libint/dg.h +++ b/src/bin/libint/dg.h @@ -341,6 +341,12 @@ class DirectedGraph : public std::enable_shared_from_this { */ FuncNameContainer func_names_; + /** precomputed quantities hoisted by assign_symbols() in scalar code, as + {local variable name, inteval field it is loaded from}, in the order + generate_code() declares them + */ + std::vector> hoisted_precomputed_; + #if !USE_ASSOCCONTAINER_BASED_DIRECTEDGRAPH static const unsigned int default_size_ = 100; #endif @@ -427,7 +433,7 @@ class DirectedGraph : public std::enable_shared_from_this { /// graph std::shared_ptr insert_expr_at( const std::shared_ptr& where, - const std::shared_ptr >& expr); + const std::shared_ptr>& expr); /// This function replaces RecurrenceRelations with concrete arithemtical /// expressions void replace_rr_with_expr(); @@ -529,7 +535,7 @@ void extract_symbols(const std::shared_ptr& dg); // use these functors with DirectedGraph::foreach struct PrerequisitesExtractor { - std::deque > vertices; + std::deque> vertices; void operator()(const std::shared_ptr& v); }; struct VertexPrinter {