Skip to main content

ghostscope_compiler/ebpf/
expression.rs

1//! Expression compilation for eBPF code generation
2//!
3//! This module handles compilation of various expression types to LLVM IR.
4
5use super::context::{CodeGenError, EbpfContext, Result};
6use crate::script::{BinaryOp, Expr};
7use aya_ebpf_bindings::bindings::bpf_func_id::BPF_FUNC_probe_read_user;
8use ghostscope_dwarf::TypeInfo as DwarfType;
9use inkwell::values::{BasicValueEnum, IntValue};
10use inkwell::AddressSpace;
11use tracing::debug;
12
13// compare cap is provided via compile_options.compare_cap (config: ebpf.compare_cap)
14
15impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> {
16    pub(crate) fn get_host_pid_tid_values(&mut self) -> Result<(IntValue<'ctx>, IntValue<'ctx>)> {
17        let i32_type = self.context.i32_type();
18        let i64_type = self.context.i64_type();
19
20        // bpf_get_current_pid_tgid() returns:
21        // - high 32 bits: TGID (process ID / getpid() view)
22        // - low 32 bits: PID (thread ID / gettid() view)
23        let host_pid_tgid = self.get_current_pid_tgid()?;
24        let host_tid = self
25            .builder
26            .build_and(
27                host_pid_tgid,
28                i64_type.const_int(0xFFFF_FFFF, false),
29                "host_tid",
30            )
31            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
32        let host_pid = self
33            .builder
34            .build_right_shift(
35                host_pid_tgid,
36                i64_type.const_int(32, false),
37                false,
38                "host_pid",
39            )
40            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
41
42        let host_pid_i32 = self
43            .builder
44            .build_int_truncate(host_pid, i32_type, "host_pid_i32")
45            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
46        let host_tid_i32 = self
47            .builder
48            .build_int_truncate(host_tid, i32_type, "host_tid_i32")
49            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
50
51        Ok((host_pid_i32, host_tid_i32))
52    }
53
54    pub(crate) fn get_special_pid_tid_values(
55        &mut self,
56    ) -> Result<(IntValue<'ctx>, IntValue<'ctx>)> {
57        const BPF_FUNC_GET_NS_CURRENT_PID_TGID: u64 = 120;
58        const BPF_PIDNS_INFO_SIZE: u64 = 8; // struct { u32 pid; u32 tgid; }
59
60        let i32_type = self.context.i32_type();
61        let i64_type = self.context.i64_type();
62        let (host_pid_i32, host_tid_i32) = self.get_host_pid_tid_values()?;
63
64        let ns_spec = if let Some(crate::PidFilterSpec::NamespaceTgid { pid_ns, .. }) =
65            self.compile_options.pid_filter_spec
66        {
67            pid_ns.helper_dev_inode()
68        } else {
69            self.compile_options
70                .special_pid_ns
71                .and_then(|pid_ns| pid_ns.helper_dev_inode())
72        };
73        let Some((pid_ns_dev, pid_ns_inode)) = ns_spec else {
74            let host_pid = self
75                .builder
76                .build_int_z_extend(host_pid_i32, i64_type, "selected_host_pid")
77                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
78            let host_tid = self
79                .builder
80                .build_int_z_extend(host_tid_i32, i64_type, "selected_host_tid")
81                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
82            return Ok((host_pid, host_tid));
83        };
84
85        let ptr_type = self.context.ptr_type(AddressSpace::default());
86        let key_arr_ty = i32_type.array_type(4);
87        let key_alloca = self.pm_key_alloca.ok_or_else(|| {
88            CodeGenError::LLVMError("pm_key not allocated in entry block".to_string())
89        })?;
90        // Reuse entry-allocated stack key storage: helper only needs first 8 bytes.
91        self.builder
92            .build_store(key_alloca, key_arr_ty.const_zero())
93            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
94
95        let pidns_info_ptr = self
96            .builder
97            .build_bit_cast(key_alloca, ptr_type, "special_pidns_info_ptr")
98            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
99
100        let helper_args = [
101            i64_type.const_int(pid_ns_dev, false).into(),
102            i64_type.const_int(pid_ns_inode, false).into(),
103            pidns_info_ptr,
104            i64_type.const_int(BPF_PIDNS_INFO_SIZE, false).into(),
105        ];
106        let helper_ret = self.create_bpf_helper_call(
107            BPF_FUNC_GET_NS_CURRENT_PID_TGID,
108            &helper_args,
109            i64_type.into(),
110            "special_ns_pid_tgid_ret",
111        )?;
112        let helper_ret = match helper_ret {
113            BasicValueEnum::IntValue(v) => v,
114            _ => {
115                return Err(CodeGenError::LLVMError(
116                    "bpf_get_ns_current_pid_tgid did not return integer".to_string(),
117                ))
118            }
119        };
120
121        let helper_ok = self
122            .builder
123            .build_int_compare(
124                inkwell::IntPredicate::EQ,
125                helper_ret,
126                i64_type.const_zero(),
127                "special_ns_helper_ok",
128            )
129            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
130
131        let ns_pid_ptr = unsafe {
132            self.builder.build_gep(
133                key_arr_ty,
134                key_alloca,
135                &[i32_type.const_zero(), i32_type.const_zero()],
136                "special_ns_pid_ptr",
137            )
138        }
139        .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
140        let ns_tgid_ptr = unsafe {
141            self.builder.build_gep(
142                key_arr_ty,
143                key_alloca,
144                &[i32_type.const_zero(), i32_type.const_int(1, false)],
145                "special_ns_tgid_ptr",
146            )
147        }
148        .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
149
150        let ns_pid = self
151            .builder
152            .build_load(i32_type, ns_pid_ptr, "special_ns_pid")
153            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
154            .into_int_value();
155        let ns_tgid = self
156            .builder
157            .build_load(i32_type, ns_tgid_ptr, "special_ns_tgid")
158            .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
159            .into_int_value();
160
161        let selected_pid_i32 = self
162            .builder
163            .build_select(helper_ok, ns_tgid, host_pid_i32, "selected_pid_i32")
164            .map_err(|e| CodeGenError::Builder(e.to_string()))?
165            .into_int_value();
166        let selected_tid_i32 = self
167            .builder
168            .build_select(helper_ok, ns_pid, host_tid_i32, "selected_tid_i32")
169            .map_err(|e| CodeGenError::Builder(e.to_string()))?
170            .into_int_value();
171
172        let selected_pid = self
173            .builder
174            .build_int_z_extend(selected_pid_i32, i64_type, "selected_pid")
175            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
176        let selected_tid = self
177            .builder
178            .build_int_z_extend(selected_tid_i32, i64_type, "selected_tid")
179            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
180
181        Ok((selected_pid, selected_tid))
182    }
183
184    fn unwrap_dwarf_type_aliases(mut t: &DwarfType) -> &DwarfType {
185        loop {
186            match t {
187                DwarfType::TypedefType {
188                    underlying_type, ..
189                } => t = underlying_type.as_ref(),
190                DwarfType::QualifiedType {
191                    underlying_type, ..
192                } => t = underlying_type.as_ref(),
193                _ => break,
194            }
195        }
196        t
197    }
198
199    fn is_dwarf_aggregate_expr(&mut self, expr: &Expr) -> bool {
200        if let Ok(Some(var)) = self.query_dwarf_for_complex_expr(expr) {
201            if let Some(ref ty) = var.dwarf_type {
202                return matches!(
203                    Self::unwrap_dwarf_type_aliases(ty),
204                    DwarfType::StructType { .. }
205                        | DwarfType::UnionType { .. }
206                        | DwarfType::ArrayType { .. }
207                );
208            }
209        }
210        false
211    }
212
213    /// Heuristic check: whether an expression should be treated as a pointer/address
214    /// Returns true for:
215    /// - Explicit address-of forms (&expr)
216    /// - Script string literals (compile to pointer data)
217    /// - Alias variables bound to addresses
218    /// - DWARF-backed expressions whose type is pointer or array
219    fn is_pointer_like_expr(&mut self, expr: &Expr) -> bool {
220        use crate::script::Expr as E;
221        match expr {
222            E::AddressOf(_) => return true,
223            E::String(_) => return true,
224            E::Variable(name) => {
225                if self.alias_variable_exists(name) {
226                    return true;
227                }
228            }
229            _ => {}
230        }
231
232        if let Ok(Some(var)) = self.query_dwarf_for_complex_expr(expr) {
233            if let Some(ref ty) = var.dwarf_type {
234                let t = Self::unwrap_dwarf_type_aliases(ty);
235                if matches!(
236                    t,
237                    DwarfType::PointerType { .. } | DwarfType::ArrayType { .. }
238                ) {
239                    return true;
240                }
241            }
242        }
243        false
244    }
245    /// Ensure that when an expression refers to a DWARF-backed variable (not via address-of),
246    /// the variable's DWARF type is a pointer or array (decays to pointer for memcmp/strncmp).
247    fn ensure_dwarf_pointer_arg(&mut self, e: &Expr, where_ctx: &str) -> Result<()> {
248        // Allow explicit address-of forms (&expr), which purposefully produce a pointer
249        if matches!(e, Expr::AddressOf(_)) {
250            return Ok(());
251        }
252        match self.query_dwarf_for_complex_expr(e) {
253            Ok(Some(var)) => {
254                let Some(mut ty) = var.dwarf_type.as_ref() else {
255                    return Err(CodeGenError::TypeError(format!(
256                        "{where_ctx}: DWARF variable has no type information"
257                    )));
258                };
259                // Unwrap typedef/qualified wrappers
260                loop {
261                    match ty {
262                        DwarfType::TypedefType {
263                            underlying_type, ..
264                        } => ty = underlying_type.as_ref(),
265                        DwarfType::QualifiedType {
266                            underlying_type, ..
267                        } => ty = underlying_type.as_ref(),
268                        _ => break,
269                    }
270                }
271                if !matches!(
272                    ty,
273                    DwarfType::PointerType { .. } | DwarfType::ArrayType { .. }
274                ) {
275                    return Err(CodeGenError::TypeError(format!(
276                        "{where_ctx}: only pointer or array DWARF variables are supported"
277                    )));
278                }
279                Ok(())
280            }
281            // No DWARF info or analyzer missing: allow script-level pointer values
282            Ok(None) | Err(_) => match self.compile_expr(e) {
283                Ok(BasicValueEnum::PointerValue(_)) => Ok(()),
284                _ => Err(CodeGenError::TypeError(format!(
285                    "{where_ctx}: expression is not a pointer"
286                ))),
287            },
288        }
289    }
290
291    /// Resolve an expression to an i64 pointer value. Accepts integer (address) and pointer values;
292    /// falls back to DWARF evaluation for complex expressions.
293    pub(crate) fn resolve_ptr_i64_from_expr(
294        &mut self,
295        e: &Expr,
296    ) -> Result<inkwell::values::IntValue<'ctx>> {
297        let mut visited = std::collections::HashSet::new();
298        self.resolve_ptr_i64_from_expr_internal(e, &mut visited, 0)
299    }
300
301    fn resolve_ptr_i64_from_expr_internal(
302        &mut self,
303        e: &Expr,
304        visited: &mut std::collections::HashSet<String>,
305        depth: usize,
306    ) -> Result<inkwell::values::IntValue<'ctx>> {
307        use crate::script::ast::BinaryOp as BO;
308        use crate::script::ast::Expr as E;
309        use inkwell::values::BasicValueEnum::*;
310        const MAX_DEPTH: usize = 64;
311        if depth > MAX_DEPTH {
312            return Err(CodeGenError::TypeError(
313                "alias expansion depth exceeded (cycle?)".into(),
314            ));
315        }
316        // Alias variable indirection: resolve its target expression first
317        if let E::Variable(name) = e {
318            if self.alias_variable_exists(name) {
319                if !visited.insert(name.clone()) {
320                    return Err(CodeGenError::TypeError(format!(
321                        "alias cycle detected for '{name}'"
322                    )));
323                }
324                if let Some(target) = self.get_alias_variable(name) {
325                    let r = self.resolve_ptr_i64_from_expr_internal(&target, visited, depth + 1);
326                    visited.remove(name);
327                    return r;
328                }
329            }
330        }
331        // Special-case: explicit address-of must yield a pointer-sized address
332        if let E::AddressOf(inner) = e {
333            // Support alias variables transparently: &alias -> address of aliased DWARF expr
334            let resolved_inner: &E = if let E::Variable(name) = inner.as_ref() {
335                if self.alias_variable_exists(name) {
336                    // Owned target for query
337                    if let Some(target) = self.get_alias_variable(name) {
338                        if let Some(var) = self.query_dwarf_for_complex_expr(&target)? {
339                            let module_hint = self.current_resolved_var_module_path.clone();
340                            let status_ptr = if self.condition_context_active {
341                                Some(self.get_or_create_cond_error_global())
342                            } else {
343                                None
344                            };
345                            return self.evaluation_result_to_address_with_hint(
346                                &var.evaluation_result,
347                                status_ptr,
348                                module_hint.as_deref(),
349                            );
350                        } else {
351                            return Err(CodeGenError::TypeError(
352                                "cannot take address of unresolved expression".into(),
353                            ));
354                        }
355                    } else {
356                        return Err(CodeGenError::TypeError(
357                            "cannot take address of unresolved expression".into(),
358                        ));
359                    }
360                } else {
361                    inner.as_ref()
362                }
363            } else {
364                inner.as_ref()
365            };
366
367            if let Some(var) = self.query_dwarf_for_complex_expr(resolved_inner)? {
368                let module_hint = self.current_resolved_var_module_path.clone();
369                let status_ptr = if self.condition_context_active {
370                    Some(self.get_or_create_cond_error_global())
371                } else {
372                    None
373                };
374                return self.evaluation_result_to_address_with_hint(
375                    &var.evaluation_result,
376                    status_ptr,
377                    module_hint.as_deref(),
378                );
379            } else {
380                return Err(CodeGenError::TypeError(
381                    "cannot take address of unresolved expression".into(),
382                ));
383            }
384        }
385
386        // Support constant-offset addressing: (alias_expr + K) or (K + alias_expr)
387        if let E::BinaryOp { left, op, right } = e {
388            if matches!(op, BO::Add) {
389                let is_nonneg_lit = |x: &E| matches!(x, E::Int(v) if *v >= 0);
390                // alias + K
391                if is_nonneg_lit(right) {
392                    if let Ok(base) =
393                        self.resolve_ptr_i64_from_expr_internal(left, visited, depth + 1)
394                    {
395                        if let E::Int(k) = &**right {
396                            let off = self.context.i64_type().const_int(*k as u64, false);
397                            return self
398                                .builder
399                                .build_int_add(base, off, "ptr_add")
400                                .map_err(|e| CodeGenError::Builder(e.to_string()));
401                        }
402                    }
403                }
404                // K + alias
405                if is_nonneg_lit(left) {
406                    if let Ok(base) =
407                        self.resolve_ptr_i64_from_expr_internal(right, visited, depth + 1)
408                    {
409                        if let E::Int(k) = &**left {
410                            let off = self.context.i64_type().const_int(*k as u64, false);
411                            return self
412                                .builder
413                                .build_int_add(base, off, "ptr_add")
414                                .map_err(|e| CodeGenError::Builder(e.to_string()));
415                        }
416                    }
417                }
418            }
419        }
420        // Prefer DWARF-based address resolution first so that array/aggregate
421        // expressions decay to their base address rather than loading values.
422        if let Ok(Some(var)) = self.query_dwarf_for_complex_expr(e) {
423            if let Some(mut dty) = var.dwarf_type.as_ref() {
424                // unwrap aliases
425                loop {
426                    match dty {
427                        DwarfType::TypedefType {
428                            underlying_type, ..
429                        } => dty = underlying_type.as_ref(),
430                        DwarfType::QualifiedType {
431                            underlying_type, ..
432                        } => dty = underlying_type.as_ref(),
433                        _ => break,
434                    }
435                }
436                match dty {
437                    DwarfType::PointerType { .. } => {
438                        let val_any = self.evaluate_result_to_llvm_value(
439                            &var.evaluation_result,
440                            dty,
441                            &var.name,
442                            self.get_compile_time_context()?.pc_address,
443                            None,
444                        )?;
445                        match val_any {
446                            IntValue(iv) => Ok(iv),
447                            PointerValue(pv) => self
448                                .builder
449                                .build_ptr_to_int(pv, self.context.i64_type(), "ptr_as_i64")
450                                .map_err(|e| CodeGenError::Builder(e.to_string())),
451                            _ => Err(CodeGenError::TypeError(
452                                "DWARF value is not pointer/integer".into(),
453                            )),
454                        }
455                    }
456                    DwarfType::ArrayType { .. } => {
457                        // Use the base address of the array as pointer
458                        let module_hint = self.current_resolved_var_module_path.clone();
459                        let status_ptr = if self.condition_context_active {
460                            Some(self.get_or_create_cond_error_global())
461                        } else {
462                            None
463                        };
464                        self.evaluation_result_to_address_with_hint(
465                            &var.evaluation_result,
466                            status_ptr,
467                            module_hint.as_deref(),
468                        )
469                    }
470                    _ => Err(CodeGenError::TypeError(
471                        "DWARF value is not pointer/array".into(),
472                    )),
473                }
474            } else {
475                let module_hint = self.current_resolved_var_module_path.clone();
476                let status_ptr = if self.condition_context_active {
477                    Some(self.get_or_create_cond_error_global())
478                } else {
479                    None
480                };
481                self.evaluation_result_to_address_with_hint(
482                    &var.evaluation_result,
483                    status_ptr,
484                    module_hint.as_deref(),
485                )
486            }
487        } else {
488            // No DWARF-backed address and not an address-of/alias+const: reject script-level pointers.
489            Err(CodeGenError::TypeError(
490                "expression is not a pointer/address".into(),
491            ))
492        }
493    }
494    /// Builtin memcmp (boolean variant): returns true iff first `len` bytes equal.
495    /// Supports dynamic `len` (expr), clamped to [0, compare_cap].
496    fn compile_memcmp_builtin(
497        &mut self,
498        a_expr: &Expr,
499        b_expr: &Expr,
500        len_expr: &Expr,
501    ) -> Result<BasicValueEnum<'ctx>> {
502        // Note: constant hex/len validation happens at parse-time; dynamic cases are handled at runtime by masking bytes.
503        // Important: Clear register cache to avoid reusing register values
504        // loaded in a previous basic block, which can violate SSA dominance
505        // when multiple memcmp calls appear in one function.
506        self.register_cache.clear();
507
508        // Note: do not resolve pointers yet; if either side is hex("...") we will synthesize bytes
509
510        // Compile length expr to i32 and clamp to [0, CAP]
511        let len_val = self.compile_expr(len_expr)?;
512        let len_iv = match len_val {
513            BasicValueEnum::IntValue(iv) => iv,
514            _ => {
515                return Err(CodeGenError::TypeError(
516                    "memcmp length must be an integer expression".into(),
517                ))
518            }
519        };
520        let i32_ty = self.context.i32_type();
521        let len_i32 = if len_iv.get_type().get_bit_width() > 32 {
522            self.builder
523                .build_int_truncate(len_iv, i32_ty, "memcmp_len_trunc")
524                .map_err(|e| CodeGenError::Builder(e.to_string()))?
525        } else if len_iv.get_type().get_bit_width() < 32 {
526            self.builder
527                .build_int_z_extend(len_iv, i32_ty, "memcmp_len_zext")
528                .map_err(|e| CodeGenError::Builder(e.to_string()))?
529        } else {
530            len_iv
531        };
532        let zero_i32 = i32_ty.const_zero();
533        let is_neg = self
534            .builder
535            .build_int_compare(
536                inkwell::IntPredicate::SLT,
537                len_i32,
538                zero_i32,
539                "memcmp_len_neg",
540            )
541            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
542        let len_nn = self
543            .builder
544            .build_select(is_neg, zero_i32, len_i32, "memcmp_len_nn")
545            .map_err(|e| CodeGenError::Builder(e.to_string()))?
546            .into_int_value();
547        let cap = self.compile_options.compare_cap;
548        let cap_const = i32_ty.const_int(cap as u64, false);
549        let gt = self
550            .builder
551            .build_int_compare(
552                inkwell::IntPredicate::UGT,
553                len_nn,
554                cap_const,
555                "memcmp_len_gt",
556            )
557            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
558        let sel_len = self
559            .builder
560            .build_select(gt, cap_const, len_nn, "memcmp_len_sel")
561            .map_err(|e| CodeGenError::Builder(e.to_string()))?
562            .into_int_value();
563
564        // Fast-path: if effective length is zero, return true without any reads
565        let len_is_zero = self
566            .builder
567            .build_int_compare(
568                inkwell::IntPredicate::EQ,
569                sel_len,
570                i32_ty.const_zero(),
571                "memcmp_len_is_zero",
572            )
573            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
574        let curr_block = self.builder.get_insert_block().unwrap();
575        let func = curr_block.get_parent().unwrap();
576        let zero_b = self.context.append_basic_block(func, "memcmp_len_zero");
577        let nz_b = self.context.append_basic_block(func, "memcmp_len_nz");
578        let cont_b = self.context.append_basic_block(func, "memcmp_len_cont");
579        self.builder
580            .build_conditional_branch(len_is_zero, zero_b, nz_b)
581            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
582
583        // Zero-length branch: true
584        self.builder.position_at_end(zero_b);
585        let bool_true = self.context.bool_type().const_int(1, false);
586        self.builder
587            .build_unconditional_branch(cont_b)
588            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
589        let zero_block = self.builder.get_insert_block().unwrap();
590
591        // Non-zero branch: perform reads and compare
592        self.builder.position_at_end(nz_b);
593
594        // Prepare static buffers of size CAP for both sides
595        let (arr_a_ty, buf_a) = self.get_or_create_i8_buffer(cap, "_gs_bi_memcmp_a");
596        let (arr_b_ty, buf_b) = self.get_or_create_i8_buffer(cap, "_gs_bi_memcmp_b");
597        let ptr_ty = self.context.ptr_type(AddressSpace::default());
598
599        // Helper: parse hex builtin into bytes
600        let parse_hex_bytes = |e: &Expr| -> Option<Vec<u8>> {
601            if let Expr::BuiltinCall { name, args } = e {
602                if name == "hex" && args.len() == 1 {
603                    if let Expr::String(s) = &args[0] {
604                        // Parser guarantees only hex digits and even length
605                        if s.is_empty() {
606                            return Some(Vec::new());
607                        }
608                        let mut out = Vec::with_capacity(s.len() / 2);
609                        let mut i = 0usize;
610                        while i + 1 < s.len() {
611                            let v = u8::from_str_radix(&s[i..i + 2], 16).ok()?;
612                            out.push(v);
613                            i += 2;
614                        }
615                        return Some(out);
616                    }
617                }
618            }
619            None
620        };
621
622        // Side A
623        // If side A is DWARF-backed (and not an explicit address-of), enforce pointer DWARF type
624        if parse_hex_bytes(a_expr).is_none() {
625            self.ensure_dwarf_pointer_arg(a_expr, "memcmp arg0")?;
626        }
627        let ok_a = if let Some(bytes) = parse_hex_bytes(a_expr) {
628            let i32_ty = self.context.i32_type();
629            let idx0 = i32_ty.const_zero();
630            for i in 0..(cap as usize) {
631                let idx_i = i32_ty.const_int(i as u64, false);
632                let pa = unsafe {
633                    self.builder
634                        .build_gep(arr_a_ty, buf_a, &[idx0, idx_i], &format!("hex_a_i{i}"))
635                        .map_err(|e| CodeGenError::Builder(e.to_string()))?
636                };
637                let byte = if i < bytes.len() { bytes[i] } else { 0 } as u64;
638                let bv = self.context.i8_type().const_int(byte, false);
639                self.builder
640                    .build_store(pa, bv)
641                    .map_err(|e| CodeGenError::Builder(e.to_string()))?;
642            }
643            self.context.bool_type().const_int(1, false)
644        } else {
645            // Resolve pointer for A and read from user memory
646            let ptr_a = self.resolve_ptr_i64_from_expr(a_expr)?;
647            let offsets_found_a = self.load_offsets_found_flag()?;
648            let dst_a = self
649                .builder
650                .build_bit_cast(buf_a, ptr_ty, "memcmp_dst_a")
651                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
652            let base_src_a = self
653                .builder
654                .build_int_to_ptr(ptr_a, ptr_ty, "memcmp_src_a")
655                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
656            let null_ptr = ptr_ty.const_null();
657            let src_a = self
658                .builder
659                .build_select::<BasicValueEnum<'ctx>, _>(
660                    offsets_found_a,
661                    base_src_a.into(),
662                    null_ptr.into(),
663                    "memcmp_src_a_or_null",
664                )
665                .map_err(|e| CodeGenError::Builder(e.to_string()))?
666                .into_pointer_value();
667            let zero_i32 = self.context.i32_type().const_zero();
668            let effective_len_a = self
669                .builder
670                .build_select::<BasicValueEnum<'ctx>, _>(
671                    offsets_found_a,
672                    sel_len.into(),
673                    zero_i32.into(),
674                    "memcmp_len_a_or_zero",
675                )
676                .map_err(|e| CodeGenError::Builder(e.to_string()))?
677                .into_int_value();
678            let ret_a = self
679                .create_bpf_helper_call(
680                    BPF_FUNC_probe_read_user as u64,
681                    &[dst_a, effective_len_a.into(), src_a.into()],
682                    self.context.i64_type().into(),
683                    "probe_read_user_memcmp_a",
684                )?
685                .into_int_value();
686            let i64_ty = self.context.i64_type();
687            let eq_a = self
688                .builder
689                .build_int_compare(
690                    inkwell::IntPredicate::EQ,
691                    ret_a,
692                    i64_ty.const_zero(),
693                    "memcmp_ok_a",
694                )
695                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
696            self.builder
697                .build_and(eq_a, offsets_found_a, "memcmp_ok_a")
698                .map_err(|e| CodeGenError::Builder(e.to_string()))?
699        };
700
701        // Side B
702        if parse_hex_bytes(b_expr).is_none() {
703            self.ensure_dwarf_pointer_arg(b_expr, "memcmp arg1")?;
704        }
705        let ok_b = if let Some(bytes) = parse_hex_bytes(b_expr) {
706            let i32_ty = self.context.i32_type();
707            let idx0 = i32_ty.const_zero();
708            for i in 0..(cap as usize) {
709                let idx_i = i32_ty.const_int(i as u64, false);
710                let pb = unsafe {
711                    self.builder
712                        .build_gep(arr_b_ty, buf_b, &[idx0, idx_i], &format!("hex_b_i{i}"))
713                        .map_err(|e| CodeGenError::Builder(e.to_string()))?
714                };
715                let byte = if i < bytes.len() { bytes[i] } else { 0 } as u64;
716                let bv = self.context.i8_type().const_int(byte, false);
717                self.builder
718                    .build_store(pb, bv)
719                    .map_err(|e| CodeGenError::Builder(e.to_string()))?;
720            }
721            self.context.bool_type().const_int(1, false)
722        } else {
723            // Resolve pointer for B and read from user memory
724            let ptr_b = self.resolve_ptr_i64_from_expr(b_expr)?;
725            let offsets_found_b = self.load_offsets_found_flag()?;
726            let dst_b = self
727                .builder
728                .build_bit_cast(buf_b, ptr_ty, "memcmp_dst_b")
729                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
730            let base_src_b = self
731                .builder
732                .build_int_to_ptr(ptr_b, ptr_ty, "memcmp_src_b")
733                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
734            let null_ptr = ptr_ty.const_null();
735            let src_b = self
736                .builder
737                .build_select::<BasicValueEnum<'ctx>, _>(
738                    offsets_found_b,
739                    base_src_b.into(),
740                    null_ptr.into(),
741                    "memcmp_src_b_or_null",
742                )
743                .map_err(|e| CodeGenError::Builder(e.to_string()))?
744                .into_pointer_value();
745            let zero_i32 = self.context.i32_type().const_zero();
746            let effective_len_b = self
747                .builder
748                .build_select::<BasicValueEnum<'ctx>, _>(
749                    offsets_found_b,
750                    sel_len.into(),
751                    zero_i32.into(),
752                    "memcmp_len_b_or_zero",
753                )
754                .map_err(|e| CodeGenError::Builder(e.to_string()))?
755                .into_int_value();
756            let ret_b = self
757                .create_bpf_helper_call(
758                    BPF_FUNC_probe_read_user as u64,
759                    &[dst_b, effective_len_b.into(), src_b.into()],
760                    self.context.i64_type().into(),
761                    "probe_read_user_memcmp_b",
762                )?
763                .into_int_value();
764            let i64_ty = self.context.i64_type();
765            let eq_b = self
766                .builder
767                .build_int_compare(
768                    inkwell::IntPredicate::EQ,
769                    ret_b,
770                    i64_ty.const_zero(),
771                    "memcmp_ok_b",
772                )
773                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
774            self.builder
775                .build_and(eq_b, offsets_found_b, "memcmp_ok_b")
776                .map_err(|e| CodeGenError::Builder(e.to_string()))?
777        };
778
779        let status_ok = self
780            .builder
781            .build_and(ok_a, ok_b, "memcmp_status_ok")
782            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
783
784        // If in condition context and either side failed, set condition error code = 1 (ProbeReadFailed)
785        if self.condition_context_active {
786            let not_a = self
787                .builder
788                .build_not(ok_a, "memcmp_fail_a")
789                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
790            let not_b = self
791                .builder
792                .build_not(ok_b, "memcmp_fail_b")
793                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
794            let any_fail = self
795                .builder
796                .build_or(not_a, not_b, "memcmp_any_fail")
797                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
798            let cur_block = self.builder.get_insert_block().unwrap();
799            let func = cur_block.get_parent().unwrap();
800            let set_b = self.context.append_basic_block(func, "memcmp_set_err");
801            let cont_b = self.context.append_basic_block(func, "memcmp_cont");
802            self.builder
803                .build_conditional_branch(any_fail, set_b, cont_b)
804                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
805            self.builder.position_at_end(set_b);
806            // Align error_code with VariableStatus::ReadError = 2
807            let _ = self.set_condition_error_if_unset(2u8);
808            // Decide which side failed (prefer recording the actual failing side)
809            let not_a_val = self
810                .builder
811                .build_not(ok_a, "memcmp_fail_a_val")
812                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
813            let not_b_val = self
814                .builder
815                .build_not(ok_b, "memcmp_fail_b_val")
816                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
817            let cur_fn = self
818                .builder
819                .get_insert_block()
820                .unwrap()
821                .get_parent()
822                .unwrap();
823            let set_a_bb = self.context.append_basic_block(cur_fn, "set_addr_a");
824            let check_b_bb = self.context.append_basic_block(cur_fn, "check_fail_b");
825            let set_b_bb = self.context.append_basic_block(cur_fn, "set_addr_b");
826            let after_set_bb = self.context.append_basic_block(cur_fn, "after_set_addr");
827
828            // Branch on A failure first
829            self.builder
830                .build_conditional_branch(not_a_val, set_a_bb, check_b_bb)
831                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
832
833            // set A address
834            self.builder.position_at_end(set_a_bb);
835            if let Some(pa) = match parse_hex_bytes(a_expr) {
836                Some(_) => None,
837                None => Some(self.resolve_ptr_i64_from_expr(a_expr)?),
838            } {
839                let _ = self.set_condition_error_addr_if_unset(pa);
840            }
841            self.builder
842                .build_unconditional_branch(after_set_bb)
843                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
844
845            // check B failure and set B
846            self.builder.position_at_end(check_b_bb);
847            self.builder
848                .build_conditional_branch(not_b_val, set_b_bb, after_set_bb)
849                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
850            self.builder.position_at_end(set_b_bb);
851            if let Some(pb) = match parse_hex_bytes(b_expr) {
852                Some(_) => None,
853                None => Some(self.resolve_ptr_i64_from_expr(b_expr)?),
854            } {
855                let _ = self.set_condition_error_addr_if_unset(pb);
856            }
857            self.builder
858                .build_unconditional_branch(after_set_bb)
859                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
860            self.builder.position_at_end(after_set_bb);
861            // Build flags: bit0=A fail, bit1=B fail, bit2=len clamped, bit3=len<=0
862            let i8t = self.context.i8_type();
863            let b_a = self
864                .builder
865                .build_int_z_extend(
866                    self.builder
867                        .build_not(ok_a, "fa")
868                        .map_err(|e| CodeGenError::Builder(e.to_string()))?,
869                    i8t,
870                    "fa8",
871                )
872                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
873            let b_b1 = self
874                .builder
875                .build_int_z_extend(
876                    self.builder
877                        .build_not(ok_b, "fb")
878                        .map_err(|e| CodeGenError::Builder(e.to_string()))?,
879                    i8t,
880                    "fb8",
881                )
882                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
883            let sh1 = self
884                .builder
885                .build_left_shift(b_b1, i8t.const_int(1, false), "b_b_shift")
886                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
887            // gt: len_nn > cap  (len clamped)
888            let b_c = self
889                .builder
890                .build_int_z_extend(gt, i8t, "clamped8")
891                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
892            let sh2 = self
893                .builder
894                .build_left_shift(b_c, i8t.const_int(2, false), "b_c_shift")
895                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
896            // len<=0: reuse len_is_zero
897            let b_z = self
898                .builder
899                .build_int_z_extend(len_is_zero, i8t, "len0_8")
900                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
901            let sh3 = self
902                .builder
903                .build_left_shift(b_z, i8t.const_int(3, false), "b_z_shift")
904                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
905            let f01 = self
906                .builder
907                .build_or(b_a, sh1, "f01")
908                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
909            let f012 = self
910                .builder
911                .build_or(f01, sh2, "f012")
912                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
913            let flags = self
914                .builder
915                .build_or(f012, sh3, "flags")
916                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
917            let _ = self.or_condition_error_flags(flags);
918            self.builder
919                .build_unconditional_branch(cont_b)
920                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
921            self.builder.position_at_end(cont_b);
922        }
923
924        // Aggregate XOR/OR across 0..CAP, masked by (i < sel_len)
925        let i32_ty = self.context.i32_type();
926        let idx0 = i32_ty.const_zero();
927        let mut acc = self.context.i8_type().const_zero();
928        for i in 0..cap as usize {
929            let idx_i = i32_ty.const_int(i as u64, false);
930            // active = (i < sel_len)
931            let active = self
932                .builder
933                .build_int_compare(
934                    inkwell::IntPredicate::ULT,
935                    idx_i,
936                    sel_len,
937                    &format!("memcmp_i{i}_active"),
938                )
939                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
940            // a[i]
941            let pa = unsafe {
942                self.builder
943                    .build_gep(arr_a_ty, buf_a, &[idx0, idx_i], &format!("memcmp_a_i{i}"))
944                    .map_err(|e| CodeGenError::Builder(e.to_string()))?
945            };
946            let va = self
947                .builder
948                .build_load(self.context.i8_type(), pa, &format!("ld_a_{i}"))
949                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
950            let va = match va {
951                BasicValueEnum::IntValue(iv) => iv,
952                _ => return Err(CodeGenError::LLVMError("memcmp load a != i8".into())),
953            };
954            // b[i]
955            let pb = unsafe {
956                self.builder
957                    .build_gep(arr_b_ty, buf_b, &[idx0, idx_i], &format!("memcmp_b_i{i}"))
958                    .map_err(|e| CodeGenError::Builder(e.to_string()))?
959            };
960            let vb = self
961                .builder
962                .build_load(self.context.i8_type(), pb, &format!("ld_b_{i}"))
963                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
964            let vb = match vb {
965                BasicValueEnum::IntValue(iv) => iv,
966                _ => return Err(CodeGenError::LLVMError("memcmp load b != i8".into())),
967            };
968            let diff = self
969                .builder
970                .build_xor(va, vb, &format!("memcmp_diff_{i}"))
971                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
972            let zero8 = self.context.i8_type().const_zero();
973            let masked = self
974                .builder
975                .build_select(active, diff, zero8, &format!("memcmp_masked_{i}"))
976                .map_err(|e| CodeGenError::Builder(e.to_string()))?
977                .into_int_value();
978            acc = self
979                .builder
980                .build_or(acc, masked, &format!("memcmp_acc_{i}"))
981                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
982        }
983        let eq_bytes = self
984            .builder
985            .build_int_compare(
986                inkwell::IntPredicate::EQ,
987                acc,
988                self.context.i8_type().const_zero(),
989                "memcmp_acc_zero",
990            )
991            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
992        let nz_result = self
993            .builder
994            .build_and(status_ok, eq_bytes, "memcmp_and")
995            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
996
997        self.builder
998            .build_unconditional_branch(cont_b)
999            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1000        let nz_block = self.builder.get_insert_block().unwrap();
1001
1002        // Merge
1003        self.builder.position_at_end(cont_b);
1004        let phi = self
1005            .builder
1006            .build_phi(self.context.bool_type(), "memcmp_phi")
1007            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1008        phi.add_incoming(&[(&bool_true, zero_block), (&nz_result, nz_block)]);
1009        Ok(phi.as_basic_value())
1010    }
1011    /// Builtin strncmp/starts_with implementation: bounded byte-compare without NUL requirement.
1012    fn compile_strncmp_builtin(
1013        &mut self,
1014        dwarf_expr: &Expr,
1015        lit: &str,
1016        n: u32,
1017    ) -> Result<BasicValueEnum<'ctx>> {
1018        // Fast path: if the first argument is a script string variable or a string literal,
1019        // perform a compile-time bounded comparison and return a constant boolean.
1020        let immediate_bytes_opt = match dwarf_expr {
1021            Expr::Variable(name) => {
1022                if self
1023                    .get_variable_type(name)
1024                    .is_some_and(|t| matches!(t, crate::script::VarType::String))
1025                {
1026                    self.get_string_variable_bytes(name).cloned()
1027                } else {
1028                    None
1029                }
1030            }
1031            Expr::String(s) => {
1032                let mut b = s.as_bytes().to_vec();
1033                b.push(0);
1034                Some(b)
1035            }
1036            _ => None,
1037        };
1038
1039        if let Some(bytes) = immediate_bytes_opt {
1040            // Treat as bounded byte compare between two immediate strings
1041            let lit_bytes = lit.as_bytes();
1042            let cap = self.compile_options.compare_cap as usize;
1043            let n_usize = std::cmp::min(n as usize, cap);
1044            // compute source content length up to NUL
1045            let content_len = bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len());
1046            let cmp_len = std::cmp::min(n_usize, std::cmp::min(content_len, lit_bytes.len()));
1047            let equal =
1048                bytes.get(0..cmp_len).unwrap_or(&[]) == lit_bytes.get(0..cmp_len).unwrap_or(&[]);
1049            let bool_val = self
1050                .context
1051                .bool_type()
1052                .const_int(if equal { 1 } else { 0 }, false);
1053            return Ok(bool_val.into());
1054        }
1055
1056        // Determine pointer value (i64) of the target memory (DWARF or alias)
1057        // Prefer DWARF resolution for richer status/hints; fallback to generic pointer resolver.
1058        let ptr_i64 = match self.query_dwarf_for_complex_expr(dwarf_expr)? {
1059            Some(var) => {
1060                if let Some(mut ty) = var.dwarf_type.as_ref() {
1061                    loop {
1062                        match ty {
1063                            DwarfType::TypedefType {
1064                                underlying_type, ..
1065                            } => ty = underlying_type.as_ref(),
1066                            DwarfType::QualifiedType {
1067                                underlying_type, ..
1068                            } => ty = underlying_type.as_ref(),
1069                            _ => break,
1070                        }
1071                    }
1072                    match ty {
1073                        DwarfType::PointerType { .. } => {
1074                            let val_any = self.evaluate_result_to_llvm_value(
1075                                &var.evaluation_result,
1076                                ty,
1077                                &var.name,
1078                                self.get_compile_time_context()?.pc_address,
1079                                None,
1080                            )?;
1081                            match val_any {
1082                                BasicValueEnum::IntValue(iv) => iv,
1083                                BasicValueEnum::PointerValue(pv) => self
1084                                    .builder
1085                                    .build_ptr_to_int(pv, self.context.i64_type(), "ptr_as_i64")
1086                                    .map_err(|e| CodeGenError::Builder(e.to_string()))?,
1087                                _ => {
1088                                    return Err(CodeGenError::TypeError(
1089                                        "strncmp requires pointer/integer value for pointer; got unsupported DWARF value".into(),
1090                                    ))
1091                                }
1092                            }
1093                        }
1094                        DwarfType::ArrayType { .. } => {
1095                            let module_hint = self.current_resolved_var_module_path.clone();
1096                            let status_ptr = if self.condition_context_active {
1097                                Some(self.get_or_create_cond_error_global())
1098                            } else {
1099                                None
1100                            };
1101                            self.evaluation_result_to_address_with_hint(
1102                                &var.evaluation_result,
1103                                status_ptr,
1104                                module_hint.as_deref(),
1105                            )?
1106                        }
1107                        _ => {
1108                            // Not a pointer/array -> treat as error
1109                            return Err(CodeGenError::TypeError(
1110                                "strncmp requires the non-string side to be an address expression (pointer/array)".into(),
1111                            ));
1112                        }
1113                    }
1114                } else {
1115                    return Err(CodeGenError::TypeError(
1116                        "strncmp non-string side lacks DWARF type info".into(),
1117                    ));
1118                }
1119            }
1120            None => {
1121                // Generic pointer expr (e.g., alias); resolve to i64
1122                self.resolve_ptr_i64_from_expr(dwarf_expr).map_err(|_| {
1123                    CodeGenError::TypeError(
1124                        "strncmp requires at least one string argument, and the other side must be an address expression (DWARF pointer/array or alias)".to_string(),
1125                    )
1126                })?
1127            }
1128        };
1129
1130        // Cap read length for safety
1131        let cap = self.compile_options.compare_cap;
1132        let max_n = std::cmp::min(n, cap);
1133        let lit_len = std::cmp::min(lit.len() as u32, cap);
1134        let cmp_len = std::cmp::min(max_n, lit_len);
1135
1136        // Read bytes into buffer
1137        let (buf_global, status, arr_ty) =
1138            self.read_user_bytes_into_buffer(ptr_i64, cmp_len, "_gs_bi_strncmp")?;
1139        let status_ok = self
1140            .builder
1141            .build_int_compare(
1142                inkwell::IntPredicate::EQ,
1143                status,
1144                self.context.i64_type().const_zero(),
1145                "rd_ok",
1146            )
1147            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1148
1149        // If in condition context and read failed, set condition error code = 1 (ProbeReadFailed)
1150        if self.condition_context_active {
1151            let cur_block = self.builder.get_insert_block().unwrap();
1152            let func = cur_block.get_parent().unwrap();
1153            let set_b = self.context.append_basic_block(func, "strncmp_set_err");
1154            let cont_b = self.context.append_basic_block(func, "strncmp_cont");
1155            let not_ok = self
1156                .builder
1157                .build_not(status_ok, "rd_fail")
1158                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1159            self.builder
1160                .build_conditional_branch(not_ok, set_b, cont_b)
1161                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1162            self.builder.position_at_end(set_b);
1163            // VariableStatus::ReadError = 2
1164            let _ = self.set_condition_error_if_unset(2u8);
1165            let _ = self.set_condition_error_addr_if_unset(ptr_i64);
1166            // flags: bit0 = read failure for strncmp
1167            let one = self.context.i8_type().const_int(1, false);
1168            let _ = self.or_condition_error_flags(one);
1169            self.builder
1170                .build_unconditional_branch(cont_b)
1171                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1172            self.builder.position_at_end(cont_b);
1173        }
1174
1175        // XOR/OR accumulation over cmp_len bytes
1176        let i32_ty = self.context.i32_type();
1177        let idx0 = i32_ty.const_zero();
1178        let mut acc = self.context.i8_type().const_zero();
1179        for (i, b) in lit.as_bytes().iter().take(cmp_len as usize).enumerate() {
1180            let idx_i = i32_ty.const_int(i as u64, false);
1181            let ptr_i = unsafe {
1182                self.builder
1183                    .build_gep(arr_ty, buf_global, &[idx0, idx_i], "ch_ptr")
1184                    .map_err(|e| CodeGenError::Builder(e.to_string()))?
1185            };
1186            let ch = self
1187                .builder
1188                .build_load(self.context.i8_type(), ptr_i, "ch")
1189                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1190            let ch = match ch {
1191                BasicValueEnum::IntValue(iv) => iv,
1192                _ => return Err(CodeGenError::LLVMError("load did not return i8".into())),
1193            };
1194            let expect = self.context.i8_type().const_int(*b as u64, false);
1195            let diff = self
1196                .builder
1197                .build_xor(ch, expect, "diff")
1198                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1199            acc = self
1200                .builder
1201                .build_or(acc, diff, "acc_or")
1202                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1203        }
1204        let eq_bytes = self
1205            .builder
1206            .build_int_compare(
1207                inkwell::IntPredicate::EQ,
1208                acc,
1209                self.context.i8_type().const_zero(),
1210                "acc_zero",
1211            )
1212            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1213
1214        let result = self
1215            .builder
1216            .build_and(status_ok, eq_bytes, "strncmp_and")
1217            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1218        Ok(result.into())
1219    }
1220    /// Compile an expression
1221    pub fn compile_expr(&mut self, expr: &Expr) -> Result<BasicValueEnum<'ctx>> {
1222        match expr {
1223            Expr::Int(value) => {
1224                // Treat script integer literals as signed i64 constants
1225                let int_value = self.context.i64_type().const_int(*value as u64, true);
1226                debug!(
1227                    "compile_expr: Int literal {} compiled to IntValue with bit width {}",
1228                    value,
1229                    int_value.get_type().get_bit_width()
1230                );
1231                Ok(int_value.into())
1232            }
1233            Expr::Float(_value) => Err(CodeGenError::TypeError(
1234                "Floating point expressions are not supported".to_string(),
1235            )),
1236            Expr::String(value) => {
1237                // Create string constant using a simpler approach
1238                let string_value = self.context.const_string(value.as_bytes(), true);
1239                let global = self
1240                    .module
1241                    .add_global(string_value.get_type(), None, "str_const");
1242                global.set_initializer(&string_value);
1243
1244                let ptr_type = self.context.ptr_type(AddressSpace::default());
1245                let cast_ptr = self
1246                    .builder
1247                    .build_bit_cast(global.as_pointer_value(), ptr_type, "str_ptr")
1248                    .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1249                Ok(cast_ptr)
1250            }
1251            Expr::Bool(value) => {
1252                // Represent booleans as i1 for logical/compare consistency
1253                let b = self
1254                    .context
1255                    .bool_type()
1256                    .const_int(if *value { 1 } else { 0 }, false);
1257                Ok(b.into())
1258            }
1259            Expr::UnaryNot(inner) => {
1260                // Compile operand to integer and compare EQ to zero to produce boolean not
1261                let v = self.compile_expr(inner)?;
1262                let iv = match v {
1263                    BasicValueEnum::IntValue(iv) => iv,
1264                    _ => {
1265                        return Err(CodeGenError::TypeError(
1266                            "Logical NOT requires integer/boolean operand".to_string(),
1267                        ))
1268                    }
1269                };
1270                let zero = iv.get_type().const_zero();
1271                let res = self
1272                    .builder
1273                    .build_int_compare(inkwell::IntPredicate::EQ, iv, zero, "not_eq0")
1274                    .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1275                Ok(res.into())
1276            }
1277            Expr::Variable(var_name) => {
1278                debug!("compile_expr: Compiling variable expression: {}", var_name);
1279
1280                // First: DWARF alias variable takes precedence
1281                if self.alias_variable_exists(var_name) {
1282                    debug!(
1283                        "compile_expr: '{}' is an alias variable; resolving to runtime address",
1284                        var_name
1285                    );
1286                    let aliased = self
1287                        .get_alias_variable(var_name)
1288                        .expect("alias existence just checked");
1289                    // Resolve to i64 address then cast to ptr
1290                    let addr_i64 = self.resolve_ptr_i64_from_expr(&aliased)?;
1291                    let ptr_ty = self.context.ptr_type(AddressSpace::default());
1292                    let as_ptr = self
1293                        .builder
1294                        .build_int_to_ptr(addr_i64, ptr_ty, "alias_as_ptr")
1295                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1296                    return Ok(as_ptr.into());
1297                }
1298
1299                // Then check if it's a concrete script-defined variable
1300                if self.variable_exists(var_name) {
1301                    debug!("compile_expr: Found script variable: {}", var_name);
1302                    let loaded_value = self.load_variable(var_name)?;
1303                    debug!(
1304                        "compile_expr: Loaded variable '{}' with type: {:?}",
1305                        var_name,
1306                        loaded_value.get_type()
1307                    );
1308                    match &loaded_value {
1309                        BasicValueEnum::IntValue(iv) => debug!(
1310                            "compile_expr: Variable '{}' is IntValue with bit width {}",
1311                            var_name,
1312                            iv.get_type().get_bit_width()
1313                        ),
1314                        BasicValueEnum::FloatValue(_) => {
1315                            debug!("compile_expr: Variable '{}' is FloatValue", var_name)
1316                        }
1317                        BasicValueEnum::PointerValue(_) => {
1318                            debug!("compile_expr: Variable '{}' is PointerValue", var_name)
1319                        }
1320                        _ => debug!("compile_expr: Variable '{}' is other type", var_name),
1321                    }
1322                    return Ok(loaded_value);
1323                }
1324
1325                // If not found in script variables nor alias map, try DWARF variables
1326                debug!(
1327                    "Variable '{}' not found in script variables, checking DWARF",
1328                    var_name
1329                );
1330                // If not a DWARF variable either, treat as out-of-scope script name for friendliness
1331                match self.query_dwarf_for_variable(var_name) {
1332                    Ok(Some(_)) => self.compile_dwarf_expression(expr),
1333                    Ok(None) => Err(CodeGenError::VariableNotInScope(var_name.clone())),
1334                    Err(e) => Err(CodeGenError::DwarfError(e.to_string())),
1335                }
1336            }
1337            Expr::SpecialVar(name) => {
1338                // Accept both "$pid" and "pid" forms from the parser
1339                let sanitized = name.trim_start_matches('$');
1340                self.handle_special_variable(sanitized)
1341            }
1342            Expr::BuiltinCall { name, args } => match name.as_str() {
1343                "memcmp" => {
1344                    if args.len() != 3 {
1345                        return Err(CodeGenError::TypeError("memcmp expects 3 arguments".into()));
1346                    }
1347                    self.compile_memcmp_builtin(&args[0], &args[1], &args[2])
1348                }
1349                "strncmp" => {
1350                    if args.len() != 3 {
1351                        return Err(CodeGenError::TypeError(
1352                            "strncmp expects 3 arguments".into(),
1353                        ));
1354                    }
1355                    let n = match &args[2] {
1356                        Expr::Int(v) if *v >= 0 => *v as u32,
1357                        _ => {
1358                            return Err(CodeGenError::TypeError(
1359                                "strncmp length must be a non-negative integer literal".into(),
1360                            ))
1361                        }
1362                    };
1363                    // Accept string on either side: string literal or script string variable
1364                    fn extract_script_string(
1365                        this: &mut EbpfContext<'_, '_>,
1366                        e: &Expr,
1367                    ) -> Option<String> {
1368                        match e {
1369                            Expr::String(s) => Some(s.clone()),
1370                            Expr::Variable(name) => this
1371                                .get_variable_type(name)
1372                                .is_some_and(|t| matches!(t, crate::script::VarType::String))
1373                                .then(|| {
1374                                    this.get_string_variable_bytes(name).map(|b| {
1375                                        let cut = b.iter().position(|&x| x == 0).unwrap_or(b.len());
1376                                        String::from_utf8_lossy(&b[..cut]).to_string()
1377                                    })
1378                                })
1379                                .flatten(),
1380                            _ => None,
1381                        }
1382                    }
1383                    let left_str = extract_script_string(self, &args[0]);
1384                    let right_str = extract_script_string(self, &args[1]);
1385                    match (left_str, right_str) {
1386                        (Some(ls), Some(rs)) => {
1387                            // Both sides strings -> compile-time fold
1388                            let ln = n as usize;
1389                            let eq = ls.as_bytes().iter().take(ln).eq(rs.as_bytes().iter().take(ln));
1390                            let bv = self.context.bool_type().const_int(eq as u64, false);
1391                            Ok(bv.into())
1392                        }
1393                        (Some(ls), None) => self.compile_strncmp_builtin(&args[1], &ls, n),
1394                        (None, Some(rs)) => self.compile_strncmp_builtin(&args[0], &rs, n),
1395                        (None, None) => Err(CodeGenError::TypeError(
1396                            "strncmp requires at least one string argument (string literal or script string variable) as the first or second parameter".into(),
1397                        )),
1398                    }
1399                }
1400                "starts_with" => {
1401                    if args.len() != 2 {
1402                        return Err(CodeGenError::TypeError(
1403                            "starts_with expects 2 arguments".into(),
1404                        ));
1405                    }
1406                    // Accept string on either side (literal or script string var)
1407                    fn extract_script_string(
1408                        this: &mut EbpfContext<'_, '_>,
1409                        e: &Expr,
1410                    ) -> Option<String> {
1411                        match e {
1412                            Expr::String(s) => Some(s.clone()),
1413                            Expr::Variable(name) => this
1414                                .get_variable_type(name)
1415                                .is_some_and(|t| matches!(t, crate::script::VarType::String))
1416                                .then(|| {
1417                                    this.get_string_variable_bytes(name).map(|b| {
1418                                        let cut = b.iter().position(|&x| x == 0).unwrap_or(b.len());
1419                                        String::from_utf8_lossy(&b[..cut]).to_string()
1420                                    })
1421                                })
1422                                .flatten(),
1423                            _ => None,
1424                        }
1425                    }
1426                    let s0 = extract_script_string(self, &args[0]);
1427                    let s1 = extract_script_string(self, &args[1]);
1428                    match (s0, s1) {
1429                        (Some(a), Some(b)) => {
1430                            // both strings -> compile-time fold
1431                            let ok = a.as_bytes().starts_with(b.as_bytes());
1432                            let bv = self.context.bool_type().const_int(ok as u64, false);
1433                            Ok(bv.into())
1434                        }
1435                        (Some(a), None) => self.compile_strncmp_builtin(&args[1], &a, a.len() as u32),
1436                        (None, Some(b)) => self.compile_strncmp_builtin(&args[0], &b, b.len() as u32),
1437                        (None, None) => Err(CodeGenError::TypeError(
1438                            "starts_with requires at least one string argument (string literal or script string variable) as the first or second parameter".into(),
1439                        )),
1440                    }
1441                }
1442                _ => Err(CodeGenError::NotImplemented(format!(
1443                    "Unknown builtin function: {name}"
1444                ))),
1445            },
1446            Expr::BinaryOp { left, op, right } => {
1447                // Guard: disallow arithmetic/ordered comparisons that involve DWARF aggregates
1448                let is_arith = matches!(
1449                    op,
1450                    BinaryOp::Add | BinaryOp::Subtract | BinaryOp::Multiply | BinaryOp::Divide
1451                );
1452                let is_ordered = matches!(
1453                    op,
1454                    BinaryOp::LessThan
1455                        | BinaryOp::LessEqual
1456                        | BinaryOp::GreaterThan
1457                        | BinaryOp::GreaterEqual
1458                );
1459                if (is_arith || is_ordered)
1460                    && (self.is_dwarf_aggregate_expr(left) || self.is_dwarf_aggregate_expr(right))
1461                {
1462                    return Err(CodeGenError::TypeError(
1463                        "Unsupported arithmetic/ordered comparison involving struct/union/array. Select a scalar field (e.g., 'obj.field'), or use '&expr + <non-negative literal>' in an alias/address context if you need a raw address."
1464                            .to_string(),
1465                    ));
1466                }
1467
1468                // Guard: disallow ordered comparisons on pointers/addresses; only ==/!= are allowed
1469                if is_ordered
1470                    && (self.is_pointer_like_expr(left) || self.is_pointer_like_expr(right))
1471                {
1472                    return Err(CodeGenError::TypeError(
1473                        "Pointer ordered comparison ('<', '<=', '>', '>=') is not supported. Use '==' or '!=' to compare addresses. If you need to adjust an address, use '&expr + <non-negative literal>' in an alias/address context; to compare values, select a scalar field (e.g., 'obj.field')."
1474                            .to_string(),
1475                    ));
1476                }
1477                // String comparison fast-path: script string vs DWARF char*/char[N]
1478                if matches!(op, BinaryOp::Equal | BinaryOp::NotEqual) {
1479                    if let (Expr::String(lit), other) = (&**left, &**right) {
1480                        return self.compile_string_comparison(
1481                            other,
1482                            lit,
1483                            matches!(op, BinaryOp::Equal),
1484                        );
1485                    } else if let (other, Expr::String(lit)) = (&**left, &**right) {
1486                        return self.compile_string_comparison(
1487                            other,
1488                            lit,
1489                            matches!(op, BinaryOp::Equal),
1490                        );
1491                    }
1492                }
1493                // Implement short-circuit for logical OR (||) and logical AND (&&)
1494                if matches!(op, BinaryOp::LogicalOr) {
1495                    // Evaluate LHS to boolean (non-zero => true). Accept integer or pointer.
1496                    let lhs_val = self.compile_expr(left)?;
1497                    let lhs_int = match lhs_val {
1498                        BasicValueEnum::IntValue(iv) => iv,
1499                        BasicValueEnum::PointerValue(pv) => self
1500                            .builder
1501                            .build_ptr_to_int(pv, self.context.i64_type(), "lor_lhs_ptr_as_i64")
1502                            .map_err(|e| CodeGenError::Builder(e.to_string()))?,
1503                        _ => {
1504                            return Err(CodeGenError::TypeError(
1505                                "Logical OR requires integer or pointer operands".to_string(),
1506                            ))
1507                        }
1508                    };
1509                    let lhs_zero = lhs_int.get_type().const_zero();
1510                    let lhs_bool = self
1511                        .builder
1512                        .build_int_compare(
1513                            inkwell::IntPredicate::NE,
1514                            lhs_int,
1515                            lhs_zero,
1516                            "lor_lhs_nz",
1517                        )
1518                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1519
1520                    // Prepare control flow blocks
1521                    let curr_block = self.builder.get_insert_block().ok_or_else(|| {
1522                        CodeGenError::LLVMError("No current basic block".to_string())
1523                    })?;
1524                    let func = curr_block
1525                        .get_parent()
1526                        .ok_or_else(|| CodeGenError::LLVMError("No parent function".to_string()))?;
1527                    let rhs_block = self.context.append_basic_block(func, "lor_rhs");
1528                    let merge_block = self.context.append_basic_block(func, "lor_merge");
1529
1530                    // If lhs is true, jump directly to merge (short-circuit)
1531                    self.builder
1532                        .build_conditional_branch(lhs_bool, merge_block, rhs_block)
1533                        .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1534
1535                    // RHS path: compute boolean only if needed
1536                    self.builder.position_at_end(rhs_block);
1537                    let rhs_val = self.compile_expr(right)?;
1538                    let rhs_int = match rhs_val {
1539                        BasicValueEnum::IntValue(iv) => iv,
1540                        BasicValueEnum::PointerValue(pv) => self
1541                            .builder
1542                            .build_ptr_to_int(pv, self.context.i64_type(), "lor_rhs_ptr_as_i64")
1543                            .map_err(|e| CodeGenError::Builder(e.to_string()))?,
1544                        _ => {
1545                            return Err(CodeGenError::TypeError(
1546                                "Logical OR requires integer or pointer operands".to_string(),
1547                            ))
1548                        }
1549                    };
1550                    let rhs_zero = rhs_int.get_type().const_zero();
1551                    let rhs_bool = self
1552                        .builder
1553                        .build_int_compare(
1554                            inkwell::IntPredicate::NE,
1555                            rhs_int,
1556                            rhs_zero,
1557                            "lor_rhs_nz",
1558                        )
1559                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1560                    // Capture the actual block where RHS computation ended
1561                    let rhs_end_block = self.builder.get_insert_block().ok_or_else(|| {
1562                        CodeGenError::LLVMError("No current basic block after RHS".to_string())
1563                    })?;
1564                    self.builder
1565                        .build_unconditional_branch(merge_block)
1566                        .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1567
1568                    // Merge: phi of i1: true from LHS-true, RHS bool from rhs_block
1569                    self.builder.position_at_end(merge_block);
1570                    let i1 = self.context.bool_type();
1571                    let phi = self
1572                        .builder
1573                        .build_phi(i1, "lor_phi")
1574                        .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1575                    let one = i1.const_int(1, false);
1576                    phi.add_incoming(&[(&one, curr_block), (&rhs_bool, rhs_end_block)]);
1577                    return Ok(phi.as_basic_value());
1578                } else if matches!(op, BinaryOp::LogicalAnd) {
1579                    // Evaluate LHS to boolean (non-zero => true). Accept integer or pointer.
1580                    let lhs_val = self.compile_expr(left)?;
1581                    let lhs_int = match lhs_val {
1582                        BasicValueEnum::IntValue(iv) => iv,
1583                        BasicValueEnum::PointerValue(pv) => self
1584                            .builder
1585                            .build_ptr_to_int(pv, self.context.i64_type(), "land_lhs_ptr_as_i64")
1586                            .map_err(|e| CodeGenError::Builder(e.to_string()))?,
1587                        _ => {
1588                            return Err(CodeGenError::TypeError(
1589                                "Logical AND requires integer or pointer operands".to_string(),
1590                            ))
1591                        }
1592                    };
1593                    let lhs_zero = lhs_int.get_type().const_zero();
1594                    let lhs_bool = self
1595                        .builder
1596                        .build_int_compare(
1597                            inkwell::IntPredicate::NE,
1598                            lhs_int,
1599                            lhs_zero,
1600                            "land_lhs_nz",
1601                        )
1602                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1603
1604                    // Prepare control flow: if lhs is true, evaluate rhs; else short-circuit to false
1605                    let curr_block = self.builder.get_insert_block().ok_or_else(|| {
1606                        CodeGenError::LLVMError("No current basic block".to_string())
1607                    })?;
1608                    let func = curr_block
1609                        .get_parent()
1610                        .ok_or_else(|| CodeGenError::LLVMError("No parent function".to_string()))?;
1611                    let rhs_block = self.context.append_basic_block(func, "land_rhs");
1612                    let merge_block = self.context.append_basic_block(func, "land_merge");
1613
1614                    // If lhs is true, go compute rhs; else jump to merge with false
1615                    self.builder
1616                        .build_conditional_branch(lhs_bool, rhs_block, merge_block)
1617                        .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1618
1619                    // RHS path
1620                    self.builder.position_at_end(rhs_block);
1621                    let rhs_val = self.compile_expr(right)?;
1622                    let rhs_int = match rhs_val {
1623                        BasicValueEnum::IntValue(iv) => iv,
1624                        BasicValueEnum::PointerValue(pv) => self
1625                            .builder
1626                            .build_ptr_to_int(pv, self.context.i64_type(), "land_rhs_ptr_as_i64")
1627                            .map_err(|e| CodeGenError::Builder(e.to_string()))?,
1628                        _ => {
1629                            return Err(CodeGenError::TypeError(
1630                                "Logical AND requires integer or pointer operands".to_string(),
1631                            ))
1632                        }
1633                    };
1634                    let rhs_zero = rhs_int.get_type().const_zero();
1635                    let rhs_bool = self
1636                        .builder
1637                        .build_int_compare(
1638                            inkwell::IntPredicate::NE,
1639                            rhs_int,
1640                            rhs_zero,
1641                            "land_rhs_nz",
1642                        )
1643                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1644                    let rhs_end_block = self.builder.get_insert_block().ok_or_else(|| {
1645                        CodeGenError::LLVMError("No current basic block after RHS".to_string())
1646                    })?;
1647                    self.builder
1648                        .build_unconditional_branch(merge_block)
1649                        .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1650
1651                    // Merge: phi(i1) with false from LHS=false path, RHS bool from rhs path
1652                    self.builder.position_at_end(merge_block);
1653                    let i1 = self.context.bool_type();
1654                    let phi = self
1655                        .builder
1656                        .build_phi(i1, "land_phi")
1657                        .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
1658                    let zero = i1.const_zero();
1659                    phi.add_incoming(&[(&rhs_bool, rhs_end_block), (&zero, curr_block)]);
1660                    return Ok(phi.as_basic_value());
1661                }
1662
1663                // Default eager evaluation for other binary ops
1664                let left_val = self.compile_expr(left)?;
1665                let right_val = self.compile_expr(right)?;
1666                self.compile_binary_op(left_val, op.clone(), right_val)
1667            }
1668            Expr::MemberAccess(_, _) => {
1669                // Use unified DWARF expression compilation
1670                self.compile_dwarf_expression(expr)
1671            }
1672            Expr::PointerDeref(_) => {
1673                // Use unified DWARF expression compilation
1674                self.compile_dwarf_expression(expr)
1675            }
1676            Expr::AddressOf(inner) => {
1677                // Address-of with ASLR-aware hint: compute runtime address using module hint
1678                // Transparently support alias variables: &alias -> address of aliased DWARF expression
1679                let target_inner: &Expr = if let Expr::Variable(var_name) = inner.as_ref() {
1680                    if self.alias_variable_exists(var_name) {
1681                        // Use the aliased target expression (by-value) and query DWARF on it
1682                        let aliased = self
1683                            .get_alias_variable(var_name)
1684                            .expect("alias existence just checked");
1685                        // First perform the DWARF query so that current_resolved_var_module_path
1686                        // is set for the aliased symbol's module; then capture the hint.
1687                        let var =
1688                            self.query_dwarf_for_complex_expr(&aliased)?
1689                                .ok_or_else(|| {
1690                                    super::context::CodeGenError::TypeError(
1691                                        "cannot take address of unresolved expression".to_string(),
1692                                    )
1693                                })?;
1694                        let module_hint = self.current_resolved_var_module_path.clone();
1695                        match self.evaluation_result_to_address_with_hint(
1696                            &var.evaluation_result,
1697                            None,
1698                            module_hint.as_deref(),
1699                        ) {
1700                            Ok(addr_i64) => {
1701                                let ptr_ty = self.context.ptr_type(AddressSpace::default());
1702                                let as_ptr = self
1703                                    .builder
1704                                    .build_int_to_ptr(addr_i64, ptr_ty, "addr_as_ptr")
1705                                    .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1706                                return Ok(as_ptr.into());
1707                            }
1708                            Err(_) => {
1709                                return Err(super::context::CodeGenError::TypeError(
1710                                    "cannot take address of rvalue".to_string(),
1711                                ));
1712                            }
1713                        }
1714                    } else {
1715                        inner.as_ref()
1716                    }
1717                } else {
1718                    inner.as_ref()
1719                };
1720
1721                let var = self
1722                    .query_dwarf_for_complex_expr(target_inner)?
1723                    .ok_or_else(|| {
1724                        super::context::CodeGenError::TypeError(
1725                            "cannot take address of unresolved expression".to_string(),
1726                        )
1727                    })?;
1728                // Use current resolved hint if available (set during DWARF resolution)
1729                let module_hint = self.current_resolved_var_module_path.clone();
1730                match self.evaluation_result_to_address_with_hint(
1731                    &var.evaluation_result,
1732                    None,
1733                    module_hint.as_deref(),
1734                ) {
1735                    Ok(addr_i64) => {
1736                        let ptr_ty = self.context.ptr_type(AddressSpace::default());
1737                        let as_ptr = self
1738                            .builder
1739                            .build_int_to_ptr(addr_i64, ptr_ty, "addr_as_ptr")
1740                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1741                        Ok(as_ptr.into())
1742                    }
1743                    Err(_) => Err(super::context::CodeGenError::TypeError(
1744                        "cannot take address of rvalue".to_string(),
1745                    )),
1746                }
1747            }
1748            Expr::ArrayAccess(_, _) => {
1749                // Use unified DWARF expression compilation
1750                self.compile_dwarf_expression(expr)
1751            }
1752            Expr::ChainAccess(_) => {
1753                // Use unified DWARF expression compilation
1754                self.compile_dwarf_expression(expr)
1755            }
1756        }
1757    }
1758
1759    /// Handle special variables like $pid, $tid, etc.
1760    pub fn handle_special_variable(&mut self, name: &str) -> Result<BasicValueEnum<'ctx>> {
1761        match name {
1762            "pid" => {
1763                let (pid, _tid) = self.get_special_pid_tid_values()?;
1764                Ok(pid.into())
1765            }
1766            "tid" => {
1767                let (_pid, tid) = self.get_special_pid_tid_values()?;
1768                Ok(tid.into())
1769            }
1770            "host_pid" => {
1771                let (host_pid, _host_tid) = self.get_host_pid_tid_values()?;
1772                let host_pid = self
1773                    .builder
1774                    .build_int_z_extend(host_pid, self.context.i64_type(), "selected_host_pid")
1775                    .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1776                Ok(host_pid.into())
1777            }
1778            "input_pid" => {
1779                let input_pid = self.compile_options.input_pid.ok_or_else(|| {
1780                    CodeGenError::NotImplemented(
1781                        "Special variable '$input_pid' is only available in -p mode".to_string(),
1782                    )
1783                })?;
1784                Ok(self
1785                    .context
1786                    .i64_type()
1787                    .const_int(input_pid as u64, false)
1788                    .into())
1789            }
1790            "timestamp" => {
1791                // Use BPF helper to get current timestamp
1792                let ts = self.get_current_timestamp()?;
1793                Ok(ts.into())
1794            }
1795            _ => {
1796                let supported =
1797                    ["$pid", "$tid", "$host_pid", "$input_pid", "$timestamp"].join(", ");
1798                Err(CodeGenError::NotImplemented(format!(
1799                    "Unknown special variable '${name}'. Supported: {supported}"
1800                )))
1801            }
1802        }
1803    }
1804
1805    /// Compile binary operations
1806    pub fn compile_binary_op(
1807        &mut self,
1808        left: BasicValueEnum<'ctx>,
1809        op: BinaryOp,
1810        right: BasicValueEnum<'ctx>,
1811    ) -> Result<BasicValueEnum<'ctx>> {
1812        use inkwell::values::BasicValueEnum::*;
1813
1814        // Debug logging to understand the actual types
1815        debug!("compile_binary_op: op={:?}", op);
1816        debug!("compile_binary_op: left type = {:?}", left.get_type());
1817        debug!("compile_binary_op: right type = {:?}", right.get_type());
1818        match &left {
1819            IntValue(iv) => debug!(
1820                "compile_binary_op: left is IntValue with bit width {}",
1821                iv.get_type().get_bit_width()
1822            ),
1823            FloatValue(_) => debug!("compile_binary_op: left is FloatValue"),
1824            PointerValue(_) => debug!("compile_binary_op: left is PointerValue"),
1825            _ => debug!("compile_binary_op: left is other type"),
1826        }
1827        match &right {
1828            IntValue(iv) => debug!(
1829                "compile_binary_op: right is IntValue with bit width {}",
1830                iv.get_type().get_bit_width()
1831            ),
1832            FloatValue(_) => debug!("compile_binary_op: right is FloatValue"),
1833            PointerValue(_) => debug!("compile_binary_op: right is PointerValue"),
1834            _ => debug!("compile_binary_op: right is other type"),
1835        }
1836
1837        match (left, right) {
1838            (IntValue(left_int), IntValue(right_int)) => {
1839                let result = match op {
1840                    BinaryOp::Add => self
1841                        .builder
1842                        .build_int_add(left_int, right_int, "add")
1843                        .map_err(|e| CodeGenError::Builder(e.to_string()))?,
1844                    BinaryOp::Subtract => self
1845                        .builder
1846                        .build_int_sub(left_int, right_int, "sub")
1847                        .map_err(|e| CodeGenError::Builder(e.to_string()))?,
1848                    BinaryOp::Multiply => self
1849                        .builder
1850                        .build_int_mul(left_int, right_int, "mul")
1851                        .map_err(|e| CodeGenError::Builder(e.to_string()))?,
1852                    BinaryOp::Divide => self
1853                        .builder
1854                        .build_int_signed_div(left_int, right_int, "div")
1855                        .map_err(|e| CodeGenError::Builder(e.to_string()))?,
1856                    // Comparison operators
1857                    BinaryOp::Equal => {
1858                        let result = self
1859                            .builder
1860                            .build_int_compare(inkwell::IntPredicate::EQ, left_int, right_int, "eq")
1861                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1862                        return Ok(result.into());
1863                    }
1864                    BinaryOp::NotEqual => {
1865                        let result = self
1866                            .builder
1867                            .build_int_compare(inkwell::IntPredicate::NE, left_int, right_int, "ne")
1868                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1869                        return Ok(result.into());
1870                    }
1871                    BinaryOp::LessThan => {
1872                        let result = self
1873                            .builder
1874                            .build_int_compare(
1875                                inkwell::IntPredicate::SLT,
1876                                left_int,
1877                                right_int,
1878                                "lt",
1879                            )
1880                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1881                        return Ok(result.into());
1882                    }
1883                    BinaryOp::LessEqual => {
1884                        let result = self
1885                            .builder
1886                            .build_int_compare(
1887                                inkwell::IntPredicate::SLE,
1888                                left_int,
1889                                right_int,
1890                                "le",
1891                            )
1892                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1893                        return Ok(result.into());
1894                    }
1895                    BinaryOp::GreaterThan => {
1896                        let result = self
1897                            .builder
1898                            .build_int_compare(
1899                                inkwell::IntPredicate::SGT,
1900                                left_int,
1901                                right_int,
1902                                "gt",
1903                            )
1904                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1905                        return Ok(result.into());
1906                    }
1907                    BinaryOp::GreaterEqual => {
1908                        let result = self
1909                            .builder
1910                            .build_int_compare(
1911                                inkwell::IntPredicate::SGE,
1912                                left_int,
1913                                right_int,
1914                                "ge",
1915                            )
1916                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1917                        return Ok(result.into());
1918                    }
1919                    // Logical operators with boolean semantics (non-zero is true)
1920                    BinaryOp::LogicalAnd => {
1921                        let lz = left_int.get_type().const_zero();
1922                        let rz = right_int.get_type().const_zero();
1923                        let lbool = self
1924                            .builder
1925                            .build_int_compare(inkwell::IntPredicate::NE, left_int, lz, "lhs_nz")
1926                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1927                        let rbool = self
1928                            .builder
1929                            .build_int_compare(inkwell::IntPredicate::NE, right_int, rz, "rhs_nz")
1930                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1931                        let result = self
1932                            .builder
1933                            .build_and(lbool, rbool, "and_bool")
1934                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1935                        return Ok(result.into());
1936                    }
1937                    BinaryOp::LogicalOr => {
1938                        let lz = left_int.get_type().const_zero();
1939                        let rz = right_int.get_type().const_zero();
1940                        let lbool = self
1941                            .builder
1942                            .build_int_compare(inkwell::IntPredicate::NE, left_int, lz, "lhs_nz")
1943                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1944                        let rbool = self
1945                            .builder
1946                            .build_int_compare(inkwell::IntPredicate::NE, right_int, rz, "rhs_nz")
1947                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1948                        let result = self
1949                            .builder
1950                            .build_or(lbool, rbool, "or_bool")
1951                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1952                        return Ok(result.into());
1953                    }
1954                };
1955                Ok(result.into())
1956            }
1957            // Pointer equality/inequality comparisons
1958            (PointerValue(lp), IntValue(ri)) | (IntValue(ri), PointerValue(lp)) => {
1959                match op {
1960                    BinaryOp::Equal | BinaryOp::NotEqual => {
1961                        let lpi64 = self
1962                            .builder
1963                            .build_ptr_to_int(lp, self.context.i64_type(), "ptr_as_i64")
1964                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1965                        // Normalize RHS to i64
1966                        let rbw = ri.get_type().get_bit_width();
1967                        let ri64 = if rbw < 64 {
1968                            self.builder
1969                                .build_int_z_extend(ri, self.context.i64_type(), "rhs_zext_i64")
1970                                .map_err(|e| CodeGenError::Builder(e.to_string()))?
1971                        } else if rbw > 64 {
1972                            self.builder
1973                                .build_int_truncate(ri, self.context.i64_type(), "rhs_trunc_i64")
1974                                .map_err(|e| CodeGenError::Builder(e.to_string()))?
1975                        } else {
1976                            ri
1977                        };
1978                        let pred = if matches!(op, BinaryOp::Equal) {
1979                            inkwell::IntPredicate::EQ
1980                        } else {
1981                            inkwell::IntPredicate::NE
1982                        };
1983                        let cmp = self
1984                            .builder
1985                            .build_int_compare(pred, lpi64, ri64, "ptr_cmp")
1986                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1987                        Ok(cmp.into())
1988                    }
1989                    _ => Err(CodeGenError::TypeError(
1990                        "Unsupported operation between aggregate address/pointer and integer: only '==' and '!=' are allowed. If you meant to offset an address, use '&expr + <non-negative literal>' in an alias/address context, or access a scalar field.".to_string(),
1991                    )),
1992                }
1993            }
1994            (PointerValue(lp), PointerValue(rp)) => match op {
1995                BinaryOp::Equal | BinaryOp::NotEqual => {
1996                    let lpi64 = self
1997                        .builder
1998                        .build_ptr_to_int(lp, self.context.i64_type(), "l_ptr_as_i64")
1999                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2000                    let rpi64 = self
2001                        .builder
2002                        .build_ptr_to_int(rp, self.context.i64_type(), "r_ptr_as_i64")
2003                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2004                    let pred = if matches!(op, BinaryOp::Equal) {
2005                        inkwell::IntPredicate::EQ
2006                    } else {
2007                        inkwell::IntPredicate::NE
2008                    };
2009                    let cmp = self
2010                        .builder
2011                        .build_int_compare(pred, lpi64, rpi64, "ptr_ptr_cmp")
2012                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2013                    Ok(cmp.into())
2014                }
2015                _ => Err(CodeGenError::TypeError(
2016                    "Pointer ordered comparison ('<', '<=', '>', '>=') is not supported. Use '==' or '!=' to compare addresses. If you need to adjust an address, use '&expr + <non-negative literal>' in an alias/address context; to compare values, select a scalar field (e.g., 'obj.field')."
2017                        .to_string(),
2018                )),
2019            },
2020            (FloatValue(left_float), FloatValue(right_float)) => match op {
2021                BinaryOp::Add => {
2022                    let result = self
2023                        .builder
2024                        .build_float_add(left_float, right_float, "add")
2025                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2026                    Ok(result.into())
2027                }
2028                BinaryOp::Subtract => {
2029                    let result = self
2030                        .builder
2031                        .build_float_sub(left_float, right_float, "sub")
2032                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2033                    Ok(result.into())
2034                }
2035                BinaryOp::Multiply => {
2036                    let result = self
2037                        .builder
2038                        .build_float_mul(left_float, right_float, "mul")
2039                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2040                    Ok(result.into())
2041                }
2042                BinaryOp::Divide => {
2043                    let result = self
2044                        .builder
2045                        .build_float_div(left_float, right_float, "div")
2046                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2047                    Ok(result.into())
2048                }
2049                // Float comparison operators
2050                BinaryOp::Equal => {
2051                    let result = self
2052                        .builder
2053                        .build_float_compare(
2054                            inkwell::FloatPredicate::OEQ,
2055                            left_float,
2056                            right_float,
2057                            "eq",
2058                        )
2059                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2060                    Ok(result.into())
2061                }
2062                BinaryOp::NotEqual => {
2063                    let result = self
2064                        .builder
2065                        .build_float_compare(
2066                            inkwell::FloatPredicate::ONE,
2067                            left_float,
2068                            right_float,
2069                            "ne",
2070                        )
2071                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2072                    Ok(result.into())
2073                }
2074                BinaryOp::LessThan => {
2075                    let result = self
2076                        .builder
2077                        .build_float_compare(
2078                            inkwell::FloatPredicate::OLT,
2079                            left_float,
2080                            right_float,
2081                            "lt",
2082                        )
2083                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2084                    Ok(result.into())
2085                }
2086                BinaryOp::LessEqual => {
2087                    let result = self
2088                        .builder
2089                        .build_float_compare(
2090                            inkwell::FloatPredicate::OLE,
2091                            left_float,
2092                            right_float,
2093                            "le",
2094                        )
2095                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2096                    Ok(result.into())
2097                }
2098                BinaryOp::GreaterThan => {
2099                    let result = self
2100                        .builder
2101                        .build_float_compare(
2102                            inkwell::FloatPredicate::OGT,
2103                            left_float,
2104                            right_float,
2105                            "gt",
2106                        )
2107                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2108                    Ok(result.into())
2109                }
2110                BinaryOp::GreaterEqual => {
2111                    let result = self
2112                        .builder
2113                        .build_float_compare(
2114                            inkwell::FloatPredicate::OGE,
2115                            left_float,
2116                            right_float,
2117                            "ge",
2118                        )
2119                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2120                    Ok(result.into())
2121                }
2122                _ => Err(CodeGenError::NotImplemented(format!(
2123                    "Float binary operation {op:?} not implemented"
2124                ))),
2125            },
2126            _ => Err(CodeGenError::TypeError(format!(
2127                "Type mismatch in binary operation {op:?}"
2128            ))),
2129        }
2130    }
2131
2132    /// Compile member access (struct.field)
2133    pub fn compile_member_access(
2134        &mut self,
2135        obj_expr: &Expr,
2136        field: &str,
2137    ) -> Result<BasicValueEnum<'ctx>> {
2138        // Create a MemberAccess expression and use the unified DWARF compilation
2139        let member_access_expr = Expr::MemberAccess(Box::new(obj_expr.clone()), field.to_string());
2140        self.compile_dwarf_expression(&member_access_expr)
2141    }
2142
2143    /// Compile pointer dereference (*ptr)
2144    pub fn compile_pointer_deref(&mut self, expr: &Expr) -> Result<BasicValueEnum<'ctx>> {
2145        // Create a PointerDeref expression and use the unified DWARF compilation
2146        let pointer_deref_expr = Expr::PointerDeref(Box::new(expr.clone()));
2147        self.compile_dwarf_expression(&pointer_deref_expr)
2148    }
2149
2150    /// Compile array access (arr[index])
2151    pub fn compile_array_access(
2152        &mut self,
2153        array_expr: &Expr,
2154        index_expr: &Expr,
2155    ) -> Result<BasicValueEnum<'ctx>> {
2156        // Create an ArrayAccess expression and use the unified DWARF compilation
2157        let array_access_expr =
2158            Expr::ArrayAccess(Box::new(array_expr.clone()), Box::new(index_expr.clone()));
2159        self.compile_dwarf_expression(&array_access_expr)
2160    }
2161
2162    /// Compile chain access (person.name.first)
2163    pub fn compile_chain_access(&mut self, chain: &[String]) -> Result<BasicValueEnum<'ctx>> {
2164        // Create a ChainAccess expression and use the unified DWARF compilation
2165        let chain_access_expr = Expr::ChainAccess(chain.to_vec());
2166        self.compile_dwarf_expression(&chain_access_expr)
2167    }
2168
2169    /// Unified DWARF expression compilation
2170    pub fn compile_dwarf_expression(
2171        &mut self,
2172        expr: &crate::script::Expr,
2173    ) -> Result<BasicValueEnum<'ctx>> {
2174        debug!(
2175            "compile_dwarf_expression: Compiling complex expression: {:?}",
2176            expr
2177        );
2178
2179        // Query DWARF for the complex expression
2180        let compile_context = self.get_compile_time_context()?.clone();
2181        let variable_with_eval = match self.query_dwarf_for_complex_expr(expr)? {
2182            Some(var) => var,
2183            None => {
2184                let expr_str = Self::expr_to_debug_string(expr);
2185                return Err(CodeGenError::VariableNotFound(expr_str));
2186            }
2187        };
2188
2189        let dwarf_type = variable_with_eval.dwarf_type.as_ref().ok_or_else(|| {
2190            CodeGenError::DwarfError("Expression has no DWARF type information".to_string())
2191        })?;
2192
2193        debug!(
2194            "compile_dwarf_expression: Found DWARF info for expression '{}' with type: {:?}",
2195            variable_with_eval.name, dwarf_type
2196        );
2197
2198        // Use the unified evaluation logic to generate LLVM IR
2199        self.evaluate_result_to_llvm_value(
2200            &variable_with_eval.evaluation_result,
2201            dwarf_type,
2202            &variable_with_eval.name,
2203            compile_context.pc_address,
2204            None,
2205        )
2206    }
2207
2208    /// Helper: Convert expression to string for debugging
2209    fn expr_to_debug_string(expr: &crate::script::Expr) -> String {
2210        use crate::script::Expr;
2211
2212        match expr {
2213            Expr::Variable(name) => name.clone(),
2214            Expr::MemberAccess(obj, field) => {
2215                format!("{}.{}", Self::expr_to_debug_string(obj), field)
2216            }
2217            Expr::ArrayAccess(arr, _) => format!("{}[index]", Self::expr_to_debug_string(arr)),
2218            Expr::ChainAccess(chain) => chain.join("."),
2219            Expr::PointerDeref(expr) => format!("*{}", Self::expr_to_debug_string(expr)),
2220            _ => "expr".to_string(),
2221        }
2222    }
2223}
2224
2225impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> {
2226    /// Compile comparison between a DWARF-side expression and a script string literal.
2227    /// Supports char* and char[N] according to design in string_comparison.md.
2228    fn compile_string_comparison(
2229        &mut self,
2230        dwarf_expr: &Expr,
2231        lit: &str,
2232        is_equal: bool,
2233    ) -> Result<BasicValueEnum<'ctx>> {
2234        use ghostscope_dwarf::TypeInfo as TI;
2235
2236        // Query DWARF for the non-string side to obtain evaluation and type info
2237        let var = self
2238            .query_dwarf_for_complex_expr(dwarf_expr)?
2239            .ok_or_else(|| {
2240                CodeGenError::TypeError(
2241                    "string comparison requires DWARF variable/expression".into(),
2242                )
2243            })?;
2244        // Try DWARF type first; if unavailable, fall back to type_name string parsing
2245        let dwarf_type_opt = var.dwarf_type.as_ref();
2246
2247        enum ParsedKind {
2248            PtrChar,
2249            ArrChar(Option<u32>),
2250            Other,
2251        }
2252        fn parse_type_name(name: &str) -> ParsedKind {
2253            let lower = name.to_lowercase();
2254            let has_char = lower.contains("char");
2255            let is_ptr = lower.contains('*');
2256            if has_char && is_ptr {
2257                return ParsedKind::PtrChar;
2258            }
2259            if has_char && lower.contains('[') {
2260                // Try to extract N inside brackets
2261                let mut n: Option<u32> = None;
2262                if let Some(start) = lower.find('[') {
2263                    if let Some(end) = lower[start + 1..].find(']') {
2264                        let inside = &lower[start + 1..start + 1 + end];
2265                        let digits: String =
2266                            inside.chars().filter(|c| c.is_ascii_digit()).collect();
2267                        if !digits.is_empty() {
2268                            if let Ok(v) = digits.parse::<u32>() {
2269                                n = Some(v);
2270                            }
2271                        }
2272                    }
2273                }
2274                return ParsedKind::ArrChar(n);
2275            }
2276            ParsedKind::Other
2277        }
2278
2279        // Helper to peel typedef/qualifier wrappers
2280        fn unwrap_aliases(t: &TI) -> &TI {
2281            let mut cur = t;
2282            loop {
2283                match cur {
2284                    TI::TypedefType {
2285                        underlying_type, ..
2286                    } => cur = underlying_type.as_ref(),
2287                    TI::QualifiedType {
2288                        underlying_type, ..
2289                    } => cur = underlying_type.as_ref(),
2290                    _ => break,
2291                }
2292            }
2293            cur
2294        }
2295
2296        // Compute runtime address of the DWARF expression
2297        let module_hint = self.current_resolved_var_module_path.clone();
2298        let status_ptr = if self.condition_context_active {
2299            Some(self.get_or_create_cond_error_global())
2300        } else {
2301            None
2302        };
2303        let addr = self.evaluation_result_to_address_with_hint(
2304            &var.evaluation_result,
2305            status_ptr,
2306            module_hint.as_deref(),
2307        )?;
2308
2309        let lit_bytes = lit.as_bytes();
2310        let lit_len = lit_bytes.len() as u32;
2311        let one = self.context.bool_type().const_int(1, false);
2312        let zero = self.context.bool_type().const_zero();
2313
2314        // Build final boolean accumulator
2315        let result = match dwarf_type_opt.map(unwrap_aliases) {
2316            // char* / const char*
2317            Some(TI::PointerType { target_type, .. }) => {
2318                // Ensure pointee is char-like
2319                let base = unwrap_aliases(target_type.as_ref());
2320                let is_char_like = matches!(base, TI::BaseType { name, size, .. } if name.contains("char") && *size == 1);
2321                if !is_char_like {
2322                    return Err(CodeGenError::TypeError(
2323                        "automatic string comparison only supports char*".into(),
2324                    ));
2325                }
2326
2327                // Evaluate expression to pointer value and read up to L+1 bytes
2328                let val_any = self.evaluate_result_to_llvm_value(
2329                    &var.evaluation_result,
2330                    var.dwarf_type.as_ref().unwrap(),
2331                    &var.name,
2332                    self.get_compile_time_context()?.pc_address,
2333                    None,
2334                )?;
2335                let ptr_i64 = match val_any {
2336                    BasicValueEnum::IntValue(iv) => iv,
2337                    BasicValueEnum::PointerValue(pv) => self
2338                        .builder
2339                        .build_ptr_to_int(pv, self.context.i64_type(), "ptr_as_i64")
2340                        .map_err(|e| CodeGenError::Builder(e.to_string()))?,
2341                    _ => {
2342                        return Err(CodeGenError::TypeError(
2343                            "pointer value must be integer or pointer".into(),
2344                        ))
2345                    }
2346                };
2347                let need = lit_len + 1;
2348                let (buf_global, ret_len, arr_ty) =
2349                    self.read_user_cstr_into_buffer(ptr_i64, need, "_gs_strbuf")?;
2350
2351                // ret_len must equal L+1
2352                let i64_ty = self.context.i64_type();
2353                let expect_len = i64_ty.const_int(need as u64, false);
2354                let len_ok = self
2355                    .builder
2356                    .build_int_compare(inkwell::IntPredicate::EQ, ret_len, expect_len, "str_len_ok")
2357                    .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2358
2359                // buf[L] must be '\0'
2360                let i32_ty = self.context.i32_type();
2361                let idx0 = i32_ty.const_zero();
2362                let idx_l = i32_ty.const_int(lit_len as u64, false);
2363                let char_ptr = unsafe {
2364                    self.builder
2365                        .build_gep(arr_ty, buf_global, &[idx0, idx_l], "nul_ptr")
2366                        .map_err(|e| CodeGenError::Builder(e.to_string()))?
2367                };
2368                let c = self
2369                    .builder
2370                    .build_load(self.context.i8_type(), char_ptr, "c_l")
2371                    .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2372                let c = match c {
2373                    BasicValueEnum::IntValue(iv) => iv,
2374                    _ => return Err(CodeGenError::LLVMError("load did not return i8".into())),
2375                };
2376                let nul_ok = self
2377                    .builder
2378                    .build_int_compare(
2379                        inkwell::IntPredicate::EQ,
2380                        c,
2381                        self.context.i8_type().const_zero(),
2382                        "nul_ok",
2383                    )
2384                    .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2385
2386                // Compare first L bytes using XOR/OR accumulation to reduce branchiness
2387                let mut acc = self.context.i8_type().const_zero();
2388                for (i, b) in lit_bytes.iter().enumerate() {
2389                    let idx_i = i32_ty.const_int(i as u64, false);
2390                    let ptr_i = unsafe {
2391                        self.builder
2392                            .build_gep(arr_ty, buf_global, &[idx0, idx_i], "ch_ptr")
2393                            .map_err(|e| CodeGenError::Builder(e.to_string()))?
2394                    };
2395                    let ch = self
2396                        .builder
2397                        .build_load(self.context.i8_type(), ptr_i, "ch")
2398                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2399                    let ch = match ch {
2400                        BasicValueEnum::IntValue(iv) => iv,
2401                        _ => return Err(CodeGenError::LLVMError("load did not return i8".into())),
2402                    };
2403                    let expect = self.context.i8_type().const_int(*b as u64, false);
2404                    let diff = self
2405                        .builder
2406                        .build_xor(ch, expect, "diff")
2407                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2408                    acc = self
2409                        .builder
2410                        .build_or(acc, diff, "acc_or")
2411                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2412                }
2413                let eq_bytes = self
2414                    .builder
2415                    .build_int_compare(
2416                        inkwell::IntPredicate::EQ,
2417                        acc,
2418                        self.context.i8_type().const_zero(),
2419                        "acc_zero",
2420                    )
2421                    .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2422                let ok1 = self
2423                    .builder
2424                    .build_and(len_ok, nul_ok, "ok_len_nul")
2425                    .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2426                self.builder
2427                    .build_and(ok1, eq_bytes, "str_eq")
2428                    .map_err(|e| CodeGenError::Builder(e.to_string()))?
2429            }
2430            // char[N]
2431            Some(TI::ArrayType {
2432                element_type,
2433                element_count,
2434                total_size,
2435            }) => {
2436                let elem = unwrap_aliases(element_type.as_ref());
2437                let is_char_like = matches!(elem, TI::BaseType { name, size, .. } if name.contains("char") && *size == 1);
2438                if !is_char_like {
2439                    return Err(CodeGenError::TypeError(
2440                        "automatic string comparison only supports char[N]".into(),
2441                    ));
2442                }
2443                // Determine N (element count)
2444                let n_opt = element_count.or_else(|| total_size.map(|ts| ts));
2445                let n = if let Some(nv) = n_opt { nv as u32 } else { 0 };
2446                if n == 0 {
2447                    return Err(CodeGenError::TypeError(
2448                        "array size unknown for char[N] comparison".into(),
2449                    ));
2450                }
2451                // If L+1 > N, compile-time false
2452                if lit_len + 1 > n {
2453                    // Return const false (or true if '!=' requested)
2454                    return Ok((if is_equal { zero } else { one }).into());
2455                }
2456                // Read exactly L+1 bytes
2457                let (buf_global, status, arr_ty) =
2458                    self.read_user_bytes_into_buffer(addr, lit_len + 1, "_gs_arrbuf")?;
2459                // status == 0
2460                let status_ok = self
2461                    .builder
2462                    .build_int_compare(
2463                        inkwell::IntPredicate::EQ,
2464                        status,
2465                        self.context.i64_type().const_zero(),
2466                        "rd_ok",
2467                    )
2468                    .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2469                // buf[L] must be '\0'
2470                let i32_ty = self.context.i32_type();
2471                let idx0 = i32_ty.const_zero();
2472                let idx_l = i32_ty.const_int(lit_len as u64, false);
2473                let char_ptr = unsafe {
2474                    self.builder
2475                        .build_gep(arr_ty, buf_global, &[idx0, idx_l], "nul_ptr")
2476                        .map_err(|e| CodeGenError::Builder(e.to_string()))?
2477                };
2478                let c = self
2479                    .builder
2480                    .build_load(self.context.i8_type(), char_ptr, "c_l")
2481                    .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2482                let c = match c {
2483                    BasicValueEnum::IntValue(iv) => iv,
2484                    _ => return Err(CodeGenError::LLVMError("load did not return i8".into())),
2485                };
2486                let nul_ok = self
2487                    .builder
2488                    .build_int_compare(
2489                        inkwell::IntPredicate::EQ,
2490                        c,
2491                        self.context.i8_type().const_zero(),
2492                        "nul_ok",
2493                    )
2494                    .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2495                // Compare first L bytes using XOR/OR accumulation
2496                let mut acc = self.context.i8_type().const_zero();
2497                for (i, b) in lit_bytes.iter().enumerate() {
2498                    let idx_i = i32_ty.const_int(i as u64, false);
2499                    let ptr_i = unsafe {
2500                        self.builder
2501                            .build_gep(arr_ty, buf_global, &[idx0, idx_i], "ch_ptr")
2502                            .map_err(|e| CodeGenError::Builder(e.to_string()))?
2503                    };
2504                    let ch = self
2505                        .builder
2506                        .build_load(self.context.i8_type(), ptr_i, "ch")
2507                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2508                    let ch = match ch {
2509                        BasicValueEnum::IntValue(iv) => iv,
2510                        _ => return Err(CodeGenError::LLVMError("load did not return i8".into())),
2511                    };
2512                    let expect = self.context.i8_type().const_int(*b as u64, false);
2513                    let diff = self
2514                        .builder
2515                        .build_xor(ch, expect, "diff")
2516                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2517                    acc = self
2518                        .builder
2519                        .build_or(acc, diff, "acc_or")
2520                        .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2521                }
2522                let eq_bytes = self
2523                    .builder
2524                    .build_int_compare(
2525                        inkwell::IntPredicate::EQ,
2526                        acc,
2527                        self.context.i8_type().const_zero(),
2528                        "acc_zero",
2529                    )
2530                    .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2531                let ok1 = self
2532                    .builder
2533                    .build_and(status_ok, nul_ok, "ok_len_nul")
2534                    .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2535                self.builder
2536                    .build_and(ok1, eq_bytes, "arr_eq")
2537                    .map_err(|e| CodeGenError::Builder(e.to_string()))?
2538            }
2539            None => {
2540                // Fallback using type_name string
2541                match parse_type_name(&var.type_name) {
2542                    ParsedKind::PtrChar => {
2543                        // Load pointer value from variable location (assume 64-bit)
2544                        let ptr_any = self.generate_memory_read(
2545                            addr,
2546                            ghostscope_dwarf::MemoryAccessSize::U64,
2547                            None,
2548                        )?;
2549                        let ptr_i64 = match ptr_any {
2550                            BasicValueEnum::IntValue(iv) => iv,
2551                            _ => {
2552                                return Err(CodeGenError::LLVMError(
2553                                    "pointer load did not return integer".to_string(),
2554                                ))
2555                            }
2556                        };
2557                        let need = lit_len + 1;
2558                        let (buf_global, ret_len, arr_ty) =
2559                            self.read_user_cstr_into_buffer(ptr_i64, need, "_gs_strbuf")?;
2560
2561                        let i64_ty = self.context.i64_type();
2562                        let expect_len = i64_ty.const_int(need as u64, false);
2563                        let len_ok = self
2564                            .builder
2565                            .build_int_compare(
2566                                inkwell::IntPredicate::EQ,
2567                                ret_len,
2568                                expect_len,
2569                                "str_len_ok",
2570                            )
2571                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2572
2573                        let i32_ty = self.context.i32_type();
2574                        let idx0 = i32_ty.const_zero();
2575                        let idx_l = i32_ty.const_int(lit_len as u64, false);
2576                        let char_ptr = unsafe {
2577                            self.builder
2578                                .build_gep(arr_ty, buf_global, &[idx0, idx_l], "nul_ptr")
2579                                .map_err(|e| CodeGenError::Builder(e.to_string()))?
2580                        };
2581                        let c = self
2582                            .builder
2583                            .build_load(self.context.i8_type(), char_ptr, "c_l")
2584                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2585                        let c = match c {
2586                            BasicValueEnum::IntValue(iv) => iv,
2587                            _ => {
2588                                return Err(CodeGenError::LLVMError(
2589                                    "load did not return i8".into(),
2590                                ))
2591                            }
2592                        };
2593                        let nul_ok = self
2594                            .builder
2595                            .build_int_compare(
2596                                inkwell::IntPredicate::EQ,
2597                                c,
2598                                self.context.i8_type().const_zero(),
2599                                "nul_ok",
2600                            )
2601                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2602
2603                        let mut acc = self.context.i8_type().const_zero();
2604                        for (i, b) in lit_bytes.iter().enumerate() {
2605                            let idx_i = i32_ty.const_int(i as u64, false);
2606                            let ptr_i = unsafe {
2607                                self.builder
2608                                    .build_gep(arr_ty, buf_global, &[idx0, idx_i], "ch_ptr")
2609                                    .map_err(|e| CodeGenError::Builder(e.to_string()))?
2610                            };
2611                            let ch = self
2612                                .builder
2613                                .build_load(self.context.i8_type(), ptr_i, "ch")
2614                                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2615                            let ch = match ch {
2616                                BasicValueEnum::IntValue(iv) => iv,
2617                                _ => {
2618                                    return Err(CodeGenError::LLVMError(
2619                                        "load did not return i8".into(),
2620                                    ))
2621                                }
2622                            };
2623                            let expect = self.context.i8_type().const_int(*b as u64, false);
2624                            let diff = self
2625                                .builder
2626                                .build_xor(ch, expect, "diff")
2627                                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2628                            acc = self
2629                                .builder
2630                                .build_or(acc, diff, "acc_or")
2631                                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2632                        }
2633                        let eq_bytes = self
2634                            .builder
2635                            .build_int_compare(
2636                                inkwell::IntPredicate::EQ,
2637                                acc,
2638                                self.context.i8_type().const_zero(),
2639                                "acc_zero",
2640                            )
2641                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2642                        let ok1 = self
2643                            .builder
2644                            .build_and(len_ok, nul_ok, "ok_len_nul")
2645                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2646                        self.builder
2647                            .build_and(ok1, eq_bytes, "str_eq")
2648                            .map_err(|e| CodeGenError::Builder(e.to_string()))?
2649                    }
2650                    ParsedKind::ArrChar(n_opt) => {
2651                        // If we know N and L+1>N, return false; else read L+1 bytes
2652                        if let Some(n) = n_opt {
2653                            if lit_len + 1 > n {
2654                                return Ok((if is_equal { zero } else { one }).into());
2655                            }
2656                        }
2657                        let (buf_global, status, arr_ty) =
2658                            self.read_user_bytes_into_buffer(addr, lit_len + 1, "_gs_arrbuf")?;
2659                        let status_ok = self
2660                            .builder
2661                            .build_int_compare(
2662                                inkwell::IntPredicate::EQ,
2663                                status,
2664                                self.context.i64_type().const_zero(),
2665                                "rd_ok",
2666                            )
2667                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2668                        let i32_ty = self.context.i32_type();
2669                        let idx0 = i32_ty.const_zero();
2670                        let idx_l = i32_ty.const_int(lit_len as u64, false);
2671                        let char_ptr = unsafe {
2672                            self.builder
2673                                .build_gep(arr_ty, buf_global, &[idx0, idx_l], "nul_ptr")
2674                                .map_err(|e| CodeGenError::Builder(e.to_string()))?
2675                        };
2676                        let c = self
2677                            .builder
2678                            .build_load(self.context.i8_type(), char_ptr, "c_l")
2679                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2680                        let c = match c {
2681                            BasicValueEnum::IntValue(iv) => iv,
2682                            _ => {
2683                                return Err(CodeGenError::LLVMError(
2684                                    "load did not return i8".into(),
2685                                ))
2686                            }
2687                        };
2688                        let nul_ok = self
2689                            .builder
2690                            .build_int_compare(
2691                                inkwell::IntPredicate::EQ,
2692                                c,
2693                                self.context.i8_type().const_zero(),
2694                                "nul_ok",
2695                            )
2696                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2697                        let mut acc = self.context.i8_type().const_zero();
2698                        for (i, b) in lit_bytes.iter().enumerate() {
2699                            let idx_i = i32_ty.const_int(i as u64, false);
2700                            let ptr_i = unsafe {
2701                                self.builder
2702                                    .build_gep(arr_ty, buf_global, &[idx0, idx_i], "ch_ptr")
2703                                    .map_err(|e| CodeGenError::Builder(e.to_string()))?
2704                            };
2705                            let ch = self
2706                                .builder
2707                                .build_load(self.context.i8_type(), ptr_i, "ch")
2708                                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2709                            let ch = match ch {
2710                                BasicValueEnum::IntValue(iv) => iv,
2711                                _ => {
2712                                    return Err(CodeGenError::LLVMError(
2713                                        "load did not return i8".into(),
2714                                    ))
2715                                }
2716                            };
2717                            let expect = self.context.i8_type().const_int(*b as u64, false);
2718                            let diff = self
2719                                .builder
2720                                .build_xor(ch, expect, "diff")
2721                                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2722                            acc = self
2723                                .builder
2724                                .build_or(acc, diff, "acc_or")
2725                                .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2726                        }
2727                        let eq_bytes = self
2728                            .builder
2729                            .build_int_compare(
2730                                inkwell::IntPredicate::EQ,
2731                                acc,
2732                                self.context.i8_type().const_zero(),
2733                                "acc_zero",
2734                            )
2735                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2736                        let ok1 = self
2737                            .builder
2738                            .build_and(status_ok, nul_ok, "ok_len_nul")
2739                            .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2740                        self.builder
2741                            .build_and(ok1, eq_bytes, "arr_eq")
2742                            .map_err(|e| CodeGenError::Builder(e.to_string()))?
2743                    }
2744                    ParsedKind::Other => {
2745                        return Err(CodeGenError::TypeError(format!(
2746                            "string comparison unsupported for type name '{}' without DWARF type",
2747                            var.type_name
2748                        )));
2749                    }
2750                }
2751            }
2752            Some(_) => {
2753                return Err(CodeGenError::TypeError(
2754                    "string comparison only supports char* or char[N]".into(),
2755                ));
2756            }
2757        };
2758
2759        // Apply == / !=
2760        let final_bool = if is_equal {
2761            result
2762        } else {
2763            self.builder
2764                .build_not(result, "not_eq")
2765                .map_err(|e| CodeGenError::Builder(e.to_string()))?
2766        };
2767        Ok(final_bool.into())
2768    }
2769}