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