ghostscope_compiler/ebpf/
variables.rs

1//! Variable management for eBPF code generation
2//!
3//! This module handles variable storage, retrieval, and type tracking.
4
5use super::context::{CodeGenError, EbpfContext, Result};
6use crate::script::VarType;
7use inkwell::types::BasicTypeEnum;
8use inkwell::values::BasicValueEnum;
9use inkwell::AddressSpace;
10use tracing::{debug, info};
11
12impl<'ctx> EbpfContext<'ctx> {
13    /// Register a DWARF alias variable. The value expression is stored and resolved at use time.
14    pub fn set_alias_variable(&mut self, name: &str, expr: crate::script::Expr) {
15        self.alias_vars.insert(name.to_string(), expr);
16        // Ensure any previous concrete variable storage is cleared to avoid confusion
17        self.variables.remove(name);
18        self.var_types.remove(name);
19        self.optimized_out_vars.remove(name);
20        self.var_pc_addresses.remove(name);
21        self.string_vars.remove(name);
22    }
23
24    /// Check whether an alias variable exists
25    pub fn alias_variable_exists(&self, name: &str) -> bool {
26        self.alias_vars.contains_key(name)
27    }
28
29    /// Get a clone of the stored alias expression if present
30    pub fn get_alias_variable(&self, name: &str) -> Option<crate::script::Expr> {
31        self.alias_vars.get(name).cloned()
32    }
33    /// Store a variable value
34    pub fn store_variable(&mut self, name: &str, value: BasicValueEnum<'ctx>) -> Result<()> {
35        // Determine variable type from the value
36        let var_type = match value {
37            BasicValueEnum::IntValue(_) => VarType::Int,
38            BasicValueEnum::FloatValue(_) => VarType::Float,
39            BasicValueEnum::PointerValue(_) => VarType::String,
40            _ => {
41                return Err(CodeGenError::TypeError(
42                    "Unsupported variable type".to_string(),
43                ))
44            }
45        };
46
47        // In eBPF, we don't use dynamic stack allocation. Instead, we store variables
48        // directly as values and use them when needed. For simple cases, we can just
49        // track the value directly.
50
51        // For eBPF compatibility, we store variables as direct values rather than
52        // allocating stack space. This works for our current use case where we
53        // mainly read variables and send them via ringbuf.
54
55        // Create a global variable if we need persistent storage
56        let global_name = format!("_var_{name}");
57        let global_var = match value {
58            BasicValueEnum::IntValue(_) => {
59                let i64_type = self.context.i64_type();
60                let global =
61                    self.module
62                        .add_global(i64_type, Some(AddressSpace::default()), &global_name);
63                global.set_initializer(&i64_type.const_zero());
64                global.as_pointer_value()
65            }
66            BasicValueEnum::FloatValue(_) => {
67                let f64_type = self.context.f64_type();
68                let global =
69                    self.module
70                        .add_global(f64_type, Some(AddressSpace::default()), &global_name);
71                global.set_initializer(&f64_type.const_zero());
72                global.as_pointer_value()
73            }
74            BasicValueEnum::PointerValue(_) => {
75                let ptr_type = self.context.ptr_type(AddressSpace::default());
76                let global =
77                    self.module
78                        .add_global(ptr_type, Some(AddressSpace::default()), &global_name);
79                global.set_initializer(&ptr_type.const_null());
80                global.as_pointer_value()
81            }
82            _ => {
83                return Err(CodeGenError::TypeError(
84                    "Unsupported variable type".to_string(),
85                ))
86            }
87        };
88
89        // Store the value in the global variable
90        self.builder
91            .build_store(global_var, value)
92            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
93
94        // Track the variable
95        self.variables.insert(name.to_string(), global_var);
96        self.var_types.insert(name.to_string(), var_type.clone());
97
98        info!(
99            "store_variable: Stored variable '{}' with type {:?}",
100            name, var_type
101        );
102        debug!("store_variable: Value type stored: {:?}", value.get_type());
103        match &value {
104            BasicValueEnum::IntValue(iv) => debug!(
105                "store_variable: Stored IntValue with bit width {}",
106                iv.get_type().get_bit_width()
107            ),
108            BasicValueEnum::FloatValue(_) => debug!("store_variable: Stored FloatValue"),
109            BasicValueEnum::PointerValue(_) => debug!("store_variable: Stored PointerValue"),
110            _ => debug!("store_variable: Stored other type"),
111        }
112        Ok(())
113    }
114
115    /// Retrieve a variable value
116    pub fn load_variable(&mut self, name: &str) -> Result<BasicValueEnum<'ctx>> {
117        if let Some(alloca) = self.variables.get(name) {
118            let var_type = self
119                .var_types
120                .get(name)
121                .ok_or_else(|| CodeGenError::VariableNotFound(name.to_string()))?;
122
123            debug!(
124                "load_variable: Loading variable '{}' with stored type {:?}",
125                name, var_type
126            );
127
128            // Get the pointed-to type, not the pointer type itself
129            let pointed_type: BasicTypeEnum = match var_type {
130                VarType::Int => self.context.i64_type().into(),
131                VarType::Float => self.context.f64_type().into(),
132                VarType::String => self.context.ptr_type(AddressSpace::default()).into(),
133                VarType::Bool => self.context.bool_type().into(),
134            };
135
136            let loaded_value = self
137                .builder
138                .build_load(pointed_type, *alloca, name)
139                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
140
141            debug!(
142                "load_variable: Loaded variable '{}' with actual type: {:?}",
143                name,
144                loaded_value.get_type()
145            );
146            match &loaded_value {
147                BasicValueEnum::IntValue(iv) => debug!(
148                    "load_variable: Loaded IntValue with bit width {}",
149                    iv.get_type().get_bit_width()
150                ),
151                BasicValueEnum::FloatValue(_) => debug!("load_variable: Loaded FloatValue"),
152                BasicValueEnum::PointerValue(_) => debug!("load_variable: Loaded PointerValue"),
153                _ => debug!("load_variable: Loaded other type"),
154            }
155
156            Ok(loaded_value)
157        } else {
158            Err(CodeGenError::VariableNotFound(name.to_string()))
159        }
160    }
161
162    /// Check if a variable exists in the current scope
163    pub fn variable_exists(&self, name: &str) -> bool {
164        self.variables.contains_key(name)
165    }
166
167    /// Get variable type
168    pub fn get_variable_type(&self, name: &str) -> Option<&VarType> {
169        self.var_types.get(name)
170    }
171
172    /// Clear all variables (for new scope)
173    pub fn clear_variables(&mut self) {
174        debug!("Clearing all variables from scope");
175        self.variables.clear();
176        self.var_types.clear();
177        self.optimized_out_vars.clear();
178        self.var_pc_addresses.clear();
179        self.alias_vars.clear();
180        self.string_vars.clear();
181    }
182
183    /// Register a string variable's bytes (including optional NUL terminator)
184    pub fn set_string_variable_bytes(&mut self, name: &str, bytes: Vec<u8>) {
185        self.string_vars.insert(name.to_string(), bytes);
186        // String vars are concrete script variables of VarType::String; no alias
187        self.alias_vars.remove(name);
188    }
189
190    /// Get string variable bytes if present
191    pub fn get_string_variable_bytes(&self, name: &str) -> Option<&Vec<u8>> {
192        self.string_vars.get(name)
193    }
194}