mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
feat(hoist): pre-compute loop hoisting metadata at compilation time (#483)
Introduce a compiler pass that analyzes and pre-computes loop hoisting information during policy compilation. This hoisted metadata is stored in lookup tables and made available to downstream consumers: - interpreter: use HoistedLoop entries during evaluation (replaces runtime scanning) - type inference: can leverage pre-computed loop structure for type propagation - RVM compiler: will consume hoisting metadata for optimized bytecode generation Changes: - populate loop hoisting tables during engine preparation and query snippet execution - refactor eval_stmts_in_loop and eval_output_expr_in_loop to consume HoistedLoop directly - add helper methods for accessing loop expressions, collections, and indices from HoistedLoop - extend Lookup with get_checked and into_slots for safe query context access and merging Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
committed by
GitHub
parent
9604fe86f1
commit
5d8387f4d9
@@ -846,6 +846,67 @@ impl Engine {
|
||||
query_module.num_statements = parser.num_statements();
|
||||
let query_schedule = Analyzer::new().analyze_query_snippet(&self.modules, &query_node)?;
|
||||
|
||||
// Populate loop hoisting for the query snippet
|
||||
// Query snippets are treated as if they're in a module appended at the end (same as analyzer)
|
||||
// The loop hoisting table already has capacity for this (ensured in prepare_for_eval)
|
||||
let module_idx = self.modules.len() as u32;
|
||||
|
||||
use crate::compiler::hoist::LoopHoister;
|
||||
let query_schedule_rc = Rc::new(query_schedule.clone());
|
||||
let mut hoister = LoopHoister::new_with_schedule(query_schedule_rc);
|
||||
hoister.populate_query_snippet(
|
||||
module_idx,
|
||||
&query_node,
|
||||
query_module.num_statements,
|
||||
query_module.num_expressions,
|
||||
)?;
|
||||
let query_loops = hoister.finalize();
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
debug_assert_eq!(
|
||||
query_loops.module_len(),
|
||||
module_idx as usize + 1,
|
||||
"query hoisting table missing expected module slot {}",
|
||||
module_idx
|
||||
);
|
||||
for stmt in &query_node.stmts {
|
||||
debug_assert!(
|
||||
query_loops
|
||||
.get_statement_loops(module_idx, stmt.sidx)
|
||||
.is_some(),
|
||||
"missing hoisted loop entry for query statement index {}",
|
||||
stmt.sidx
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the existing table, merge in the query loops, and set it back
|
||||
let mut existing_table = self.interpreter.take_loop_hoisting_table();
|
||||
existing_table.truncate_modules(self.modules.len());
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
debug_assert!(
|
||||
existing_table.module_len() <= self.modules.len(),
|
||||
"loop hoisting table should not retain extra modules before merge"
|
||||
);
|
||||
}
|
||||
existing_table.merge_query_loops(query_loops, self.modules.len());
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
for stmt in &query_node.stmts {
|
||||
debug_assert!(
|
||||
existing_table
|
||||
.get_statement_loops(module_idx, stmt.sidx)
|
||||
.is_some(),
|
||||
"missing hoisted loop entry after merge for module {} stmt {}",
|
||||
module_idx,
|
||||
stmt.sidx
|
||||
);
|
||||
}
|
||||
}
|
||||
self.interpreter.set_loop_hoisting_table(existing_table);
|
||||
|
||||
Ok((Ref::new(query_module), query_node, query_schedule))
|
||||
}
|
||||
|
||||
@@ -873,9 +934,8 @@ impl Engine {
|
||||
if !self.prepared {
|
||||
// Analyze the modules and determine how statements must be scheduled.
|
||||
let analyzer = Analyzer::new();
|
||||
let schedule = analyzer.analyze(&self.modules)?;
|
||||
let schedule = Rc::new(analyzer.analyze(&self.modules)?);
|
||||
|
||||
self.interpreter.set_schedule(Some(schedule));
|
||||
self.interpreter.set_modules(self.modules.clone());
|
||||
|
||||
self.interpreter.clear_builtins_cache();
|
||||
@@ -889,6 +949,16 @@ impl Engine {
|
||||
self.interpreter.gather_rules()?;
|
||||
self.interpreter.process_imports()?;
|
||||
|
||||
// Populate loop hoisting table for efficient evaluation
|
||||
// Reserve capacity for 1 extra module (for query modules)
|
||||
use crate::compiler::hoist::LoopHoister;
|
||||
let hoister = LoopHoister::new_with_schedule(schedule.clone());
|
||||
let loop_lookup = hoister.populate_with_extra_capacity(&self.modules, 0)?;
|
||||
self.interpreter.set_loop_hoisting_table(loop_lookup);
|
||||
|
||||
// Set schedule after hoisting completes
|
||||
self.interpreter.set_schedule(Some(schedule));
|
||||
|
||||
#[cfg(feature = "azure_policy")]
|
||||
if for_target {
|
||||
// Resolve and validate target specifications across all modules
|
||||
|
||||
Reference in New Issue
Block a user