mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
refactor: consolidate RVM instruction variants and clean up VM internals (#651)
Merge the three separate Assert* instructions (AssertNot, AssertCondition,
AssertNotUndefined) into a single `Guard { register, mode }` instruction
with a GuardMode enum. This cuts duplicated match arms across display,
listing, parser, dispatch, and all compiler emit sites.
Drop the unnecessary `#[repr(C)]` from the Instruction enum. It was never
exposed across FFI, so the C-compatible 4-byte discriminant was pure waste.
Without it Rust picks a 1-byte discriminant, shrinking every instruction
from 8 bytes to 6. A new `instruction_size` unit test locks this at 6.
While touching these files, also clean up several long-standing issues:
- Deduplicate the iteration-state setup in loops.rs by extracting a shared
resolve_iteration_state() helper -- the stack-based and stackless paths
had near-identical 40-line blocks.
- Collapse the ExitWithSuccess / ExitWithFailure match arms into one.
- In rules.rs, stop cloning Arc<Program> just to borrow a RuleInfo -- clone
the small RuleInfo struct directly and extract a get_rule_info() helper.
- Move the memory check into dispatch (runs per instruction) and remove the
now-dead enforce_memory_check() entry-point calls.
- Apply map_or_else style throughout listing.rs for consistency.
This commit is contained in:
committed by
GitHub
parent
1a8fc08773
commit
126cc12eb5
@@ -7,7 +7,7 @@ use crate::compiler::destructuring_planner::plans::{
|
||||
AssignmentPlan, BindingPlan, DestructuringPlan, WildcardSide,
|
||||
};
|
||||
use crate::lexer::Span;
|
||||
use crate::rvm::instructions::Instruction;
|
||||
use crate::rvm::instructions::{GuardMode, Instruction};
|
||||
use crate::value::Value;
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
@@ -91,19 +91,6 @@ impl<'a> Compiler<'a> {
|
||||
AssignmentPlan::EqualityCheck { lhs_expr, rhs_expr } => {
|
||||
let lhs_reg = self.compile_rego_expr_with_span(lhs_expr, lhs_expr.span(), false)?;
|
||||
let rhs_reg = self.compile_rego_expr_with_span(rhs_expr, rhs_expr.span(), false)?;
|
||||
if !self.soft_assert_mode {
|
||||
// AssertEq handles the equality assertion inline; the returned
|
||||
// register is not used as a boolean by the caller — it is the
|
||||
// expression's "result register" for potential downstream use.
|
||||
self.emit_instruction(
|
||||
Instruction::AssertEq {
|
||||
left: lhs_reg,
|
||||
right: rhs_reg,
|
||||
},
|
||||
span,
|
||||
);
|
||||
return Ok(lhs_reg);
|
||||
}
|
||||
let dest = self.alloc_register();
|
||||
self.emit_instruction(
|
||||
Instruction::Eq {
|
||||
@@ -113,6 +100,15 @@ impl<'a> Compiler<'a> {
|
||||
},
|
||||
span,
|
||||
);
|
||||
if !self.soft_assert_mode {
|
||||
self.emit_instruction(
|
||||
Instruction::Guard {
|
||||
register: dest,
|
||||
mode: GuardMode::Condition,
|
||||
},
|
||||
span,
|
||||
);
|
||||
}
|
||||
Ok(dest)
|
||||
}
|
||||
AssignmentPlan::WildcardMatch {
|
||||
@@ -125,7 +121,10 @@ impl<'a> Compiler<'a> {
|
||||
let rhs_reg =
|
||||
self.compile_rego_expr_with_span(rhs_expr, rhs_expr.span(), false)?;
|
||||
self.emit_instruction(
|
||||
Instruction::AssertNotUndefined { register: rhs_reg },
|
||||
Instruction::Guard {
|
||||
register: rhs_reg,
|
||||
mode: GuardMode::NotUndefined,
|
||||
},
|
||||
span,
|
||||
);
|
||||
Ok(self.load_bool_literal(true, span))
|
||||
@@ -134,7 +133,10 @@ impl<'a> Compiler<'a> {
|
||||
let lhs_reg =
|
||||
self.compile_rego_expr_with_span(lhs_expr, lhs_expr.span(), false)?;
|
||||
self.emit_instruction(
|
||||
Instruction::AssertNotUndefined { register: lhs_reg },
|
||||
Instruction::Guard {
|
||||
register: lhs_reg,
|
||||
mode: GuardMode::NotUndefined,
|
||||
},
|
||||
span,
|
||||
);
|
||||
Ok(self.load_bool_literal(true, span))
|
||||
@@ -206,44 +208,44 @@ impl<'a> Compiler<'a> {
|
||||
DestructuringPlan::EqualityExpr(expected_expr) => {
|
||||
let expected_reg =
|
||||
self.compile_rego_expr_with_span(expected_expr, expected_expr.span(), false)?;
|
||||
let cmp_reg = self.alloc_register();
|
||||
self.emit_instruction(
|
||||
Instruction::Eq {
|
||||
dest: cmp_reg,
|
||||
left: value_register,
|
||||
right: expected_reg,
|
||||
},
|
||||
span,
|
||||
);
|
||||
if self.soft_assert_mode {
|
||||
let cmp_reg = self.alloc_register();
|
||||
self.emit_instruction(
|
||||
Instruction::Eq {
|
||||
dest: cmp_reg,
|
||||
left: value_register,
|
||||
right: expected_reg,
|
||||
},
|
||||
span,
|
||||
);
|
||||
return Ok(Some(cmp_reg));
|
||||
}
|
||||
self.emit_instruction(
|
||||
Instruction::AssertEq {
|
||||
left: value_register,
|
||||
right: expected_reg,
|
||||
Instruction::Guard {
|
||||
register: cmp_reg,
|
||||
mode: GuardMode::Condition,
|
||||
},
|
||||
span,
|
||||
);
|
||||
}
|
||||
DestructuringPlan::EqualityValue(expected_value) => {
|
||||
let expected_reg = self.load_literal_value(expected_value, span);
|
||||
let cmp_reg = self.alloc_register();
|
||||
self.emit_instruction(
|
||||
Instruction::Eq {
|
||||
dest: cmp_reg,
|
||||
left: value_register,
|
||||
right: expected_reg,
|
||||
},
|
||||
span,
|
||||
);
|
||||
if self.soft_assert_mode {
|
||||
let cmp_reg = self.alloc_register();
|
||||
self.emit_instruction(
|
||||
Instruction::Eq {
|
||||
dest: cmp_reg,
|
||||
left: value_register,
|
||||
right: expected_reg,
|
||||
},
|
||||
span,
|
||||
);
|
||||
return Ok(Some(cmp_reg));
|
||||
}
|
||||
self.emit_instruction(
|
||||
Instruction::AssertEq {
|
||||
left: value_register,
|
||||
right: expected_reg,
|
||||
Instruction::Guard {
|
||||
register: cmp_reg,
|
||||
mode: GuardMode::Condition,
|
||||
},
|
||||
span,
|
||||
);
|
||||
@@ -263,8 +265,9 @@ impl<'a> Compiler<'a> {
|
||||
);
|
||||
if context.require_defined_values() {
|
||||
self.emit_instruction(
|
||||
Instruction::AssertNotUndefined {
|
||||
Instruction::Guard {
|
||||
register: element_reg,
|
||||
mode: GuardMode::NotUndefined,
|
||||
},
|
||||
span,
|
||||
);
|
||||
@@ -289,8 +292,9 @@ impl<'a> Compiler<'a> {
|
||||
span,
|
||||
);
|
||||
self.emit_instruction(
|
||||
Instruction::AssertNotUndefined {
|
||||
Instruction::Guard {
|
||||
register: field_reg,
|
||||
mode: GuardMode::NotUndefined,
|
||||
},
|
||||
span,
|
||||
);
|
||||
@@ -310,8 +314,9 @@ impl<'a> Compiler<'a> {
|
||||
span,
|
||||
);
|
||||
self.emit_instruction(
|
||||
Instruction::AssertNotUndefined {
|
||||
Instruction::Guard {
|
||||
register: field_reg,
|
||||
mode: GuardMode::NotUndefined,
|
||||
},
|
||||
span,
|
||||
);
|
||||
@@ -349,7 +354,13 @@ impl<'a> Compiler<'a> {
|
||||
self.add_variable(var_name, dest);
|
||||
|
||||
if context.require_defined_values() {
|
||||
self.emit_instruction(Instruction::AssertNotUndefined { register: dest }, span);
|
||||
self.emit_instruction(
|
||||
Instruction::Guard {
|
||||
register: dest,
|
||||
mode: GuardMode::NotUndefined,
|
||||
},
|
||||
span,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -393,13 +404,22 @@ impl<'a> Compiler<'a> {
|
||||
span,
|
||||
);
|
||||
|
||||
let cmp_reg = self.alloc_register();
|
||||
self.emit_instruction(
|
||||
Instruction::AssertEq {
|
||||
Instruction::Eq {
|
||||
dest: cmp_reg,
|
||||
left: actual_len_reg,
|
||||
right: expected_len_reg,
|
||||
},
|
||||
span,
|
||||
);
|
||||
self.emit_instruction(
|
||||
Instruction::Guard {
|
||||
register: cmp_reg,
|
||||
mode: GuardMode::Condition,
|
||||
},
|
||||
span,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ use super::{Compiler, CompilerError, Register, Result};
|
||||
use crate::ast::{Expr, ExprRef};
|
||||
use crate::compiler::destructuring_planner::plans::BindingPlan;
|
||||
use crate::lexer::Span;
|
||||
use crate::rvm::instructions::GuardMode;
|
||||
use crate::rvm::Instruction;
|
||||
use crate::Value;
|
||||
use alloc::{format, string::ToString};
|
||||
@@ -32,8 +33,9 @@ impl<'a> Compiler<'a> {
|
||||
let result_reg = reg;
|
||||
if assert_condition {
|
||||
self.emit_instruction(
|
||||
Instruction::AssertCondition {
|
||||
condition: result_reg,
|
||||
Instruction::Guard {
|
||||
register: result_reg,
|
||||
mode: GuardMode::Condition,
|
||||
},
|
||||
span,
|
||||
);
|
||||
@@ -113,8 +115,9 @@ impl<'a> Compiler<'a> {
|
||||
|
||||
if assert_condition {
|
||||
self.emit_instruction(
|
||||
Instruction::AssertCondition {
|
||||
condition: result_reg,
|
||||
Instruction::Guard {
|
||||
register: result_reg,
|
||||
mode: GuardMode::Condition,
|
||||
},
|
||||
span,
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
use super::{Compiler, CompilerError, ComprehensionType, ContextType, Result};
|
||||
use crate::ast::{self, LiteralStmt, Query};
|
||||
use crate::rvm::instructions::GuardMode;
|
||||
use crate::rvm::program::RuleType;
|
||||
use crate::rvm::Instruction;
|
||||
use alloc::format;
|
||||
@@ -242,7 +243,22 @@ impl<'a> Compiler<'a> {
|
||||
compiler.compile_rego_expr_with_span(expr, expr.span(), false)
|
||||
})?;
|
||||
|
||||
self.emit_instruction(Instruction::AssertNot { operand: expr_reg }, &stmt.span);
|
||||
let negated_reg = self.alloc_register();
|
||||
self.emit_instruction(
|
||||
Instruction::Not {
|
||||
dest: negated_reg,
|
||||
operand: expr_reg,
|
||||
},
|
||||
&stmt.span,
|
||||
);
|
||||
|
||||
self.emit_instruction(
|
||||
Instruction::Guard {
|
||||
register: negated_reg,
|
||||
mode: GuardMode::Condition,
|
||||
},
|
||||
&stmt.span,
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -543,14 +543,12 @@ impl<'a> Compiler<'a> {
|
||||
// A definition has a known static value if every body (including
|
||||
// else-branches) would produce the same literal.
|
||||
let def_static_value = if bodies.is_empty() {
|
||||
// No bodies — value comes from the head's value_expr.
|
||||
let head_value = self
|
||||
.context_stack
|
||||
.last()
|
||||
.and_then(|ctx| ctx.value_expr.clone());
|
||||
Self::static_value_of_expr(&head_value)
|
||||
} else {
|
||||
// Replay the same value_expr resolution as the body loop.
|
||||
let head_value = self
|
||||
.context_stack
|
||||
.last()
|
||||
|
||||
Reference in New Issue
Block a user