1use super::context::{CodeGenError, EbpfContext, Result, RuntimeAddress};
6use super::expression_plan::{
7 BinaryEmitKind, BinaryIntegerSemantics, BuiltinCallPlan, SpecialVarPlan,
8};
9use crate::script::{BinaryOp, Expr};
10use aya_ebpf_bindings::bindings::bpf_func_id::BPF_FUNC_probe_read_user;
11use ghostscope_dwarf::{
12 AmbiguityReason, Availability, CIntegerComparisonPlan, CIntegerComparisonType,
13 RuntimeRequirement, TypeInfo as DwarfType, TypeLayoutError, UnsupportedReason,
14 VariableReadPlan,
15};
16use inkwell::values::{BasicValueEnum, IntValue, PointerValue};
17use inkwell::AddressSpace;
18use std::path::{Path, PathBuf};
19use tracing::debug;
20
21#[derive(Clone)]
24pub(super) struct DynamicTypeInfo {
25 pub(super) dwarf_type: DwarfType,
26 pub(super) module_path: Option<PathBuf>,
27}
28
29pub(super) struct DynamicLvalue<'ctx> {
30 pub(super) address: RuntimeAddress<'ctx>,
31 pub(super) type_info: DynamicTypeInfo,
32}
33
34struct IndexableElementInfo {
35 element_type: DwarfType,
36 stride: u64,
37 module_path: Option<PathBuf>,
38}
39
40impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> {
41 pub(crate) fn get_host_pid_tid_values(&mut self) -> Result<(IntValue<'ctx>, IntValue<'ctx>)> {
42 let i32_type = self.context.i32_type();
43 let i64_type = self.context.i64_type();
44
45 let host_pid_tgid = self.get_current_pid_tgid()?;
49 let host_tid = self
50 .builder
51 .build_and(
52 host_pid_tgid,
53 i64_type.const_int(0xFFFF_FFFF, false),
54 "host_tid",
55 )
56 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
57 let host_pid = self
58 .builder
59 .build_right_shift(
60 host_pid_tgid,
61 i64_type.const_int(32, false),
62 false,
63 "host_pid",
64 )
65 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
66
67 let host_pid_i32 = self
68 .builder
69 .build_int_truncate(host_pid, i32_type, "host_pid_i32")
70 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
71 let host_tid_i32 = self
72 .builder
73 .build_int_truncate(host_tid, i32_type, "host_tid_i32")
74 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
75
76 Ok((host_pid_i32, host_tid_i32))
77 }
78
79 pub(crate) fn get_special_pid_tid_values(
80 &mut self,
81 ) -> Result<(IntValue<'ctx>, IntValue<'ctx>)> {
82 const BPF_FUNC_GET_NS_CURRENT_PID_TGID: u64 = 120;
83 const BPF_PIDNS_INFO_SIZE: u64 = 8; let i32_type = self.context.i32_type();
86 let i64_type = self.context.i64_type();
87 let (host_pid_i32, host_tid_i32) = self.get_host_pid_tid_values()?;
88
89 let ns_spec = if let Some(crate::PidFilterSpec::NamespaceTgid { pid_ns, .. }) =
90 self.compile_options.pid_filter_spec
91 {
92 pid_ns.helper_dev_inode()
93 } else {
94 self.compile_options
95 .special_pid_ns
96 .and_then(|pid_ns| pid_ns.helper_dev_inode())
97 };
98 let Some((pid_ns_dev, pid_ns_inode)) = ns_spec else {
99 let host_pid = self
100 .builder
101 .build_int_z_extend(host_pid_i32, i64_type, "selected_host_pid")
102 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
103 let host_tid = self
104 .builder
105 .build_int_z_extend(host_tid_i32, i64_type, "selected_host_tid")
106 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
107 return Ok((host_pid, host_tid));
108 };
109
110 let ptr_type = self.context.ptr_type(AddressSpace::default());
111 let key_arr_ty = i32_type.array_type(4);
112 let key_alloca = self.pm_key_alloca.ok_or_else(|| {
113 CodeGenError::LLVMError("pm_key not allocated in entry block".to_string())
114 })?;
115 self.builder
117 .build_store(key_alloca, key_arr_ty.const_zero())
118 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
119
120 let pidns_info_ptr = self
121 .builder
122 .build_bit_cast(key_alloca, ptr_type, "special_pidns_info_ptr")
123 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
124
125 let helper_args = [
126 i64_type.const_int(pid_ns_dev, false).into(),
127 i64_type.const_int(pid_ns_inode, false).into(),
128 pidns_info_ptr,
129 i64_type.const_int(BPF_PIDNS_INFO_SIZE, false).into(),
130 ];
131 let helper_ret = self.create_bpf_helper_call(
132 BPF_FUNC_GET_NS_CURRENT_PID_TGID,
133 &helper_args,
134 i64_type.into(),
135 "special_ns_pid_tgid_ret",
136 )?;
137 let helper_ret = match helper_ret {
138 BasicValueEnum::IntValue(v) => v,
139 _ => {
140 return Err(CodeGenError::LLVMError(
141 "bpf_get_ns_current_pid_tgid did not return integer".to_string(),
142 ))
143 }
144 };
145
146 let helper_ok = self
147 .builder
148 .build_int_compare(
149 inkwell::IntPredicate::EQ,
150 helper_ret,
151 i64_type.const_zero(),
152 "special_ns_helper_ok",
153 )
154 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
155
156 let ns_pid_ptr = unsafe {
159 self.builder.build_gep(
160 key_arr_ty,
161 key_alloca,
162 &[i32_type.const_zero(), i32_type.const_zero()],
163 "special_ns_pid_ptr",
164 )
165 }
166 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
167 let ns_tgid_ptr = unsafe {
170 self.builder.build_gep(
171 key_arr_ty,
172 key_alloca,
173 &[i32_type.const_zero(), i32_type.const_int(1, false)],
174 "special_ns_tgid_ptr",
175 )
176 }
177 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
178
179 let ns_pid = self
180 .builder
181 .build_load(i32_type, ns_pid_ptr, "special_ns_pid")
182 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
183 .into_int_value();
184 let ns_tgid = self
185 .builder
186 .build_load(i32_type, ns_tgid_ptr, "special_ns_tgid")
187 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?
188 .into_int_value();
189
190 let selected_pid_i32 = self
191 .builder
192 .build_select(helper_ok, ns_tgid, host_pid_i32, "selected_pid_i32")
193 .map_err(|e| CodeGenError::Builder(e.to_string()))?
194 .into_int_value();
195 let selected_tid_i32 = self
196 .builder
197 .build_select(helper_ok, ns_pid, host_tid_i32, "selected_tid_i32")
198 .map_err(|e| CodeGenError::Builder(e.to_string()))?
199 .into_int_value();
200
201 let selected_pid = self
202 .builder
203 .build_int_z_extend(selected_pid_i32, i64_type, "selected_pid")
204 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
205 let selected_tid = self
206 .builder
207 .build_int_z_extend(selected_tid_i32, i64_type, "selected_tid")
208 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
209
210 Ok((selected_pid, selected_tid))
211 }
212
213 pub(super) fn is_dwarf_aggregate_expr(&mut self, expr: &Expr) -> bool {
214 if let Expr::Cast { target_type, .. } = expr {
215 return self
216 .resolve_cast_target_type(target_type)
217 .ok()
218 .is_some_and(|ty| ghostscope_dwarf::is_c_aggregate_type(&ty));
219 }
220
221 if let Ok(Some(var)) = self.query_dwarf_for_complex_expr(expr) {
222 if let Some(ref ty) = var.dwarf_type {
223 return ghostscope_dwarf::is_c_aggregate_type(ty);
224 }
225 }
226 false
227 }
228
229 pub(super) fn is_pointer_like_expr(&mut self, expr: &Expr) -> bool {
236 use crate::script::Expr as E;
237 match expr {
238 E::AddressOf(_) => return true,
239 E::String(_) => return true,
240 E::Cast { target_type, .. } => {
241 if self
242 .resolve_cast_target_type(target_type)
243 .ok()
244 .is_some_and(|ty| {
245 matches!(
246 ghostscope_dwarf::strip_type_aliases(&ty),
247 DwarfType::PointerType { .. } | DwarfType::ArrayType { .. }
248 )
249 })
250 {
251 return true;
252 }
253 }
254 E::Variable(name) => {
255 if self.alias_variable_exists(name) {
256 return true;
257 }
258 }
259 _ => {}
260 }
261
262 if let Ok(Some(var)) = self.query_dwarf_for_complex_expr(expr) {
263 if let Some(ref ty) = var.dwarf_type {
264 if ghostscope_dwarf::is_c_pointer_or_array_type(ty) {
265 return true;
266 }
267 }
268 }
269 false
270 }
271
272 pub(super) fn resolve_cast_target_type(&self, target_type: &str) -> Result<DwarfType> {
273 let analyzer = self.process_analyzer;
274 let resolved = if let Some(context) = self.current_compile_time_context.as_ref() {
275 let module_path = Path::new(&context.module_path);
276 analyzer
277 .map(|analyzer| analyzer.try_resolve_type_spec_in_module(module_path, target_type))
278 .transpose()
279 .map_err(|err| CodeGenError::DwarfError(err.to_string()))?
280 .flatten()
281 .or_else(|| ghostscope_dwarf::DwarfAnalyzer::resolve_builtin_type_spec(target_type))
282 } else {
283 analyzer
284 .map(|analyzer| analyzer.try_resolve_type_spec(target_type))
285 .transpose()
286 .map_err(|err| CodeGenError::DwarfError(err.to_string()))?
287 .flatten()
288 .or_else(|| ghostscope_dwarf::DwarfAnalyzer::resolve_builtin_type_spec(target_type))
289 };
290
291 resolved.ok_or_else(|| {
292 CodeGenError::DwarfError(format!("cast target type '{target_type}' was not found"))
293 })
294 }
295
296 pub(super) fn cast_pointer_target_type(target_type: &DwarfType) -> Option<DwarfType> {
297 match ghostscope_dwarf::strip_type_aliases(target_type) {
298 DwarfType::PointerType { target_type, .. } => Some(target_type.as_ref().clone()),
299 _ => None,
300 }
301 }
302
303 fn is_float_dwarf_type(target_type: &DwarfType) -> bool {
304 match ghostscope_dwarf::strip_type_aliases(target_type) {
305 DwarfType::BaseType { encoding, .. } => {
306 *encoding == ghostscope_dwarf::constants::DW_ATE_float.0 as u16
307 }
308 _ => false,
309 }
310 }
311
312 fn is_bool_dwarf_type(target_type: &DwarfType) -> bool {
313 match ghostscope_dwarf::strip_type_aliases(target_type) {
314 DwarfType::BaseType { encoding, .. } => {
315 *encoding == ghostscope_dwarf::constants::DW_ATE_boolean.0 as u16
316 }
317 _ => false,
318 }
319 }
320
321 pub(super) fn cast_value_byte_len(target_type: &DwarfType) -> Option<usize> {
322 if matches!(
323 ghostscope_dwarf::strip_type_aliases(target_type),
324 DwarfType::PointerType { .. }
325 ) {
326 return Some(8);
327 }
328
329 if let Some(integer_type) = ghostscope_dwarf::c_integer_comparison_type(target_type) {
330 return Some(integer_type.size.clamp(1, 8) as usize);
331 }
332
333 None
334 }
335
336 pub(super) fn cast_source_pointer_value(
337 &mut self,
338 expr: &Expr,
339 ) -> Result<RuntimeAddress<'ctx>> {
340 if let Ok(address) = self.resolve_runtime_address_from_expr(expr) {
341 return Ok(address);
342 }
343
344 match self.compile_expr(expr)? {
345 BasicValueEnum::IntValue(value) => Ok(RuntimeAddress::available(
346 self.normalize_int_to_i64(value, "cast_ptr_i64")?,
347 self.context,
348 )),
349 BasicValueEnum::PointerValue(value) => self
350 .builder
351 .build_ptr_to_int(value, self.context.i64_type(), "cast_ptr_value")
352 .map(|value| RuntimeAddress::available(value, self.context))
353 .map_err(|err| CodeGenError::Builder(err.to_string())),
354 _ => Err(CodeGenError::TypeError(
355 "cast source expression did not produce an address-sized value".to_string(),
356 )),
357 }
358 }
359
360 pub(super) fn cast_source_memory_address(
361 &mut self,
362 expr: &Expr,
363 ) -> Result<RuntimeAddress<'ctx>> {
364 if let Ok(address) = self.resolve_runtime_address_from_expr(expr) {
365 return Ok(address);
366 }
367
368 if let Some(plan) = self.query_dwarf_for_complex_expr(expr)? {
369 let status_ptr = if self.condition_context_active {
370 Some(self.get_or_create_cond_error_global())
371 } else {
372 None
373 };
374 let pc_address = self.get_compile_time_context()?.pc_address;
375 if let Ok(address) =
376 self.variable_read_plan_to_runtime_address(&plan, pc_address, status_ptr)
377 {
378 return Ok(address);
379 }
380 }
381
382 match self.compile_expr(expr)? {
383 BasicValueEnum::IntValue(value) => Ok(RuntimeAddress::available(
384 self.normalize_int_to_i64(value, "cast_mem_i64")?,
385 self.context,
386 )),
387 BasicValueEnum::PointerValue(value) => self
388 .builder
389 .build_ptr_to_int(value, self.context.i64_type(), "cast_mem_ptr")
390 .map(|value| RuntimeAddress::available(value, self.context))
391 .map_err(|err| CodeGenError::Builder(err.to_string())),
392 _ => Err(CodeGenError::TypeError(
393 "cast source expression is not addressable".to_string(),
394 )),
395 }
396 }
397
398 fn cast_lvalue_address_and_type(
399 &mut self,
400 expr: &Expr,
401 target_type: &str,
402 ) -> Result<DynamicLvalue<'ctx>> {
403 let target_type = self.resolve_cast_target_type(target_type)?;
404 let module_path = self
405 .current_compile_time_context
406 .as_ref()
407 .map(|context| PathBuf::from(&context.module_path));
408
409 if let Some(pointee_type) = Self::cast_pointer_target_type(&target_type) {
410 let address = self.cast_source_pointer_value(expr)?;
411 return Ok(DynamicLvalue {
412 address,
413 type_info: DynamicTypeInfo {
414 dwarf_type: pointee_type,
415 module_path,
416 },
417 });
418 }
419
420 let address = self.cast_source_memory_address(expr)?;
421 Ok(DynamicLvalue {
422 address,
423 type_info: DynamicTypeInfo {
424 dwarf_type: target_type,
425 module_path,
426 },
427 })
428 }
429
430 fn cast_index_base(
431 &mut self,
432 expr: &Expr,
433 ) -> Result<Option<(IndexableElementInfo, RuntimeAddress<'ctx>)>> {
434 let Expr::Cast {
435 expr: source_expr,
436 target_type,
437 } = expr
438 else {
439 return Ok(None);
440 };
441
442 let target_type = self.resolve_cast_target_type(target_type)?;
443 let module_path = self
444 .current_compile_time_context
445 .as_ref()
446 .map(|context| PathBuf::from(&context.module_path));
447
448 match ghostscope_dwarf::strip_type_aliases(&target_type) {
449 DwarfType::PointerType { .. } => {
450 let Some(element_info) = Self::indexable_info_from_type(&target_type, module_path)
451 else {
452 return Ok(None);
453 };
454 let base_address = self.cast_source_pointer_value(source_expr)?;
455 Ok(Some((element_info, base_address)))
456 }
457 DwarfType::ArrayType { .. } => {
458 let Some(element_info) = Self::indexable_info_from_type(&target_type, module_path)
459 else {
460 return Ok(None);
461 };
462 let base_address = self.cast_source_memory_address(source_expr)?;
463 Ok(Some((element_info, base_address)))
464 }
465 _ => Ok(None),
466 }
467 }
468
469 fn indexable_info_from_type(
470 dwarf_type: &DwarfType,
471 module_path: Option<PathBuf>,
472 ) -> Option<IndexableElementInfo> {
473 ghostscope_dwarf::indexable_element_layout(dwarf_type).map(|layout| IndexableElementInfo {
474 element_type: layout.element_type,
475 stride: layout.stride,
476 module_path,
477 })
478 }
479
480 fn compiled_pointer_value_to_runtime_address(
481 &mut self,
482 value: BasicValueEnum<'ctx>,
483 int_name: &str,
484 ptr_name: &str,
485 error_message: &'static str,
486 ) -> Result<RuntimeAddress<'ctx>> {
487 match value {
488 BasicValueEnum::IntValue(value) => Ok(RuntimeAddress::available(
489 self.normalize_int_to_i64(value, int_name)?,
490 self.context,
491 )),
492 BasicValueEnum::PointerValue(value) => self
493 .builder
494 .build_ptr_to_int(value, self.context.i64_type(), ptr_name)
495 .map(|value| RuntimeAddress::available(value, self.context))
496 .map_err(|err| CodeGenError::Builder(err.to_string())),
497 _ => Err(CodeGenError::TypeError(error_message.to_string())),
498 }
499 }
500
501 fn dynamic_lvalue_from_indexable_base(
502 &mut self,
503 element_info: IndexableElementInfo,
504 base_address: RuntimeAddress<'ctx>,
505 index_value: IntValue<'ctx>,
506 name: &str,
507 ) -> Result<DynamicLvalue<'ctx>> {
508 let stride_value = self
509 .context
510 .i64_type()
511 .const_int(element_info.stride, false);
512 let byte_offset = self
513 .builder
514 .build_int_mul(index_value, stride_value, &format!("{name}_byte_offset"))
515 .map_err(|err| CodeGenError::Builder(err.to_string()))?;
516 let element_address = self
517 .builder
518 .build_int_add(
519 base_address.value,
520 byte_offset,
521 &format!("{name}_element_address"),
522 )
523 .map_err(|err| CodeGenError::Builder(err.to_string()))?;
524
525 Ok(DynamicLvalue {
526 address: base_address.with_value(element_address),
527 type_info: DynamicTypeInfo {
528 dwarf_type: element_info.element_type,
529 module_path: element_info.module_path,
530 },
531 })
532 }
533
534 fn dynamic_lvalue_from_const_pointer_arithmetic(
535 &mut self,
536 expr: &Expr,
537 ) -> Result<Option<DynamicLvalue<'ctx>>> {
538 let Some((base_expr, index)) = self.pointer_arithmetic_parts_expanding_aliases(expr)?
539 else {
540 return Ok(None);
541 };
542 let Some((element_info, base_address)) = self.cast_index_base(&base_expr)? else {
543 return Ok(None);
544 };
545 let index_value = self.context.i64_type().const_int(index as u64, true);
546 self.dynamic_lvalue_from_indexable_base(
547 element_info,
548 base_address,
549 index_value,
550 "dynamic_cast_ptr_arith",
551 )
552 .map(Some)
553 }
554
555 fn compile_cast_integer_value(
556 &mut self,
557 expr: &Expr,
558 target_type: &DwarfType,
559 ) -> Result<IntValue<'ctx>> {
560 let value = match self.compile_expr(expr)? {
561 BasicValueEnum::IntValue(value) => value,
562 BasicValueEnum::PointerValue(value) => self
563 .builder
564 .build_ptr_to_int(value, self.context.i64_type(), "cast_int_ptr")
565 .map_err(|err| CodeGenError::Builder(err.to_string()))?,
566 _ => {
567 return Err(CodeGenError::TypeError(
568 "integer cast source must be an integer or pointer".to_string(),
569 ))
570 }
571 };
572
573 if Self::is_bool_dwarf_type(target_type) {
574 let value = self.normalize_int_to_i64(value, "cast_bool_i64")?;
575 return self
576 .builder
577 .build_int_compare(
578 inkwell::IntPredicate::NE,
579 value,
580 self.context.i64_type().const_zero(),
581 "cast_bool",
582 )
583 .map_err(|err| CodeGenError::Builder(err.to_string()));
584 }
585
586 let Some(integer_type) = ghostscope_dwarf::c_integer_comparison_type(target_type) else {
587 return Err(CodeGenError::TypeError(format!(
588 "cast target '{}' is not an integer type",
589 target_type.type_name()
590 )));
591 };
592
593 let bit_width = integer_type.size.saturating_mul(8).clamp(1, 64) as u32;
594 let target_int_type = self.context.custom_width_int_type(bit_width);
595 let current_width = value.get_type().get_bit_width();
596 let narrowed = if current_width > bit_width {
597 self.builder
598 .build_int_truncate(value, target_int_type, "cast_int_trunc")
599 .map_err(|err| CodeGenError::Builder(err.to_string()))?
600 } else if current_width < bit_width {
601 if integer_type.is_unsigned || current_width == 1 {
602 self.builder
603 .build_int_z_extend(value, target_int_type, "cast_int_zext")
604 .map_err(|err| CodeGenError::Builder(err.to_string()))?
605 } else {
606 self.builder
607 .build_int_s_extend(value, target_int_type, "cast_int_sext")
608 .map_err(|err| CodeGenError::Builder(err.to_string()))?
609 }
610 } else {
611 value
612 };
613
614 if bit_width == 64 {
615 return Ok(narrowed);
616 }
617
618 if integer_type.is_unsigned {
619 self.builder
620 .build_int_z_extend(narrowed, self.context.i64_type(), "cast_int_zext_i64")
621 .map_err(|err| CodeGenError::Builder(err.to_string()))
622 } else {
623 self.builder
624 .build_int_s_extend(narrowed, self.context.i64_type(), "cast_int_sext_i64")
625 .map_err(|err| CodeGenError::Builder(err.to_string()))
626 }
627 }
628
629 fn compile_cast_expr_value(
630 &mut self,
631 expr: &Expr,
632 target_type: &str,
633 ) -> Result<BasicValueEnum<'ctx>> {
634 let target_type = self.resolve_cast_target_type(target_type)?;
635
636 if Self::cast_pointer_target_type(&target_type).is_some() {
637 let address = self.cast_source_pointer_value(expr)?;
638 let ptr_ty = self.context.ptr_type(AddressSpace::default());
639 return self
640 .builder
641 .build_int_to_ptr(address.value, ptr_ty, "cast_as_ptr")
642 .map(|value| value.into())
643 .map_err(|err| CodeGenError::Builder(err.to_string()));
644 }
645
646 if ghostscope_dwarf::is_c_aggregate_type(&target_type) {
647 let address = self.cast_source_memory_address(expr)?;
648 let ptr_ty = self.context.ptr_type(AddressSpace::default());
649 return self
650 .builder
651 .build_int_to_ptr(address.value, ptr_ty, "cast_aggregate_ptr")
652 .map(|value| value.into())
653 .map_err(|err| CodeGenError::Builder(err.to_string()));
654 }
655
656 if ghostscope_dwarf::c_integer_comparison_type(&target_type).is_some() {
657 return self
658 .compile_cast_integer_value(expr, &target_type)
659 .map(|value| value.into());
660 }
661
662 if Self::is_float_dwarf_type(&target_type) {
663 return Err(CodeGenError::TypeError(
664 "floating-point casts are only supported for memory reads/printing".to_string(),
665 ));
666 }
667
668 Err(CodeGenError::TypeError(format!(
669 "cast target '{}' is not supported as a value expression",
670 target_type.type_name()
671 )))
672 }
673
674 pub(crate) fn integer_literal_value(expr: &Expr) -> Option<i64> {
675 use crate::script::ast::BinaryOp as BO;
676 use crate::script::ast::Expr as E;
677
678 match expr {
679 E::Int(value) => Some(*value),
680 E::BinaryOp {
681 left,
682 op: BO::Add,
683 right,
684 } => {
685 Self::integer_literal_value(left)?.checked_add(Self::integer_literal_value(right)?)
686 }
687 E::BinaryOp {
688 left,
689 op: BO::Subtract,
690 right,
691 } => {
692 Self::integer_literal_value(left)?.checked_sub(Self::integer_literal_value(right)?)
693 }
694 E::BinaryOp {
695 left,
696 op: BO::Multiply,
697 right,
698 } => {
699 Self::integer_literal_value(left)?.checked_mul(Self::integer_literal_value(right)?)
700 }
701 E::BinaryOp {
702 left,
703 op: BO::Divide,
704 right,
705 } => {
706 Self::integer_literal_value(left)?.checked_div(Self::integer_literal_value(right)?)
707 }
708 E::BinaryOp {
709 left,
710 op: BO::Modulo,
711 right,
712 } => {
713 Self::integer_literal_value(left)?.checked_rem(Self::integer_literal_value(right)?)
714 }
715 E::BinaryOp {
716 left,
717 op: BO::BitAnd,
718 right,
719 } => Some(Self::integer_literal_value(left)? & Self::integer_literal_value(right)?),
720 E::BinaryOp {
721 left,
722 op: BO::BitXor,
723 right,
724 } => Some(Self::integer_literal_value(left)? ^ Self::integer_literal_value(right)?),
725 E::BinaryOp {
726 left,
727 op: BO::BitOr,
728 right,
729 } => Some(Self::integer_literal_value(left)? | Self::integer_literal_value(right)?),
730 E::BinaryOp {
731 left,
732 op: BO::ShiftLeft,
733 right,
734 } => {
735 let shift = u32::try_from(Self::integer_literal_value(right)?).ok()?;
736 Self::integer_literal_value(left)?.checked_shl(shift)
737 }
738 E::BinaryOp {
739 left,
740 op: BO::ShiftRight,
741 right,
742 } => {
743 let shift = u32::try_from(Self::integer_literal_value(right)?).ok()?;
744 Self::integer_literal_value(left)?.checked_shr(shift)
745 }
746 E::UnaryBitNot(inner) => Some(!Self::integer_literal_value(inner)?),
747 _ => None,
748 }
749 }
750
751 pub(crate) fn pointer_arithmetic_parts(expr: &Expr) -> Option<(&Expr, i64)> {
752 use crate::script::ast::Expr as E;
753
754 fn collect_offset(expr: &Expr, acc: i64) -> Option<(&Expr, i64)> {
755 use crate::script::ast::BinaryOp as BO;
756 use crate::script::ast::Expr as E;
757
758 match expr {
759 E::BinaryOp {
760 left,
761 op: BO::Add,
762 right,
763 } => match (&**left, &**right) {
764 (ptr_side, int_expr)
765 if EbpfContext::<'static, 'static>::integer_literal_value(int_expr)
766 .is_some() =>
767 {
768 let index =
769 EbpfContext::<'static, 'static>::integer_literal_value(int_expr)?;
770 collect_offset(ptr_side, acc.checked_add(index)?)
771 }
772 (int_expr, ptr_side)
773 if EbpfContext::<'static, 'static>::integer_literal_value(int_expr)
774 .is_some() =>
775 {
776 let index =
777 EbpfContext::<'static, 'static>::integer_literal_value(int_expr)?;
778 collect_offset(ptr_side, acc.checked_add(index)?)
779 }
780 _ => Some((expr, acc)),
781 },
782 E::BinaryOp {
783 left,
784 op: BO::Subtract,
785 right,
786 } => match &**right {
787 int_expr
788 if EbpfContext::<'static, 'static>::integer_literal_value(int_expr)
789 .is_some() =>
790 {
791 let index =
792 EbpfContext::<'static, 'static>::integer_literal_value(int_expr)?;
793 collect_offset(left, acc.checked_sub(index)?)
794 }
795 _ => Some((expr, acc)),
796 },
797 _ => Some((expr, acc)),
798 }
799 }
800
801 let E::BinaryOp { .. } = expr else {
802 return None;
803 };
804
805 let (base, index) = collect_offset(expr, 0)?;
806 match base {
807 E::BinaryOp { .. } => None,
808 _ => Some((base, index)),
809 }
810 }
811
812 pub(crate) fn pointer_arithmetic_parts_expanding_aliases(
813 &self,
814 expr: &Expr,
815 ) -> Result<Option<(Expr, i64)>> {
816 let Some((base, index)) = Self::pointer_arithmetic_parts(expr) else {
817 return Ok(None);
818 };
819
820 let mut base = base.clone();
821 let mut index = index;
822 let mut visited = std::collections::HashSet::new();
823
824 loop {
825 let Expr::Variable(name) = &base else {
826 break;
827 };
828 if !self.alias_variable_exists(name) {
829 break;
830 }
831 if !visited.insert(name.clone()) {
832 return Err(CodeGenError::TypeError(format!(
833 "alias cycle detected for '{name}'"
834 )));
835 }
836 let Some(target) = self.get_alias_variable(name) else {
837 break;
838 };
839 if let Some((alias_base, alias_index)) = Self::pointer_arithmetic_parts(&target) {
840 index = alias_index.checked_add(index).ok_or_else(|| {
841 CodeGenError::TypeError("pointer arithmetic offset overflow".to_string())
842 })?;
843 base = alias_base.clone();
844 } else {
845 base = target;
846 }
847 }
848
849 Ok(Some((base, index)))
850 }
851
852 fn is_dwarf_pointer_or_array_arg(&mut self, expr: &Expr) -> Result<bool> {
853 let Some(var) = self.query_dwarf_for_complex_expr(expr)? else {
854 return Ok(false);
855 };
856 let Some(ty) = var.dwarf_type.as_ref() else {
857 return Ok(false);
858 };
859 let ty = ghostscope_dwarf::strip_type_aliases(ty);
860 Ok(matches!(
861 ty,
862 DwarfType::PointerType { .. } | DwarfType::ArrayType { .. }
863 ))
864 }
865
866 fn dwarf_integer_comparison_expr(&mut self, expr: &Expr) -> Option<CIntegerComparisonType> {
867 if let Expr::Cast { target_type, .. } = expr {
868 return self
869 .resolve_cast_target_type(target_type)
870 .ok()
871 .and_then(|ty| ghostscope_dwarf::c_integer_comparison_type(&ty));
872 }
873
874 if let Ok(Some(var)) = self.query_dwarf_for_complex_expr(expr) {
875 if let Some(ref ty) = var.dwarf_type {
876 return ghostscope_dwarf::c_integer_comparison_type(ty);
877 }
878 }
879 None
880 }
881
882 fn integer_comparison_plan_for_exprs(
883 &mut self,
884 left: &Expr,
885 right: &Expr,
886 ) -> Option<CIntegerComparisonPlan> {
887 let left_ty = self.dwarf_integer_comparison_expr(left);
888 let right_ty = self.dwarf_integer_comparison_expr(right);
889 if left_ty.is_none() && right_ty.is_none() {
890 return None;
891 }
892
893 Some(ghostscope_dwarf::usual_c_arithmetic_comparison_plan(
894 left_ty.unwrap_or_else(CIntegerComparisonType::signed_i64),
895 right_ty.unwrap_or_else(CIntegerComparisonType::signed_i64),
896 ))
897 }
898
899 pub(super) fn unsigned_ordering_width_for_exprs(
900 &mut self,
901 left: &Expr,
902 right: &Expr,
903 ) -> Option<u32> {
904 let plan = self.integer_comparison_plan_for_exprs(left, right)?;
905 if plan.is_unsigned {
906 Some((plan.size * 8) as u32)
907 } else {
908 None
909 }
910 }
911
912 pub(super) fn unsigned_shift_width_for_expr(&mut self, expr: &Expr) -> Option<u32> {
913 let c_type = self.dwarf_integer_comparison_expr(expr)?.promoted();
914 if c_type.is_unsigned {
915 Some((c_type.size * 8) as u32)
916 } else {
917 None
918 }
919 }
920
921 fn ensure_dwarf_pointer_arg(&mut self, e: &Expr, where_ctx: &str) -> Result<()> {
924 if matches!(e, Expr::AddressOf(_)) {
926 return Ok(());
927 }
928 if let Some((ptr_side, _)) = self.pointer_arithmetic_parts_expanding_aliases(e)? {
929 if matches!(&ptr_side, Expr::AddressOf(_))
930 || self
931 .is_dwarf_pointer_or_array_arg(&ptr_side)
932 .unwrap_or(false)
933 {
934 return Ok(());
935 }
936 }
937 if self.is_dynamic_pointer_arithmetic_expr(e)?
938 || self.expands_to_nonliteral_pointer_arithmetic(e)?
939 {
940 return Ok(());
941 }
942 match self.query_dwarf_for_complex_expr(e) {
943 Ok(Some(var)) => {
944 let Some(ty) = var.dwarf_type.as_ref() else {
945 return Err(CodeGenError::TypeError(format!(
946 "{where_ctx}: DWARF variable has no type information"
947 )));
948 };
949 let ty = ghostscope_dwarf::strip_type_aliases(ty);
950 if !matches!(
951 ty,
952 DwarfType::PointerType { .. } | DwarfType::ArrayType { .. }
953 ) {
954 return Err(CodeGenError::TypeError(format!(
955 "{where_ctx}: only pointer or array DWARF variables are supported"
956 )));
957 }
958 Ok(())
959 }
960 Ok(None) | Err(_) => match self.compile_expr(e) {
962 Ok(BasicValueEnum::PointerValue(_)) => Ok(()),
963 _ => Err(CodeGenError::TypeError(format!(
964 "{where_ctx}: expression is not a pointer"
965 ))),
966 },
967 }
968 }
969
970 fn is_dynamic_pointer_arithmetic_expr(&mut self, expr: &Expr) -> Result<bool> {
971 use crate::script::ast::BinaryOp as BO;
972 use crate::script::ast::Expr as E;
973
974 let E::BinaryOp { left, op, right } = expr else {
975 return Ok(false);
976 };
977
978 match op {
979 BO::Add => Ok(self.is_dynamic_indexable_pointer_base(left)?
980 || self.is_dynamic_indexable_pointer_base(right)?),
981 BO::Subtract => self.is_dynamic_indexable_pointer_base(left),
982 _ => Ok(false),
983 }
984 }
985
986 fn is_dynamic_indexable_pointer_base(&mut self, expr: &Expr) -> Result<bool> {
987 if matches!(expr, Expr::AddressOf(_)) {
988 return Ok(true);
989 }
990
991 if self.cast_index_base(expr)?.is_some() {
992 return Ok(true);
993 }
994
995 if self
996 .query_dwarf_for_complex_expr(expr)
997 .ok()
998 .flatten()
999 .and_then(|var| var.dwarf_type)
1000 .is_some_and(|ty| ghostscope_dwarf::is_c_pointer_or_array_type(&ty))
1001 {
1002 return Ok(true);
1003 }
1004
1005 let expanded = self.expand_alias_variable_expr(expr)?;
1006 if matches!(expanded, Expr::AddressOf(_)) {
1007 return Ok(true);
1008 }
1009 let Some((base_expr, _static_index)) =
1010 self.pointer_arithmetic_parts_expanding_aliases(&expanded)?
1011 else {
1012 return Ok(false);
1013 };
1014
1015 Ok(self
1016 .query_dwarf_for_complex_expr(&base_expr)
1017 .ok()
1018 .flatten()
1019 .and_then(|var| var.dwarf_type)
1020 .is_some_and(|ty| ghostscope_dwarf::is_c_pointer_or_array_type(&ty)))
1021 }
1022
1023 fn expands_to_nonliteral_pointer_arithmetic(&mut self, expr: &Expr) -> Result<bool> {
1024 let expanded = self.expand_alias_variable_expr(expr)?;
1025 self.is_nonliteral_pointer_arithmetic_expr(&expanded)
1026 }
1027
1028 fn is_nonliteral_pointer_arithmetic_expr(&mut self, expr: &Expr) -> Result<bool> {
1029 use crate::script::ast::BinaryOp as BO;
1030 use crate::script::ast::Expr as E;
1031
1032 let E::BinaryOp { left, op, right } = expr else {
1033 return Ok(false);
1034 };
1035
1036 match op {
1037 BO::Add => {
1038 let left_is_ptr = self.is_dynamic_indexable_pointer_base(left)?;
1039 let right_is_ptr = self.is_dynamic_indexable_pointer_base(right)?;
1040 let left_is_literal = Self::integer_literal_value(left).is_some();
1041 let right_is_literal = Self::integer_literal_value(right).is_some();
1042
1043 if (left_is_ptr && !right_is_ptr && !right_is_literal)
1044 || (right_is_ptr && !left_is_ptr && !left_is_literal)
1045 {
1046 return Ok(true);
1047 }
1048
1049 Ok(self.expands_to_nonliteral_pointer_arithmetic(left)?
1050 || self.expands_to_nonliteral_pointer_arithmetic(right)?)
1051 }
1052 BO::Subtract => {
1053 let left_is_ptr = self.is_dynamic_indexable_pointer_base(left)?;
1054 let right_is_literal = Self::integer_literal_value(right).is_some();
1055
1056 if left_is_ptr && !right_is_literal {
1057 return Ok(true);
1058 }
1059
1060 self.expands_to_nonliteral_pointer_arithmetic(left)
1061 }
1062 _ => Ok(false),
1063 }
1064 }
1065
1066 pub(crate) fn resolve_ptr_i64_from_expr(
1069 &mut self,
1070 e: &Expr,
1071 ) -> Result<inkwell::values::IntValue<'ctx>> {
1072 self.resolve_runtime_address_from_expr(e)
1073 .map(|address| address.value)
1074 }
1075
1076 pub(crate) fn resolve_runtime_address_from_expr(
1077 &mut self,
1078 e: &Expr,
1079 ) -> Result<RuntimeAddress<'ctx>> {
1080 let mut visited = std::collections::HashSet::new();
1081 self.resolve_runtime_address_from_expr_internal(e, &mut visited, 0)
1082 }
1083
1084 fn resolve_runtime_address_from_expr_internal(
1085 &mut self,
1086 e: &Expr,
1087 visited: &mut std::collections::HashSet<String>,
1088 depth: usize,
1089 ) -> Result<RuntimeAddress<'ctx>> {
1090 use crate::script::ast::BinaryOp as BO;
1091 use crate::script::ast::Expr as E;
1092 use inkwell::values::BasicValueEnum::*;
1093 const MAX_DEPTH: usize = 64;
1094 if depth > MAX_DEPTH {
1095 return Err(CodeGenError::TypeError(
1096 "alias expansion depth exceeded (cycle?)".into(),
1097 ));
1098 }
1099 if let E::Cast { expr, target_type } = e {
1100 let target_type_info = self.resolve_cast_target_type(target_type)?;
1101 if Self::cast_pointer_target_type(&target_type_info).is_some() {
1102 return self.cast_source_pointer_value(expr);
1103 }
1104 return self.cast_source_memory_address(expr);
1105 }
1106 if let E::Variable(name) = e {
1108 if self.alias_variable_exists(name) {
1109 if !visited.insert(name.clone()) {
1110 return Err(CodeGenError::TypeError(format!(
1111 "alias cycle detected for '{name}'"
1112 )));
1113 }
1114 if let Some(target) = self.get_alias_variable(name) {
1115 let r = self.resolve_runtime_address_from_expr_internal(
1116 &target,
1117 visited,
1118 depth + 1,
1119 );
1120 visited.remove(name);
1121 return r;
1122 }
1123 }
1124 }
1125 if let E::AddressOf(inner) = e {
1127 let resolved_inner: &E = if let E::Variable(name) = inner.as_ref() {
1129 if self.alias_variable_exists(name) {
1130 if let Some(target) = self.get_alias_variable(name) {
1132 if let Some(var) = self.query_dwarf_for_complex_expr(&target)? {
1133 let status_ptr = if self.condition_context_active {
1134 Some(self.get_or_create_cond_error_global())
1135 } else {
1136 None
1137 };
1138 let pc_address = self.get_compile_time_context()?.pc_address;
1139 return self.variable_read_plan_to_runtime_address(
1140 &var, pc_address, status_ptr,
1141 );
1142 } else {
1143 return Err(CodeGenError::TypeError(
1144 "cannot take address of unresolved expression".into(),
1145 ));
1146 }
1147 } else {
1148 return Err(CodeGenError::TypeError(
1149 "cannot take address of unresolved expression".into(),
1150 ));
1151 }
1152 } else {
1153 inner.as_ref()
1154 }
1155 } else {
1156 inner.as_ref()
1157 };
1158
1159 if let E::ArrayAccess(array_expr, index_expr) = resolved_inner {
1160 if let Some(element_lvalue) =
1161 self.compile_dynamic_array_element_address(array_expr, index_expr)?
1162 {
1163 return Ok(element_lvalue.address);
1164 }
1165 }
1166
1167 if let Some(lvalue) = self.dynamic_lvalue_address_and_type(resolved_inner)? {
1168 return Ok(lvalue.address);
1169 }
1170
1171 if let Some(var) = self.query_dwarf_for_complex_expr(resolved_inner)? {
1172 let status_ptr = if self.condition_context_active {
1173 Some(self.get_or_create_cond_error_global())
1174 } else {
1175 None
1176 };
1177 let pc_address = self.get_compile_time_context()?.pc_address;
1178 return self.variable_read_plan_to_runtime_address(&var, pc_address, status_ptr);
1179 } else {
1180 return Err(CodeGenError::TypeError(
1181 "cannot take address of unresolved expression".into(),
1182 ));
1183 }
1184 }
1185
1186 if let Some(address) = self.dynamic_pointer_arithmetic_address(e)? {
1187 return Ok(address);
1188 }
1189
1190 if let Some((ptr_side, index)) = self.pointer_arithmetic_parts_expanding_aliases(e)? {
1191 if matches!(&ptr_side, E::AddressOf(_)) {
1192 let base =
1193 self.resolve_runtime_address_from_expr_internal(&ptr_side, visited, depth + 1)?;
1194 let off = self.context.i64_type().const_int(index as u64, false);
1195 let value = self
1196 .builder
1197 .build_int_add(base.value, off, "ptr_add")
1198 .map_err(|err| CodeGenError::Builder(err.to_string()))?;
1199 return Ok(base.with_value(value));
1200 } else if let Some((element_info, base_address)) = self.cast_index_base(&ptr_side)? {
1201 let index_value = self.context.i64_type().const_int(index as u64, true);
1202 return self
1203 .dynamic_lvalue_from_indexable_base(
1204 element_info,
1205 base_address,
1206 index_value,
1207 "cast_ptr_add",
1208 )
1209 .map(|lvalue| lvalue.address);
1210 } else if let Some(var) = self.query_dwarf_for_complex_expr(&ptr_side)? {
1211 if var.dwarf_type.is_some() {
1212 let pointed_plan = var
1213 .plan_pointer_element_index(index)
1214 .map_err(|err| CodeGenError::DwarfError(err.to_string()))?;
1215 let status_ptr = if self.condition_context_active {
1216 Some(self.get_or_create_cond_error_global())
1217 } else {
1218 None
1219 };
1220 let pc_address = self.get_compile_time_context()?.pc_address;
1221 return self.variable_read_plan_to_runtime_address(
1222 &pointed_plan,
1223 pc_address,
1224 status_ptr,
1225 );
1226 }
1227 }
1228 }
1229
1230 if let E::BinaryOp { left, op, right } = e {
1232 if matches!(op, BO::Add) {
1233 if let Some(k) = Self::integer_literal_value(right) {
1235 if let Ok(base) =
1236 self.resolve_runtime_address_from_expr_internal(left, visited, depth + 1)
1237 {
1238 let off = self.context.i64_type().const_int(k as u64, false);
1239 let value = self
1240 .builder
1241 .build_int_add(base.value, off, "ptr_add")
1242 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1243 return Ok(base.with_value(value));
1244 }
1245 }
1246 if let Some(k) = Self::integer_literal_value(left) {
1248 if let Ok(base) =
1249 self.resolve_runtime_address_from_expr_internal(right, visited, depth + 1)
1250 {
1251 let off = self.context.i64_type().const_int(k as u64, false);
1252 let value = self
1253 .builder
1254 .build_int_add(base.value, off, "ptr_add")
1255 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1256 return Ok(base.with_value(value));
1257 }
1258 }
1259 } else if matches!(op, BO::Subtract) {
1260 if let Some(k) = Self::integer_literal_value(right) {
1261 if let Ok(base) =
1262 self.resolve_runtime_address_from_expr_internal(left, visited, depth + 1)
1263 {
1264 let off = self
1265 .context
1266 .i64_type()
1267 .const_int(k.wrapping_neg() as u64, false);
1268 let value = self
1269 .builder
1270 .build_int_add(base.value, off, "ptr_sub")
1271 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1272 return Ok(base.with_value(value));
1273 }
1274 }
1275 }
1276 }
1277 if let Ok(Some(var)) = self.query_dwarf_for_complex_expr(e) {
1280 if let Some(dty) = var.dwarf_type.as_ref() {
1281 let dty = ghostscope_dwarf::strip_type_aliases(dty);
1282 match dty {
1283 DwarfType::PointerType { .. } => {
1284 let pc_address = self.get_compile_time_context()?.pc_address;
1285 let val_any =
1286 self.variable_read_plan_to_llvm_value(&var, pc_address, None)?;
1287 match val_any {
1288 IntValue(iv) => Ok(RuntimeAddress::available(iv, self.context)),
1289 PointerValue(pv) => self
1290 .builder
1291 .build_ptr_to_int(pv, self.context.i64_type(), "ptr_as_i64")
1292 .map(|value| RuntimeAddress::available(value, self.context))
1293 .map_err(|e| CodeGenError::Builder(e.to_string())),
1294 _ => Err(CodeGenError::TypeError(
1295 "DWARF value is not pointer/integer".into(),
1296 )),
1297 }
1298 }
1299 DwarfType::ArrayType { .. } => {
1300 let status_ptr = if self.condition_context_active {
1302 Some(self.get_or_create_cond_error_global())
1303 } else {
1304 None
1305 };
1306 let pc_address = self.get_compile_time_context()?.pc_address;
1307 self.variable_read_plan_to_runtime_address(&var, pc_address, status_ptr)
1308 }
1309 _ => Err(CodeGenError::TypeError(
1310 "DWARF value is not pointer/array".into(),
1311 )),
1312 }
1313 } else {
1314 let status_ptr = if self.condition_context_active {
1315 Some(self.get_or_create_cond_error_global())
1316 } else {
1317 None
1318 };
1319 let pc_address = self.get_compile_time_context()?.pc_address;
1320 self.variable_read_plan_to_runtime_address(&var, pc_address, status_ptr)
1321 }
1322 } else {
1323 Err(CodeGenError::TypeError(
1325 "expression is not a pointer/address".into(),
1326 ))
1327 }
1328 }
1329
1330 fn dynamic_pointer_arithmetic_address(
1331 &mut self,
1332 expr: &Expr,
1333 ) -> Result<Option<RuntimeAddress<'ctx>>> {
1334 use crate::script::ast::BinaryOp as BO;
1335 use crate::script::ast::Expr as E;
1336
1337 let E::BinaryOp { left, op, right } = expr else {
1338 return Ok(None);
1339 };
1340
1341 match op {
1342 BO::Add => {
1343 if let Some(address) = self.dynamic_raw_address_candidate(left, right, false)? {
1344 return Ok(Some(address));
1345 }
1346 if let Some(address) = self.dynamic_raw_address_candidate(right, left, false)? {
1347 return Ok(Some(address));
1348 }
1349 if let Some(address) = self.dynamic_index_address_candidate(left, right)? {
1350 return Ok(Some(address));
1351 }
1352 self.dynamic_index_address_candidate(right, left)
1353 }
1354 BO::Subtract => {
1355 if let Some(address) = self.dynamic_raw_address_candidate(left, right, true)? {
1356 return Ok(Some(address));
1357 }
1358 let negative_right = E::BinaryOp {
1359 left: Box::new(E::Int(0)),
1360 op: BO::Subtract,
1361 right: right.clone(),
1362 };
1363 self.dynamic_index_address_candidate(left, &negative_right)
1364 }
1365 _ => Ok(None),
1366 }
1367 }
1368
1369 fn dynamic_raw_address_candidate(
1370 &mut self,
1371 base_expr: &Expr,
1372 offset_expr: &Expr,
1373 subtract: bool,
1374 ) -> Result<Option<RuntimeAddress<'ctx>>> {
1375 if Self::integer_literal_value(offset_expr).is_some() {
1376 return Ok(None);
1377 }
1378
1379 let expanded_base = self.expand_alias_variable_expr(base_expr)?;
1380 if !matches!(expanded_base, Expr::AddressOf(_)) {
1381 return Ok(None);
1382 }
1383
1384 let base_address = self.resolve_runtime_address_from_expr(&expanded_base)?;
1385 let offset = match self.compile_expr(offset_expr)? {
1386 BasicValueEnum::IntValue(value) => {
1387 self.normalize_int_to_i64(value, "dynamic_raw_offset_i64")?
1388 }
1389 _ => {
1390 return Err(CodeGenError::TypeError(
1391 "raw address offset expression must compile to an integer".to_string(),
1392 ))
1393 }
1394 };
1395 let offset = if subtract {
1396 self.builder
1397 .build_int_neg(offset, "dynamic_raw_offset_neg")
1398 .map_err(|err| CodeGenError::Builder(err.to_string()))?
1399 } else {
1400 offset
1401 };
1402 let address = self
1403 .builder
1404 .build_int_add(base_address.value, offset, "dynamic_raw_address")
1405 .map_err(|err| CodeGenError::Builder(err.to_string()))?;
1406 Ok(Some(base_address.with_value(address)))
1407 }
1408
1409 fn dynamic_index_address_candidate(
1410 &mut self,
1411 base_expr: &Expr,
1412 index_expr: &Expr,
1413 ) -> Result<Option<RuntimeAddress<'ctx>>> {
1414 match self.compile_dynamic_array_element_address(base_expr, index_expr) {
1415 Ok(Some(element_lvalue)) => Ok(Some(element_lvalue.address)),
1416 Ok(None) => Ok(None),
1417 Err(CodeGenError::VariableNotFound(_))
1418 | Err(CodeGenError::VariableNotInScope(_))
1419 | Err(CodeGenError::TypeError(_)) => Ok(None),
1420 Err(err) => Err(err),
1421 }
1422 }
1423
1424 fn compile_memcmp_builtin(
1427 &mut self,
1428 a_expr: &Expr,
1429 b_expr: &Expr,
1430 len_expr: &Expr,
1431 ) -> Result<BasicValueEnum<'ctx>> {
1432 let len_val = self.compile_expr(len_expr)?;
1438 let len_iv = match len_val {
1439 BasicValueEnum::IntValue(iv) => iv,
1440 _ => {
1441 return Err(CodeGenError::TypeError(
1442 "memcmp length must be an integer expression".into(),
1443 ))
1444 }
1445 };
1446 let i32_ty = self.context.i32_type();
1447 let len_i32 = if len_iv.get_type().get_bit_width() > 32 {
1448 self.builder
1449 .build_int_truncate(len_iv, i32_ty, "memcmp_len_trunc")
1450 .map_err(|e| CodeGenError::Builder(e.to_string()))?
1451 } else if len_iv.get_type().get_bit_width() < 32 {
1452 self.builder
1453 .build_int_z_extend(len_iv, i32_ty, "memcmp_len_zext")
1454 .map_err(|e| CodeGenError::Builder(e.to_string()))?
1455 } else {
1456 len_iv
1457 };
1458 let zero_i32 = i32_ty.const_zero();
1459 let is_neg = self
1460 .builder
1461 .build_int_compare(
1462 inkwell::IntPredicate::SLT,
1463 len_i32,
1464 zero_i32,
1465 "memcmp_len_neg",
1466 )
1467 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1468 let len_nn = self
1469 .builder
1470 .build_select(is_neg, zero_i32, len_i32, "memcmp_len_nn")
1471 .map_err(|e| CodeGenError::Builder(e.to_string()))?
1472 .into_int_value();
1473 let cap = self.compile_options.compare_cap;
1474 let cap_const = i32_ty.const_int(cap as u64, false);
1475 let gt = self
1476 .builder
1477 .build_int_compare(
1478 inkwell::IntPredicate::UGT,
1479 len_nn,
1480 cap_const,
1481 "memcmp_len_gt",
1482 )
1483 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1484 let sel_len = self
1485 .builder
1486 .build_select(gt, cap_const, len_nn, "memcmp_len_sel")
1487 .map_err(|e| CodeGenError::Builder(e.to_string()))?
1488 .into_int_value();
1489
1490 let len_is_zero = self
1492 .builder
1493 .build_int_compare(
1494 inkwell::IntPredicate::EQ,
1495 sel_len,
1496 i32_ty.const_zero(),
1497 "memcmp_len_is_zero",
1498 )
1499 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1500 let func = self.current_function("compile memcmp length branch")?;
1501 let zero_b = self.context.append_basic_block(func, "memcmp_len_zero");
1502 let nz_b = self.context.append_basic_block(func, "memcmp_len_nz");
1503 let cont_b = self.context.append_basic_block(func, "memcmp_len_cont");
1504 self.builder
1505 .build_conditional_branch(len_is_zero, zero_b, nz_b)
1506 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1507
1508 self.builder.position_at_end(zero_b);
1510 let bool_true = self.context.bool_type().const_int(1, false);
1511 self.builder
1512 .build_unconditional_branch(cont_b)
1513 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1514 let zero_block = self.current_insert_block("finish memcmp zero-length block")?;
1515
1516 self.builder.position_at_end(nz_b);
1518
1519 let (arr_a_ty, buf_a) = self.get_or_create_i8_buffer(cap, "_gs_bi_memcmp_a");
1521 let (arr_b_ty, buf_b) = self.get_or_create_i8_buffer(cap, "_gs_bi_memcmp_b");
1522 let ptr_ty = self.context.ptr_type(AddressSpace::default());
1523
1524 let parse_hex_bytes = |e: &Expr| -> Option<Vec<u8>> {
1526 if let Expr::BuiltinCall { name, args } = e {
1527 if name == "hex" && args.len() == 1 {
1528 if let Expr::String(s) = &args[0] {
1529 if s.is_empty() {
1531 return Some(Vec::new());
1532 }
1533 let mut out = Vec::with_capacity(s.len() / 2);
1534 let mut i = 0usize;
1535 while i + 1 < s.len() {
1536 let v = u8::from_str_radix(&s[i..i + 2], 16).ok()?;
1537 out.push(v);
1538 i += 2;
1539 }
1540 return Some(out);
1541 }
1542 }
1543 }
1544 None
1545 };
1546
1547 if parse_hex_bytes(a_expr).is_none() {
1550 self.ensure_dwarf_pointer_arg(a_expr, "memcmp arg0")?;
1551 }
1552 let ok_a = if let Some(bytes) = parse_hex_bytes(a_expr) {
1553 let i32_ty = self.context.i32_type();
1554 let idx0 = i32_ty.const_zero();
1555 for i in 0..(cap as usize) {
1556 let idx_i = i32_ty.const_int(i as u64, false);
1557 let pa = unsafe {
1559 self.builder
1560 .build_gep(arr_a_ty, buf_a, &[idx0, idx_i], &format!("hex_a_i{i}"))
1561 .map_err(|e| CodeGenError::Builder(e.to_string()))?
1562 };
1563 let byte = if i < bytes.len() { bytes[i] } else { 0 } as u64;
1564 let bv = self.context.i8_type().const_int(byte, false);
1565 self.builder
1566 .build_store(pa, bv)
1567 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1568 }
1569 self.context.bool_type().const_int(1, false)
1570 } else {
1571 let ptr_a = self.resolve_runtime_address_from_expr(a_expr)?;
1573 let offsets_found_a = ptr_a.offsets_found;
1574 let dst_a = self
1575 .builder
1576 .build_bit_cast(buf_a, ptr_ty, "memcmp_dst_a")
1577 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1578 let base_src_a = self
1579 .builder
1580 .build_int_to_ptr(ptr_a.value, ptr_ty, "memcmp_src_a")
1581 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1582 let null_ptr = ptr_ty.const_null();
1583 let src_a = self
1584 .builder
1585 .build_select::<BasicValueEnum<'ctx>, _>(
1586 offsets_found_a,
1587 base_src_a.into(),
1588 null_ptr.into(),
1589 "memcmp_src_a_or_null",
1590 )
1591 .map_err(|e| CodeGenError::Builder(e.to_string()))?
1592 .into_pointer_value();
1593 let zero_i32 = self.context.i32_type().const_zero();
1594 let effective_len_a = self
1595 .builder
1596 .build_select::<BasicValueEnum<'ctx>, _>(
1597 offsets_found_a,
1598 sel_len.into(),
1599 zero_i32.into(),
1600 "memcmp_len_a_or_zero",
1601 )
1602 .map_err(|e| CodeGenError::Builder(e.to_string()))?
1603 .into_int_value();
1604 let ret_a = self
1605 .create_bpf_helper_call(
1606 BPF_FUNC_probe_read_user as u64,
1607 &[dst_a, effective_len_a.into(), src_a.into()],
1608 self.context.i64_type().into(),
1609 "probe_read_user_memcmp_a",
1610 )?
1611 .into_int_value();
1612 let i64_ty = self.context.i64_type();
1613 let eq_a = self
1614 .builder
1615 .build_int_compare(
1616 inkwell::IntPredicate::EQ,
1617 ret_a,
1618 i64_ty.const_zero(),
1619 "memcmp_ok_a",
1620 )
1621 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1622 self.builder
1623 .build_and(eq_a, offsets_found_a, "memcmp_ok_a")
1624 .map_err(|e| CodeGenError::Builder(e.to_string()))?
1625 };
1626
1627 if parse_hex_bytes(b_expr).is_none() {
1629 self.ensure_dwarf_pointer_arg(b_expr, "memcmp arg1")?;
1630 }
1631 let ok_b = if let Some(bytes) = parse_hex_bytes(b_expr) {
1632 let i32_ty = self.context.i32_type();
1633 let idx0 = i32_ty.const_zero();
1634 for i in 0..(cap as usize) {
1635 let idx_i = i32_ty.const_int(i as u64, false);
1636 let pb = unsafe {
1638 self.builder
1639 .build_gep(arr_b_ty, buf_b, &[idx0, idx_i], &format!("hex_b_i{i}"))
1640 .map_err(|e| CodeGenError::Builder(e.to_string()))?
1641 };
1642 let byte = if i < bytes.len() { bytes[i] } else { 0 } as u64;
1643 let bv = self.context.i8_type().const_int(byte, false);
1644 self.builder
1645 .build_store(pb, bv)
1646 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1647 }
1648 self.context.bool_type().const_int(1, false)
1649 } else {
1650 let ptr_b = self.resolve_runtime_address_from_expr(b_expr)?;
1652 let offsets_found_b = ptr_b.offsets_found;
1653 let dst_b = self
1654 .builder
1655 .build_bit_cast(buf_b, ptr_ty, "memcmp_dst_b")
1656 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1657 let base_src_b = self
1658 .builder
1659 .build_int_to_ptr(ptr_b.value, ptr_ty, "memcmp_src_b")
1660 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1661 let null_ptr = ptr_ty.const_null();
1662 let src_b = self
1663 .builder
1664 .build_select::<BasicValueEnum<'ctx>, _>(
1665 offsets_found_b,
1666 base_src_b.into(),
1667 null_ptr.into(),
1668 "memcmp_src_b_or_null",
1669 )
1670 .map_err(|e| CodeGenError::Builder(e.to_string()))?
1671 .into_pointer_value();
1672 let zero_i32 = self.context.i32_type().const_zero();
1673 let effective_len_b = self
1674 .builder
1675 .build_select::<BasicValueEnum<'ctx>, _>(
1676 offsets_found_b,
1677 sel_len.into(),
1678 zero_i32.into(),
1679 "memcmp_len_b_or_zero",
1680 )
1681 .map_err(|e| CodeGenError::Builder(e.to_string()))?
1682 .into_int_value();
1683 let ret_b = self
1684 .create_bpf_helper_call(
1685 BPF_FUNC_probe_read_user as u64,
1686 &[dst_b, effective_len_b.into(), src_b.into()],
1687 self.context.i64_type().into(),
1688 "probe_read_user_memcmp_b",
1689 )?
1690 .into_int_value();
1691 let i64_ty = self.context.i64_type();
1692 let eq_b = self
1693 .builder
1694 .build_int_compare(
1695 inkwell::IntPredicate::EQ,
1696 ret_b,
1697 i64_ty.const_zero(),
1698 "memcmp_ok_b",
1699 )
1700 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1701 self.builder
1702 .build_and(eq_b, offsets_found_b, "memcmp_ok_b")
1703 .map_err(|e| CodeGenError::Builder(e.to_string()))?
1704 };
1705
1706 let status_ok = self
1707 .builder
1708 .build_and(ok_a, ok_b, "memcmp_status_ok")
1709 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1710
1711 if self.condition_context_active {
1713 let not_a = self
1714 .builder
1715 .build_not(ok_a, "memcmp_fail_a")
1716 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1717 let not_b = self
1718 .builder
1719 .build_not(ok_b, "memcmp_fail_b")
1720 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1721 let any_fail = self
1722 .builder
1723 .build_or(not_a, not_b, "memcmp_any_fail")
1724 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1725 let func = self.current_function("compile memcmp condition error branch")?;
1726 let set_b = self.context.append_basic_block(func, "memcmp_set_err");
1727 let cont_b = self.context.append_basic_block(func, "memcmp_cont");
1728 self.builder
1729 .build_conditional_branch(any_fail, set_b, cont_b)
1730 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1731 self.builder.position_at_end(set_b);
1732 let _ = self.set_condition_error_if_unset(2u8);
1734 let not_a_val = self
1736 .builder
1737 .build_not(ok_a, "memcmp_fail_a_val")
1738 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1739 let not_b_val = self
1740 .builder
1741 .build_not(ok_b, "memcmp_fail_b_val")
1742 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1743 let cur_fn = self.current_function("compile memcmp failure address branch")?;
1744 let set_a_bb = self.context.append_basic_block(cur_fn, "set_addr_a");
1745 let check_b_bb = self.context.append_basic_block(cur_fn, "check_fail_b");
1746 let set_b_bb = self.context.append_basic_block(cur_fn, "set_addr_b");
1747 let after_set_bb = self.context.append_basic_block(cur_fn, "after_set_addr");
1748
1749 self.builder
1751 .build_conditional_branch(not_a_val, set_a_bb, check_b_bb)
1752 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1753
1754 self.builder.position_at_end(set_a_bb);
1756 if let Some(pa) = match parse_hex_bytes(a_expr) {
1757 Some(_) => None,
1758 None => Some(self.resolve_ptr_i64_from_expr(a_expr)?),
1759 } {
1760 let _ = self.set_condition_error_addr_if_unset(pa);
1761 }
1762 self.builder
1763 .build_unconditional_branch(after_set_bb)
1764 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1765
1766 self.builder.position_at_end(check_b_bb);
1768 self.builder
1769 .build_conditional_branch(not_b_val, set_b_bb, after_set_bb)
1770 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1771 self.builder.position_at_end(set_b_bb);
1772 if let Some(pb) = match parse_hex_bytes(b_expr) {
1773 Some(_) => None,
1774 None => Some(self.resolve_ptr_i64_from_expr(b_expr)?),
1775 } {
1776 let _ = self.set_condition_error_addr_if_unset(pb);
1777 }
1778 self.builder
1779 .build_unconditional_branch(after_set_bb)
1780 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1781 self.builder.position_at_end(after_set_bb);
1782 let i8t = self.context.i8_type();
1784 let b_a = self
1785 .builder
1786 .build_int_z_extend(
1787 self.builder
1788 .build_not(ok_a, "fa")
1789 .map_err(|e| CodeGenError::Builder(e.to_string()))?,
1790 i8t,
1791 "fa8",
1792 )
1793 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1794 let b_b1 = self
1795 .builder
1796 .build_int_z_extend(
1797 self.builder
1798 .build_not(ok_b, "fb")
1799 .map_err(|e| CodeGenError::Builder(e.to_string()))?,
1800 i8t,
1801 "fb8",
1802 )
1803 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1804 let sh1 = self
1805 .builder
1806 .build_left_shift(b_b1, i8t.const_int(1, false), "b_b_shift")
1807 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1808 let b_c = self
1810 .builder
1811 .build_int_z_extend(gt, i8t, "clamped8")
1812 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1813 let sh2 = self
1814 .builder
1815 .build_left_shift(b_c, i8t.const_int(2, false), "b_c_shift")
1816 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1817 let b_z = self
1819 .builder
1820 .build_int_z_extend(len_is_zero, i8t, "len0_8")
1821 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1822 let sh3 = self
1823 .builder
1824 .build_left_shift(b_z, i8t.const_int(3, false), "b_z_shift")
1825 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1826 let f01 = self
1827 .builder
1828 .build_or(b_a, sh1, "f01")
1829 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1830 let f012 = self
1831 .builder
1832 .build_or(f01, sh2, "f012")
1833 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1834 let flags = self
1835 .builder
1836 .build_or(f012, sh3, "flags")
1837 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1838 let _ = self.or_condition_error_flags(flags);
1839 self.builder
1840 .build_unconditional_branch(cont_b)
1841 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1842 self.builder.position_at_end(cont_b);
1843 }
1844
1845 let i32_ty = self.context.i32_type();
1847 let idx0 = i32_ty.const_zero();
1848 let mut acc = self.context.i8_type().const_zero();
1849 for i in 0..cap as usize {
1850 let idx_i = i32_ty.const_int(i as u64, false);
1851 let active = self
1853 .builder
1854 .build_int_compare(
1855 inkwell::IntPredicate::ULT,
1856 idx_i,
1857 sel_len,
1858 &format!("memcmp_i{i}_active"),
1859 )
1860 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1861 let pa = unsafe {
1864 self.builder
1865 .build_gep(arr_a_ty, buf_a, &[idx0, idx_i], &format!("memcmp_a_i{i}"))
1866 .map_err(|e| CodeGenError::Builder(e.to_string()))?
1867 };
1868 let va = self
1869 .builder
1870 .build_load(self.context.i8_type(), pa, &format!("ld_a_{i}"))
1871 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1872 let va = match va {
1873 BasicValueEnum::IntValue(iv) => iv,
1874 _ => return Err(CodeGenError::LLVMError("memcmp load a != i8".into())),
1875 };
1876 let pb = unsafe {
1879 self.builder
1880 .build_gep(arr_b_ty, buf_b, &[idx0, idx_i], &format!("memcmp_b_i{i}"))
1881 .map_err(|e| CodeGenError::Builder(e.to_string()))?
1882 };
1883 let vb = self
1884 .builder
1885 .build_load(self.context.i8_type(), pb, &format!("ld_b_{i}"))
1886 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1887 let vb = match vb {
1888 BasicValueEnum::IntValue(iv) => iv,
1889 _ => return Err(CodeGenError::LLVMError("memcmp load b != i8".into())),
1890 };
1891 let diff = self
1892 .builder
1893 .build_xor(va, vb, &format!("memcmp_diff_{i}"))
1894 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1895 let zero8 = self.context.i8_type().const_zero();
1896 let masked = self
1897 .builder
1898 .build_select(active, diff, zero8, &format!("memcmp_masked_{i}"))
1899 .map_err(|e| CodeGenError::Builder(e.to_string()))?
1900 .into_int_value();
1901 acc = self
1902 .builder
1903 .build_or(acc, masked, &format!("memcmp_acc_{i}"))
1904 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1905 }
1906 let eq_bytes = self
1907 .builder
1908 .build_int_compare(
1909 inkwell::IntPredicate::EQ,
1910 acc,
1911 self.context.i8_type().const_zero(),
1912 "memcmp_acc_zero",
1913 )
1914 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1915 let nz_result = self
1916 .builder
1917 .build_and(status_ok, eq_bytes, "memcmp_and")
1918 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1919
1920 self.builder
1921 .build_unconditional_branch(cont_b)
1922 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1923 let nz_block = self.current_insert_block("finish memcmp non-zero block")?;
1924
1925 self.builder.position_at_end(cont_b);
1927 let phi = self
1928 .builder
1929 .build_phi(self.context.bool_type(), "memcmp_phi")
1930 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1931 phi.add_incoming(&[(&bool_true, zero_block), (&nz_result, nz_block)]);
1932 Ok(phi.as_basic_value())
1933 }
1934 fn compile_bounded_compare_len_i32(
1936 &mut self,
1937 len_expr: &Expr,
1938 max_len: u32,
1939 name_prefix: &str,
1940 ) -> Result<(IntValue<'ctx>, IntValue<'ctx>)> {
1941 let len_val = self.compile_expr(len_expr)?;
1942 let len_iv = match len_val {
1943 BasicValueEnum::IntValue(iv) => iv,
1944 _ => {
1945 return Err(CodeGenError::TypeError(format!(
1946 "{name_prefix} length must be an integer expression"
1947 )))
1948 }
1949 };
1950 let i32_ty = self.context.i32_type();
1951 let len_i32 = if len_iv.get_type().get_bit_width() > 32 {
1952 self.builder
1953 .build_int_truncate(len_iv, i32_ty, &format!("{name_prefix}_len_trunc"))
1954 .map_err(|e| CodeGenError::Builder(e.to_string()))?
1955 } else if len_iv.get_type().get_bit_width() < 32 {
1956 self.builder
1957 .build_int_z_extend(len_iv, i32_ty, &format!("{name_prefix}_len_zext"))
1958 .map_err(|e| CodeGenError::Builder(e.to_string()))?
1959 } else {
1960 len_iv
1961 };
1962 let zero_i32 = i32_ty.const_zero();
1963 let is_neg = self
1964 .builder
1965 .build_int_compare(
1966 inkwell::IntPredicate::SLT,
1967 len_i32,
1968 zero_i32,
1969 &format!("{name_prefix}_len_neg"),
1970 )
1971 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1972 let len_nn = self
1973 .builder
1974 .build_select(is_neg, zero_i32, len_i32, &format!("{name_prefix}_len_nn"))
1975 .map_err(|e| CodeGenError::Builder(e.to_string()))?
1976 .into_int_value();
1977 let max_const = i32_ty.const_int(max_len as u64, false);
1978 let gt = self
1979 .builder
1980 .build_int_compare(
1981 inkwell::IntPredicate::UGT,
1982 len_nn,
1983 max_const,
1984 &format!("{name_prefix}_len_gt"),
1985 )
1986 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
1987 let bounded_len = self
1988 .builder
1989 .build_select(gt, max_const, len_nn, &format!("{name_prefix}_len_sel"))
1990 .map_err(|e| CodeGenError::Builder(e.to_string()))?
1991 .into_int_value();
1992 let is_zero = self
1993 .builder
1994 .build_int_compare(
1995 inkwell::IntPredicate::EQ,
1996 bounded_len,
1997 zero_i32,
1998 &format!("{name_prefix}_len_is_zero"),
1999 )
2000 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2001 Ok((bounded_len, is_zero))
2002 }
2003
2004 fn compile_strncmp_builtin(
2005 &mut self,
2006 dwarf_expr: &Expr,
2007 lit: &str,
2008 n_expr: &Expr,
2009 ) -> Result<BasicValueEnum<'ctx>> {
2010 let immediate_bytes_opt = match dwarf_expr {
2013 Expr::Variable(name) => {
2014 if self
2015 .get_variable_type(name)
2016 .is_some_and(|t| matches!(t, crate::script::VarType::String))
2017 {
2018 self.get_string_variable_bytes(name).cloned()
2019 } else {
2020 None
2021 }
2022 }
2023 Expr::String(s) => {
2024 let mut b = s.as_bytes().to_vec();
2025 b.push(0);
2026 Some(b)
2027 }
2028 _ => None,
2029 };
2030
2031 if let Some(bytes) = immediate_bytes_opt {
2032 if let Expr::Int(n) = n_expr {
2033 let n_usize = std::cmp::min(
2034 (*n).max(0) as usize,
2035 self.compile_options.compare_cap as usize,
2036 );
2037 let content_len = bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len());
2038 let cmp_len = std::cmp::min(n_usize, std::cmp::min(content_len, lit.len()));
2039 let equal = bytes.get(0..cmp_len).unwrap_or(&[])
2040 == lit.as_bytes().get(0..cmp_len).unwrap_or(&[]);
2041 let bool_val = self
2042 .context
2043 .bool_type()
2044 .const_int(if equal { 1 } else { 0 }, false);
2045 return Ok(bool_val.into());
2046 }
2047
2048 let cap = self.compile_options.compare_cap as usize;
2050 let content_len = bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len());
2051 let cmp_bound = std::cmp::min(cap, std::cmp::min(content_len, lit.len())) as u32;
2052 if cmp_bound == 0 {
2053 return Ok(self.context.bool_type().const_int(1, false).into());
2054 }
2055 let (bounded_len, _len_is_zero) =
2056 self.compile_bounded_compare_len_i32(n_expr, cmp_bound, "strncmp")?;
2057 let i32_ty = self.context.i32_type();
2058 let i8_ty = self.context.i8_type();
2059 let mut acc = i8_ty.const_zero();
2060 for (i, (byte, lit_byte)) in bytes
2061 .iter()
2062 .copied()
2063 .zip(lit.as_bytes().iter().copied())
2064 .take(cmp_bound as usize)
2065 .enumerate()
2066 {
2067 let active = self
2068 .builder
2069 .build_int_compare(
2070 inkwell::IntPredicate::UGT,
2071 bounded_len,
2072 i32_ty.const_int(i as u64, false),
2073 "strncmp_imm_active",
2074 )
2075 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2076 let diff = i8_ty.const_int((byte ^ lit_byte) as u64, false);
2077 let active_diff = self
2078 .builder
2079 .build_select(active, diff, i8_ty.const_zero(), "strncmp_imm_diff")
2080 .map_err(|e| CodeGenError::Builder(e.to_string()))?
2081 .into_int_value();
2082 acc = self
2083 .builder
2084 .build_or(acc, active_diff, "strncmp_imm_acc")
2085 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2086 }
2087 let equal = self
2088 .builder
2089 .build_int_compare(
2090 inkwell::IntPredicate::EQ,
2091 acc,
2092 i8_ty.const_zero(),
2093 "strncmp_imm_eq",
2094 )
2095 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2096 return Ok(equal.into());
2097 }
2098
2099 let ptr_i64 = match self.query_dwarf_for_complex_expr(dwarf_expr)? {
2102 Some(var) => {
2103 if let Some(ty) = var.dwarf_type.as_ref() {
2104 let ty = ghostscope_dwarf::strip_type_aliases(ty);
2105 match ty {
2106 DwarfType::PointerType { .. } => {
2107 let pc_address = self.get_compile_time_context()?.pc_address;
2108 let val_any =
2109 self.variable_read_plan_to_llvm_value(&var, pc_address, None)?;
2110 match val_any {
2111 BasicValueEnum::IntValue(iv) => {
2112 RuntimeAddress::available(iv, self.context)
2113 }
2114 BasicValueEnum::PointerValue(pv) => self
2115 .builder
2116 .build_ptr_to_int(pv, self.context.i64_type(), "ptr_as_i64")
2117 .map(|value| RuntimeAddress::available(value, self.context))
2118 .map_err(|e| CodeGenError::Builder(e.to_string()))?,
2119 _ => {
2120 return Err(CodeGenError::TypeError(
2121 "strncmp requires pointer/integer value for pointer; got unsupported DWARF value".into(),
2122 ))
2123 }
2124 }
2125 }
2126 DwarfType::ArrayType { .. } => {
2127 let status_ptr = if self.condition_context_active {
2128 Some(self.get_or_create_cond_error_global())
2129 } else {
2130 None
2131 };
2132 let pc_address = self.get_compile_time_context()?.pc_address;
2133 self.variable_read_plan_to_runtime_address(
2134 &var, pc_address, status_ptr,
2135 )?
2136 }
2137 _ => {
2138 return Err(CodeGenError::TypeError(
2140 "strncmp requires the non-string side to be an address expression (pointer/array)".into(),
2141 ));
2142 }
2143 }
2144 } else {
2145 return Err(CodeGenError::TypeError(
2146 "strncmp non-string side lacks DWARF type info".into(),
2147 ));
2148 }
2149 }
2150 None => {
2151 self.resolve_runtime_address_from_expr(dwarf_expr).map_err(|_| {
2153 CodeGenError::TypeError(
2154 "strncmp requires at least one string argument, and the other side must be an address expression (DWARF pointer/array or alias)".to_string(),
2155 )
2156 })?
2157 }
2158 };
2159
2160 let cap = self.compile_options.compare_cap;
2161 let cmp_bound = std::cmp::min(lit.len() as u32, cap);
2162 if cmp_bound == 0 {
2163 return Ok(self.context.bool_type().const_int(1, false).into());
2164 }
2165 let (bounded_len, len_is_zero) =
2166 self.compile_bounded_compare_len_i32(n_expr, cmp_bound, "strncmp")?;
2167
2168 let func = self.current_function("compile strncmp length branch")?;
2169 let zero_b = self.context.append_basic_block(func, "strncmp_len_zero");
2170 let nz_b = self.context.append_basic_block(func, "strncmp_len_nz");
2171 let final_b = self.context.append_basic_block(func, "strncmp_len_cont");
2172 self.builder
2173 .build_conditional_branch(len_is_zero, zero_b, nz_b)
2174 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2175
2176 self.builder.position_at_end(zero_b);
2177 let bool_true = self.context.bool_type().const_int(1, false);
2178 self.builder
2179 .build_unconditional_branch(final_b)
2180 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2181 let zero_block = self.current_insert_block("finish strncmp zero-length block")?;
2182
2183 self.builder.position_at_end(nz_b);
2184
2185 let (arr_ty, buf_global) = self.get_or_create_i8_buffer(cmp_bound, "_gs_bi_strncmp");
2186 let ptr_ty = self.context.ptr_type(AddressSpace::default());
2187 let dst_ptr = self
2188 .builder
2189 .build_bit_cast(buf_global, ptr_ty, "strncmp_dst_ptr")
2190 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2191 let base_src_ptr = self
2192 .builder
2193 .build_int_to_ptr(ptr_i64.value, ptr_ty, "strncmp_src_ptr")
2194 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2195 let src_ptr = self
2196 .builder
2197 .build_select::<BasicValueEnum<'ctx>, _>(
2198 ptr_i64.offsets_found,
2199 base_src_ptr.into(),
2200 ptr_ty.const_null().into(),
2201 "strncmp_src_or_null",
2202 )
2203 .map_err(|e| CodeGenError::Builder(e.to_string()))?
2204 .into_pointer_value();
2205 let effective_len = self
2206 .builder
2207 .build_select::<BasicValueEnum<'ctx>, _>(
2208 ptr_i64.offsets_found,
2209 bounded_len.into(),
2210 self.context.i32_type().const_zero().into(),
2211 "strncmp_len_or_zero",
2212 )
2213 .map_err(|e| CodeGenError::Builder(e.to_string()))?
2214 .into_int_value();
2215 let ret = self
2216 .create_bpf_helper_call(
2217 BPF_FUNC_probe_read_user as u64,
2218 &[dst_ptr, effective_len.into(), src_ptr.into()],
2219 self.context.i64_type().into(),
2220 "probe_read_user_strncmp",
2221 )?
2222 .into_int_value();
2223 let read_ok = self
2224 .builder
2225 .build_int_compare(
2226 inkwell::IntPredicate::EQ,
2227 ret,
2228 self.context.i64_type().const_zero(),
2229 "rd_ok",
2230 )
2231 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2232 let status_ok = self
2233 .builder
2234 .build_and(read_ok, ptr_i64.offsets_found, "strncmp_ok_with_offsets")
2235 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2236
2237 if self.condition_context_active {
2239 let func = self.current_function("compile strncmp condition error branch")?;
2240 let set_b = self.context.append_basic_block(func, "strncmp_set_err");
2241 let cont_b = self.context.append_basic_block(func, "strncmp_cont");
2242 let not_ok = self
2243 .builder
2244 .build_not(status_ok, "rd_fail")
2245 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2246 self.builder
2247 .build_conditional_branch(not_ok, set_b, cont_b)
2248 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2249 self.builder.position_at_end(set_b);
2250 let _ = self.set_condition_error_if_unset(2u8);
2252 let _ = self.set_condition_error_addr_if_unset(ptr_i64.value);
2253 let one = self.context.i8_type().const_int(1, false);
2255 let _ = self.or_condition_error_flags(one);
2256 self.builder
2257 .build_unconditional_branch(cont_b)
2258 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2259 self.builder.position_at_end(cont_b);
2260 }
2261
2262 let i32_ty = self.context.i32_type();
2264 let idx0 = i32_ty.const_zero();
2265 let mut acc = self.context.i8_type().const_zero();
2266 for (i, b) in lit.as_bytes().iter().take(cmp_bound as usize).enumerate() {
2267 let idx_i = i32_ty.const_int(i as u64, false);
2268 let ptr_i = unsafe {
2270 self.builder
2271 .build_gep(arr_ty, buf_global, &[idx0, idx_i], "ch_ptr")
2272 .map_err(|e| CodeGenError::Builder(e.to_string()))?
2273 };
2274 let ch = self
2275 .builder
2276 .build_load(self.context.i8_type(), ptr_i, "ch")
2277 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2278 let ch = match ch {
2279 BasicValueEnum::IntValue(iv) => iv,
2280 _ => return Err(CodeGenError::LLVMError("load did not return i8".into())),
2281 };
2282 let expect = self.context.i8_type().const_int(*b as u64, false);
2283 let diff = self
2284 .builder
2285 .build_xor(ch, expect, "diff")
2286 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2287 let active = self
2288 .builder
2289 .build_int_compare(
2290 inkwell::IntPredicate::UGT,
2291 bounded_len,
2292 idx_i,
2293 "strncmp_byte_active",
2294 )
2295 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2296 let diff = self
2297 .builder
2298 .build_select(
2299 active,
2300 diff,
2301 self.context.i8_type().const_zero(),
2302 "strncmp_active_diff",
2303 )
2304 .map_err(|e| CodeGenError::Builder(e.to_string()))?
2305 .into_int_value();
2306 acc = self
2307 .builder
2308 .build_or(acc, diff, "acc_or")
2309 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2310 }
2311 let eq_bytes = self
2312 .builder
2313 .build_int_compare(
2314 inkwell::IntPredicate::EQ,
2315 acc,
2316 self.context.i8_type().const_zero(),
2317 "acc_zero",
2318 )
2319 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2320
2321 let result = self
2322 .builder
2323 .build_and(status_ok, eq_bytes, "strncmp_and")
2324 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2325 self.builder
2326 .build_unconditional_branch(final_b)
2327 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2328 let nz_block = self.current_insert_block("finish strncmp non-zero block")?;
2329
2330 self.builder.position_at_end(final_b);
2331 let result_phi = self
2332 .builder
2333 .build_phi(self.context.bool_type(), "strncmp_result")
2334 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2335 result_phi.add_incoming(&[(&bool_true, zero_block), (&result, nz_block)]);
2336 Ok(result_phi.as_basic_value())
2337 }
2338 pub fn compile_expr(&mut self, expr: &Expr) -> Result<BasicValueEnum<'ctx>> {
2340 match expr {
2341 Expr::Int(value) => {
2342 let int_value = self.context.i64_type().const_int(*value as u64, true);
2344 debug!(
2345 "compile_expr: Int literal {} compiled to IntValue with bit width {}",
2346 value,
2347 int_value.get_type().get_bit_width()
2348 );
2349 Ok(int_value.into())
2350 }
2351 Expr::Float(_value) => Err(CodeGenError::TypeError(
2352 "Floating point expressions are not supported".to_string(),
2353 )),
2354 Expr::String(value) => {
2355 let string_value = self.context.const_string(value.as_bytes(), true);
2357 let global = self
2358 .module
2359 .add_global(string_value.get_type(), None, "str_const");
2360 global.set_initializer(&string_value);
2361
2362 let ptr_type = self.context.ptr_type(AddressSpace::default());
2363 let cast_ptr = self
2364 .builder
2365 .build_bit_cast(global.as_pointer_value(), ptr_type, "str_ptr")
2366 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2367 Ok(cast_ptr)
2368 }
2369 Expr::Bool(value) => {
2370 let b = self
2372 .context
2373 .bool_type()
2374 .const_int(if *value { 1 } else { 0 }, false);
2375 Ok(b.into())
2376 }
2377 Expr::UnaryNot(inner) => {
2378 let v = self.compile_expr(inner)?;
2380 let iv = match v {
2381 BasicValueEnum::IntValue(iv) => iv,
2382 _ => {
2383 return Err(CodeGenError::TypeError(
2384 "Logical NOT requires integer/boolean operand".to_string(),
2385 ))
2386 }
2387 };
2388 let zero = iv.get_type().const_zero();
2389 let res = self
2390 .builder
2391 .build_int_compare(inkwell::IntPredicate::EQ, iv, zero, "not_eq0")
2392 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2393 Ok(res.into())
2394 }
2395 Expr::UnaryBitNot(inner) => {
2396 let unsigned_width = self
2397 .dwarf_integer_comparison_expr(inner)
2398 .map(CIntegerComparisonType::promoted)
2399 .and_then(|integer_type| {
2400 integer_type
2401 .is_unsigned
2402 .then_some((integer_type.size * 8) as u32)
2403 });
2404 let v = self.compile_expr(inner)?;
2405 let iv = match v {
2406 BasicValueEnum::IntValue(iv) => iv,
2407 _ => {
2408 return Err(CodeGenError::TypeError(
2409 "Bitwise NOT requires integer/boolean operand".to_string(),
2410 ))
2411 }
2412 };
2413 if let Some(bit_width) = unsigned_width {
2414 let iv =
2415 self.normalize_int_for_unsigned_compare(iv, bit_width, "bitnot_unsigned")?;
2416 let result = self
2417 .builder
2418 .build_xor(iv, iv.get_type().const_all_ones(), "bitnot")
2419 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2420 return self
2421 .zero_extend_int_to_i64_if_needed(result, "bitnot_zext_i64")
2422 .map(|value| value.into());
2423 }
2424 let iv = if iv.get_type().get_bit_width() == 1 {
2425 self.builder
2426 .build_int_z_extend(iv, self.context.i64_type(), "bitnot_bool_i64")
2427 .map_err(|e| CodeGenError::Builder(e.to_string()))?
2428 } else {
2429 iv
2430 };
2431 let all_ones = iv.get_type().const_all_ones();
2432 let result = self
2433 .builder
2434 .build_xor(iv, all_ones, "bitnot")
2435 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2436 Ok(result.into())
2437 }
2438 Expr::Variable(var_name) => {
2439 debug!("compile_expr: Compiling variable expression: {}", var_name);
2440
2441 if self.alias_variable_exists(var_name) {
2443 debug!(
2444 "compile_expr: '{}' is an alias variable; resolving to runtime address",
2445 var_name
2446 );
2447 let aliased = self
2448 .get_alias_variable(var_name)
2449 .expect("alias existence just checked");
2450 let addr_i64 = self.resolve_ptr_i64_from_expr(&aliased)?;
2452 let ptr_ty = self.context.ptr_type(AddressSpace::default());
2453 let as_ptr = self
2454 .builder
2455 .build_int_to_ptr(addr_i64, ptr_ty, "alias_as_ptr")
2456 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2457 return Ok(as_ptr.into());
2458 }
2459
2460 if self.variable_exists(var_name) {
2462 debug!("compile_expr: Found script variable: {}", var_name);
2463 let loaded_value = self.load_variable(var_name)?;
2464 debug!(
2465 "compile_expr: Loaded variable '{}' with type: {:?}",
2466 var_name,
2467 loaded_value.get_type()
2468 );
2469 match &loaded_value {
2470 BasicValueEnum::IntValue(iv) => debug!(
2471 "compile_expr: Variable '{}' is IntValue with bit width {}",
2472 var_name,
2473 iv.get_type().get_bit_width()
2474 ),
2475 BasicValueEnum::FloatValue(_) => {
2476 debug!("compile_expr: Variable '{}' is FloatValue", var_name)
2477 }
2478 BasicValueEnum::PointerValue(_) => {
2479 debug!("compile_expr: Variable '{}' is PointerValue", var_name)
2480 }
2481 _ => debug!("compile_expr: Variable '{}' is other type", var_name),
2482 }
2483 return Ok(loaded_value);
2484 }
2485
2486 debug!(
2488 "Variable '{}' not found in script variables, checking DWARF",
2489 var_name
2490 );
2491 match self.query_dwarf_for_variable(var_name) {
2493 Ok(Some(_)) => self.compile_dwarf_expression(expr),
2494 Ok(None) => Err(CodeGenError::VariableNotInScope(var_name.clone())),
2495 Err(e) => Err(CodeGenError::DwarfError(e.to_string())),
2496 }
2497 }
2498 Expr::SpecialVar(name) => {
2499 let sanitized = name.trim_start_matches('$');
2501 self.handle_special_variable(sanitized)
2502 }
2503 Expr::BuiltinCall { name, args } => match self.plan_builtin_call(name, args)? {
2504 BuiltinCallPlan::Memcmp => {
2505 self.compile_memcmp_builtin(&args[0], &args[1], &args[2])
2506 }
2507 BuiltinCallPlan::Strncmp => {
2508 fn extract_script_string(
2510 this: &mut EbpfContext<'_, '_>,
2511 e: &Expr,
2512 ) -> Option<String> {
2513 match e {
2514 Expr::String(s) => Some(s.clone()),
2515 Expr::Variable(name) => this
2516 .get_variable_type(name)
2517 .is_some_and(|t| matches!(t, crate::script::VarType::String))
2518 .then(|| {
2519 this.get_string_variable_bytes(name).map(|b| {
2520 let cut = b.iter().position(|&x| x == 0).unwrap_or(b.len());
2521 String::from_utf8_lossy(&b[..cut]).to_string()
2522 })
2523 })
2524 .flatten(),
2525 _ => None,
2526 }
2527 }
2528 let left_str = extract_script_string(self, &args[0]);
2529 let right_str = extract_script_string(self, &args[1]);
2530 match (left_str, right_str) {
2531 (Some(ls), Some(rs)) => {
2532 let left_expr = Expr::String(ls);
2533 self.compile_strncmp_builtin(&left_expr, &rs, &args[2])
2534 }
2535 (Some(ls), None) => self.compile_strncmp_builtin(&args[1], &ls, &args[2]),
2536 (None, Some(rs)) => self.compile_strncmp_builtin(&args[0], &rs, &args[2]),
2537 (None, None) => Err(CodeGenError::TypeError(
2538 "strncmp requires at least one string argument (string literal or script string variable) as the first or second parameter".into(),
2539 )),
2540 }
2541 }
2542 BuiltinCallPlan::StartsWith => {
2543 fn extract_script_string(
2545 this: &mut EbpfContext<'_, '_>,
2546 e: &Expr,
2547 ) -> Option<String> {
2548 match e {
2549 Expr::String(s) => Some(s.clone()),
2550 Expr::Variable(name) => this
2551 .get_variable_type(name)
2552 .is_some_and(|t| matches!(t, crate::script::VarType::String))
2553 .then(|| {
2554 this.get_string_variable_bytes(name).map(|b| {
2555 let cut = b.iter().position(|&x| x == 0).unwrap_or(b.len());
2556 String::from_utf8_lossy(&b[..cut]).to_string()
2557 })
2558 })
2559 .flatten(),
2560 _ => None,
2561 }
2562 }
2563 let s0 = extract_script_string(self, &args[0]);
2564 let s1 = extract_script_string(self, &args[1]);
2565 match (s0, s1) {
2566 (Some(a), Some(b)) => {
2567 let ok = a.as_bytes().starts_with(b.as_bytes());
2569 let bv = self.context.bool_type().const_int(ok as u64, false);
2570 Ok(bv.into())
2571 }
2572 (Some(a), None) => {
2573 let n_expr = Expr::Int(a.len() as i64);
2574 self.compile_strncmp_builtin(&args[1], &a, &n_expr)
2575 }
2576 (None, Some(b)) => {
2577 let n_expr = Expr::Int(b.len() as i64);
2578 self.compile_strncmp_builtin(&args[0], &b, &n_expr)
2579 }
2580 (None, None) => Err(CodeGenError::TypeError(
2581 "starts_with requires at least one string argument (string literal or script string variable) as the first or second parameter".into(),
2582 )),
2583 }
2584 }
2585 },
2586 Expr::BinaryOp { left, op, right } => {
2587 let binary_plan = self.plan_binary_expr(left, op, right)?;
2588 if let BinaryEmitKind::StringComparison(string_plan) = &binary_plan.emit_kind {
2589 let other = if string_plan.literal_on_left {
2590 right.as_ref()
2591 } else {
2592 left.as_ref()
2593 };
2594 return self.compile_string_comparison(
2595 other,
2596 &string_plan.literal,
2597 string_plan.equal,
2598 );
2599 }
2600 if matches!(&binary_plan.emit_kind, BinaryEmitKind::LogicalOr) {
2602 let lhs_val = self.compile_expr(left)?;
2604 let lhs_int = match lhs_val {
2605 BasicValueEnum::IntValue(iv) => iv,
2606 BasicValueEnum::PointerValue(pv) => self
2607 .builder
2608 .build_ptr_to_int(pv, self.context.i64_type(), "lor_lhs_ptr_as_i64")
2609 .map_err(|e| CodeGenError::Builder(e.to_string()))?,
2610 _ => {
2611 return Err(CodeGenError::TypeError(
2612 "Logical OR requires integer or pointer operands".to_string(),
2613 ))
2614 }
2615 };
2616 let lhs_zero = lhs_int.get_type().const_zero();
2617 let lhs_bool = self
2618 .builder
2619 .build_int_compare(
2620 inkwell::IntPredicate::NE,
2621 lhs_int,
2622 lhs_zero,
2623 "lor_lhs_nz",
2624 )
2625 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2626
2627 let curr_block = self.builder.get_insert_block().ok_or_else(|| {
2629 CodeGenError::LLVMError("No current basic block".to_string())
2630 })?;
2631 let func = curr_block
2632 .get_parent()
2633 .ok_or_else(|| CodeGenError::LLVMError("No parent function".to_string()))?;
2634 let rhs_block = self.context.append_basic_block(func, "lor_rhs");
2635 let merge_block = self.context.append_basic_block(func, "lor_merge");
2636
2637 self.builder
2639 .build_conditional_branch(lhs_bool, merge_block, rhs_block)
2640 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2641
2642 self.builder.position_at_end(rhs_block);
2644 let rhs_val = self.compile_expr(right)?;
2645 let rhs_int = match rhs_val {
2646 BasicValueEnum::IntValue(iv) => iv,
2647 BasicValueEnum::PointerValue(pv) => self
2648 .builder
2649 .build_ptr_to_int(pv, self.context.i64_type(), "lor_rhs_ptr_as_i64")
2650 .map_err(|e| CodeGenError::Builder(e.to_string()))?,
2651 _ => {
2652 return Err(CodeGenError::TypeError(
2653 "Logical OR requires integer or pointer operands".to_string(),
2654 ))
2655 }
2656 };
2657 let rhs_zero = rhs_int.get_type().const_zero();
2658 let rhs_bool = self
2659 .builder
2660 .build_int_compare(
2661 inkwell::IntPredicate::NE,
2662 rhs_int,
2663 rhs_zero,
2664 "lor_rhs_nz",
2665 )
2666 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2667 let rhs_end_block = self.builder.get_insert_block().ok_or_else(|| {
2669 CodeGenError::LLVMError("No current basic block after RHS".to_string())
2670 })?;
2671 self.builder
2672 .build_unconditional_branch(merge_block)
2673 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2674
2675 self.builder.position_at_end(merge_block);
2677 let i1 = self.context.bool_type();
2678 let phi = self
2679 .builder
2680 .build_phi(i1, "lor_phi")
2681 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2682 let one = i1.const_int(1, false);
2683 phi.add_incoming(&[(&one, curr_block), (&rhs_bool, rhs_end_block)]);
2684 return Ok(phi.as_basic_value());
2685 } else if matches!(&binary_plan.emit_kind, BinaryEmitKind::LogicalAnd) {
2686 let lhs_val = self.compile_expr(left)?;
2688 let lhs_int = match lhs_val {
2689 BasicValueEnum::IntValue(iv) => iv,
2690 BasicValueEnum::PointerValue(pv) => self
2691 .builder
2692 .build_ptr_to_int(pv, self.context.i64_type(), "land_lhs_ptr_as_i64")
2693 .map_err(|e| CodeGenError::Builder(e.to_string()))?,
2694 _ => {
2695 return Err(CodeGenError::TypeError(
2696 "Logical AND requires integer or pointer operands".to_string(),
2697 ))
2698 }
2699 };
2700 let lhs_zero = lhs_int.get_type().const_zero();
2701 let lhs_bool = self
2702 .builder
2703 .build_int_compare(
2704 inkwell::IntPredicate::NE,
2705 lhs_int,
2706 lhs_zero,
2707 "land_lhs_nz",
2708 )
2709 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2710
2711 let curr_block = self.builder.get_insert_block().ok_or_else(|| {
2713 CodeGenError::LLVMError("No current basic block".to_string())
2714 })?;
2715 let func = curr_block
2716 .get_parent()
2717 .ok_or_else(|| CodeGenError::LLVMError("No parent function".to_string()))?;
2718 let rhs_block = self.context.append_basic_block(func, "land_rhs");
2719 let merge_block = self.context.append_basic_block(func, "land_merge");
2720
2721 self.builder
2723 .build_conditional_branch(lhs_bool, rhs_block, merge_block)
2724 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2725
2726 self.builder.position_at_end(rhs_block);
2728 let rhs_val = self.compile_expr(right)?;
2729 let rhs_int = match rhs_val {
2730 BasicValueEnum::IntValue(iv) => iv,
2731 BasicValueEnum::PointerValue(pv) => self
2732 .builder
2733 .build_ptr_to_int(pv, self.context.i64_type(), "land_rhs_ptr_as_i64")
2734 .map_err(|e| CodeGenError::Builder(e.to_string()))?,
2735 _ => {
2736 return Err(CodeGenError::TypeError(
2737 "Logical AND requires integer or pointer operands".to_string(),
2738 ))
2739 }
2740 };
2741 let rhs_zero = rhs_int.get_type().const_zero();
2742 let rhs_bool = self
2743 .builder
2744 .build_int_compare(
2745 inkwell::IntPredicate::NE,
2746 rhs_int,
2747 rhs_zero,
2748 "land_rhs_nz",
2749 )
2750 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2751 let rhs_end_block = self.builder.get_insert_block().ok_or_else(|| {
2752 CodeGenError::LLVMError("No current basic block after RHS".to_string())
2753 })?;
2754 self.builder
2755 .build_unconditional_branch(merge_block)
2756 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2757
2758 self.builder.position_at_end(merge_block);
2760 let i1 = self.context.bool_type();
2761 let phi = self
2762 .builder
2763 .build_phi(i1, "land_phi")
2764 .map_err(|e| CodeGenError::LLVMError(e.to_string()))?;
2765 let zero = i1.const_zero();
2766 phi.add_incoming(&[(&rhs_bool, rhs_end_block), (&zero, curr_block)]);
2767 return Ok(phi.as_basic_value());
2768 }
2769
2770 let left_val = self.compile_expr(left)?;
2772 let right_val = self.compile_expr(right)?;
2773 self.compile_binary_op_with_ordering(
2774 left_val,
2775 binary_plan.op,
2776 right_val,
2777 binary_plan.integer_semantics,
2778 )
2779 }
2780 Expr::MemberAccess(_, _) => {
2781 self.compile_dwarf_expression(expr)
2783 }
2784 Expr::PointerDeref(_) => {
2785 self.compile_dwarf_expression(expr)
2787 }
2788 Expr::AddressOf(inner) => {
2789 let target_inner: &Expr = if let Expr::Variable(var_name) = inner.as_ref() {
2792 if self.alias_variable_exists(var_name) {
2793 let aliased = self
2795 .get_alias_variable(var_name)
2796 .expect("alias existence just checked");
2797 let var =
2798 self.query_dwarf_for_complex_expr(&aliased)?
2799 .ok_or_else(|| {
2800 super::context::CodeGenError::TypeError(
2801 "cannot take address of unresolved expression".to_string(),
2802 )
2803 })?;
2804 let pc_address = self.get_compile_time_context()?.pc_address;
2805 match self.variable_read_plan_to_runtime_address(&var, pc_address, None) {
2806 Ok(address) => {
2807 let ptr_ty = self.context.ptr_type(AddressSpace::default());
2808 let as_ptr = self
2809 .builder
2810 .build_int_to_ptr(address.value, ptr_ty, "addr_as_ptr")
2811 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2812 return Ok(as_ptr.into());
2813 }
2814 Err(_) => {
2815 return Err(super::context::CodeGenError::TypeError(
2816 "cannot take address of rvalue".to_string(),
2817 ));
2818 }
2819 }
2820 } else {
2821 inner.as_ref()
2822 }
2823 } else {
2824 inner.as_ref()
2825 };
2826
2827 if let Some(lvalue) = self.dynamic_lvalue_address_and_type(target_inner)? {
2828 let ptr_ty = self.context.ptr_type(AddressSpace::default());
2829 let as_ptr = self
2830 .builder
2831 .build_int_to_ptr(lvalue.address.value, ptr_ty, "addr_as_ptr")
2832 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2833 return Ok(as_ptr.into());
2834 }
2835
2836 let var = self
2837 .query_dwarf_for_complex_expr(target_inner)?
2838 .ok_or_else(|| {
2839 super::context::CodeGenError::TypeError(
2840 "cannot take address of unresolved expression".to_string(),
2841 )
2842 })?;
2843 let pc_address = self.get_compile_time_context()?.pc_address;
2844 match self.variable_read_plan_to_runtime_address(&var, pc_address, None) {
2845 Ok(address) => {
2846 let ptr_ty = self.context.ptr_type(AddressSpace::default());
2847 let as_ptr = self
2848 .builder
2849 .build_int_to_ptr(address.value, ptr_ty, "addr_as_ptr")
2850 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2851 Ok(as_ptr.into())
2852 }
2853 Err(_) => Err(super::context::CodeGenError::TypeError(
2854 "cannot take address of rvalue".to_string(),
2855 )),
2856 }
2857 }
2858 Expr::ArrayAccess(_, _) => {
2859 self.compile_dwarf_expression(expr)
2861 }
2862 Expr::Cast {
2863 expr: inner,
2864 target_type,
2865 } => self.compile_cast_expr_value(inner, target_type),
2866 Expr::ChainAccess(_) => {
2867 self.compile_dwarf_expression(expr)
2869 }
2870 }
2871 }
2872
2873 pub fn handle_special_variable(&mut self, name: &str) -> Result<BasicValueEnum<'ctx>> {
2875 match self.plan_special_variable(name)? {
2876 SpecialVarPlan::Pid => {
2877 let (pid, _tid) = self.get_special_pid_tid_values()?;
2878 Ok(pid.into())
2879 }
2880 SpecialVarPlan::Tid => {
2881 let (_pid, tid) = self.get_special_pid_tid_values()?;
2882 Ok(tid.into())
2883 }
2884 SpecialVarPlan::HostPid => {
2885 let (host_pid, _host_tid) = self.get_host_pid_tid_values()?;
2886 let host_pid = self
2887 .builder
2888 .build_int_z_extend(host_pid, self.context.i64_type(), "selected_host_pid")
2889 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2890 Ok(host_pid.into())
2891 }
2892 SpecialVarPlan::InputPid => {
2893 let input_pid = self.compile_options.input_pid.ok_or_else(|| {
2894 CodeGenError::NotImplemented(
2895 "Special variable '$input_pid' is only available in -p mode".to_string(),
2896 )
2897 })?;
2898 Ok(self
2899 .context
2900 .i64_type()
2901 .const_int(input_pid as u64, false)
2902 .into())
2903 }
2904 SpecialVarPlan::Timestamp => {
2905 let ts = self.get_current_timestamp()?;
2907 Ok(ts.into())
2908 }
2909 SpecialVarPlan::Pc => self.load_special_register_value(16),
2910 SpecialVarPlan::Sp => self.load_special_register_value(7),
2911 }
2912 }
2913
2914 fn load_special_register_value(&mut self, dwarf_reg: u16) -> Result<BasicValueEnum<'ctx>> {
2915 let pt_regs = self.get_pt_regs_parameter()?;
2916 self.load_register_value(dwarf_reg, pt_regs)
2917 }
2918
2919 pub fn compile_binary_op(
2921 &mut self,
2922 left: BasicValueEnum<'ctx>,
2923 op: BinaryOp,
2924 right: BasicValueEnum<'ctx>,
2925 ) -> Result<BasicValueEnum<'ctx>> {
2926 self.compile_binary_op_with_ordering(left, op, right, BinaryIntegerSemantics::default())
2927 }
2928
2929 pub(crate) fn build_signed_int_div_via_udiv(
2930 &mut self,
2931 left: IntValue<'ctx>,
2932 right: IntValue<'ctx>,
2933 name: &str,
2934 ) -> Result<IntValue<'ctx>> {
2935 let int_type = left.get_type();
2936 let zero = int_type.const_zero();
2937 let left_is_neg = self
2938 .builder
2939 .build_int_compare(inkwell::IntPredicate::SLT, left, zero, "sdiv_lhs_neg")
2940 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2941 let right_is_neg = self
2942 .builder
2943 .build_int_compare(inkwell::IntPredicate::SLT, right, zero, "sdiv_rhs_neg")
2944 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2945 let neg_left = self
2946 .builder
2947 .build_int_sub(zero, left, "sdiv_lhs_negated")
2948 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2949 let neg_right = self
2950 .builder
2951 .build_int_sub(zero, right, "sdiv_rhs_negated")
2952 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2953 let abs_left = self
2954 .builder
2955 .build_select::<BasicValueEnum<'ctx>, _>(
2956 left_is_neg,
2957 neg_left.into(),
2958 left.into(),
2959 "sdiv_lhs_abs",
2960 )
2961 .map_err(|e| CodeGenError::Builder(e.to_string()))?
2962 .into_int_value();
2963 let abs_right = self
2964 .builder
2965 .build_select::<BasicValueEnum<'ctx>, _>(
2966 right_is_neg,
2967 neg_right.into(),
2968 right.into(),
2969 "sdiv_rhs_abs",
2970 )
2971 .map_err(|e| CodeGenError::Builder(e.to_string()))?
2972 .into_int_value();
2973 let abs_quotient = self
2974 .builder
2975 .build_int_unsigned_div(abs_left, abs_right, "sdiv_abs_udiv")
2976 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2977 let negative_result = self
2978 .builder
2979 .build_xor(left_is_neg, right_is_neg, "sdiv_result_neg")
2980 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2981 let neg_quotient = self
2982 .builder
2983 .build_int_sub(zero, abs_quotient, "sdiv_negated")
2984 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
2985 self.builder
2986 .build_select::<BasicValueEnum<'ctx>, _>(
2987 negative_result,
2988 neg_quotient.into(),
2989 abs_quotient.into(),
2990 name,
2991 )
2992 .map_err(|e| CodeGenError::Builder(e.to_string()))
2993 .map(|value| value.into_int_value())
2994 }
2995
2996 pub(crate) fn build_signed_int_rem_via_urem(
2997 &mut self,
2998 left: IntValue<'ctx>,
2999 right: IntValue<'ctx>,
3000 name: &str,
3001 ) -> Result<IntValue<'ctx>> {
3002 let int_type = left.get_type();
3003 let zero = int_type.const_zero();
3004 let left_is_neg = self
3005 .builder
3006 .build_int_compare(inkwell::IntPredicate::SLT, left, zero, "srem_lhs_neg")
3007 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3008 let right_is_neg = self
3009 .builder
3010 .build_int_compare(inkwell::IntPredicate::SLT, right, zero, "srem_rhs_neg")
3011 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3012 let neg_left = self
3013 .builder
3014 .build_int_sub(zero, left, "srem_lhs_negated")
3015 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3016 let neg_right = self
3017 .builder
3018 .build_int_sub(zero, right, "srem_rhs_negated")
3019 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3020 let abs_left = self
3021 .builder
3022 .build_select::<BasicValueEnum<'ctx>, _>(
3023 left_is_neg,
3024 neg_left.into(),
3025 left.into(),
3026 "srem_lhs_abs",
3027 )
3028 .map_err(|e| CodeGenError::Builder(e.to_string()))?
3029 .into_int_value();
3030 let abs_right = self
3031 .builder
3032 .build_select::<BasicValueEnum<'ctx>, _>(
3033 right_is_neg,
3034 neg_right.into(),
3035 right.into(),
3036 "srem_rhs_abs",
3037 )
3038 .map_err(|e| CodeGenError::Builder(e.to_string()))?
3039 .into_int_value();
3040 let abs_remainder = self
3041 .builder
3042 .build_int_unsigned_rem(abs_left, abs_right, "srem_abs_urem")
3043 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3044 let neg_remainder = self
3045 .builder
3046 .build_int_sub(zero, abs_remainder, "srem_negated")
3047 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3048 self.builder
3049 .build_select::<BasicValueEnum<'ctx>, _>(
3050 left_is_neg,
3051 neg_remainder.into(),
3052 abs_remainder.into(),
3053 name,
3054 )
3055 .map_err(|e| CodeGenError::Builder(e.to_string()))
3056 .map(|value| value.into_int_value())
3057 }
3058
3059 fn normalize_int_for_unsigned_compare(
3060 &mut self,
3061 value: IntValue<'ctx>,
3062 bit_width: u32,
3063 name: &str,
3064 ) -> Result<IntValue<'ctx>> {
3065 let current_width = value.get_type().get_bit_width();
3066 if current_width == bit_width {
3067 return Ok(value);
3068 }
3069
3070 let target_type = self.context.custom_width_int_type(bit_width);
3071 if current_width > bit_width {
3072 self.builder
3073 .build_int_truncate(value, target_type, name)
3074 .map_err(|e| CodeGenError::Builder(e.to_string()))
3075 } else {
3076 self.builder
3077 .build_int_z_extend(value, target_type, name)
3078 .map_err(|e| CodeGenError::Builder(e.to_string()))
3079 }
3080 }
3081
3082 fn align_int_widths_for_binary_op(
3083 &mut self,
3084 left: IntValue<'ctx>,
3085 right: IntValue<'ctx>,
3086 ) -> Result<(IntValue<'ctx>, IntValue<'ctx>)> {
3087 let left_width = left.get_type().get_bit_width();
3088 let right_width = right.get_type().get_bit_width();
3089 if left_width == right_width {
3090 return Ok((left, right));
3091 }
3092
3093 let target_width = left_width.max(right_width);
3094 let target_type = self.context.custom_width_int_type(target_width);
3095 let left = if left_width < target_width {
3096 self.builder
3097 .build_int_z_extend(left, target_type, "lhs_width_align")
3098 .map_err(|e| CodeGenError::Builder(e.to_string()))?
3099 } else {
3100 left
3101 };
3102 let right = if right_width < target_width {
3103 self.builder
3104 .build_int_z_extend(right, target_type, "rhs_width_align")
3105 .map_err(|e| CodeGenError::Builder(e.to_string()))?
3106 } else {
3107 right
3108 };
3109 Ok((left, right))
3110 }
3111
3112 fn mask_shift_amount(&mut self, amount: IntValue<'ctx>, name: &str) -> Result<IntValue<'ctx>> {
3113 let bit_width = amount.get_type().get_bit_width();
3114 let mask = amount
3115 .get_type()
3116 .const_int(u64::from(bit_width.saturating_sub(1)), false);
3117 self.builder
3118 .build_and(amount, mask, name)
3119 .map_err(|e| CodeGenError::Builder(e.to_string()))
3120 }
3121
3122 fn normalize_ints_for_unsigned_width(
3123 &mut self,
3124 left: IntValue<'ctx>,
3125 right: IntValue<'ctx>,
3126 bit_width: u32,
3127 name: &str,
3128 ) -> Result<(IntValue<'ctx>, IntValue<'ctx>)> {
3129 let left = self.normalize_int_for_unsigned_compare(
3130 left,
3131 bit_width,
3132 &format!("{name}_lhs_unsigned"),
3133 )?;
3134 let right = self.normalize_int_for_unsigned_compare(
3135 right,
3136 bit_width,
3137 &format!("{name}_rhs_unsigned"),
3138 )?;
3139 Ok((left, right))
3140 }
3141
3142 fn zero_extend_int_to_i64_if_needed(
3143 &mut self,
3144 value: IntValue<'ctx>,
3145 name: &str,
3146 ) -> Result<IntValue<'ctx>> {
3147 if value.get_type().get_bit_width() >= 64 {
3148 return Ok(value);
3149 }
3150 self.builder
3151 .build_int_z_extend(value, self.context.i64_type(), name)
3152 .map_err(|e| CodeGenError::Builder(e.to_string()))
3153 }
3154
3155 fn compile_binary_op_with_ordering(
3156 &mut self,
3157 left: BasicValueEnum<'ctx>,
3158 op: BinaryOp,
3159 right: BasicValueEnum<'ctx>,
3160 integer_semantics: BinaryIntegerSemantics,
3161 ) -> Result<BasicValueEnum<'ctx>> {
3162 use inkwell::values::BasicValueEnum::*;
3163
3164 debug!("compile_binary_op: op={:?}", op);
3166 debug!("compile_binary_op: left type = {:?}", left.get_type());
3167 debug!("compile_binary_op: right type = {:?}", right.get_type());
3168 match &left {
3169 IntValue(iv) => debug!(
3170 "compile_binary_op: left is IntValue with bit width {}",
3171 iv.get_type().get_bit_width()
3172 ),
3173 FloatValue(_) => debug!("compile_binary_op: left is FloatValue"),
3174 PointerValue(_) => debug!("compile_binary_op: left is PointerValue"),
3175 _ => debug!("compile_binary_op: left is other type"),
3176 }
3177 match &right {
3178 IntValue(iv) => debug!(
3179 "compile_binary_op: right is IntValue with bit width {}",
3180 iv.get_type().get_bit_width()
3181 ),
3182 FloatValue(_) => debug!("compile_binary_op: right is FloatValue"),
3183 PointerValue(_) => debug!("compile_binary_op: right is PointerValue"),
3184 _ => debug!("compile_binary_op: right is other type"),
3185 }
3186
3187 match (left, right) {
3188 (IntValue(left_int), IntValue(right_int)) => {
3189 let (left_int, right_int) =
3190 self.align_int_widths_for_binary_op(left_int, right_int)?;
3191 let unsigned_cmp_values = if let Some(bit_width) =
3192 integer_semantics.unsigned_ordering_width
3193 {
3194 Some((
3195 self.normalize_int_for_unsigned_compare(
3196 left_int,
3197 bit_width,
3198 "lhs_unsigned_cmp",
3199 )?,
3200 self.normalize_int_for_unsigned_compare(
3201 right_int,
3202 bit_width,
3203 "rhs_unsigned_cmp",
3204 )?,
3205 ))
3206 } else {
3207 None
3208 };
3209 let result = match op {
3210 BinaryOp::Add => self
3211 .builder
3212 .build_int_add(left_int, right_int, "add")
3213 .map_err(|e| CodeGenError::Builder(e.to_string()))?,
3214 BinaryOp::Subtract => self
3215 .builder
3216 .build_int_sub(left_int, right_int, "sub")
3217 .map_err(|e| CodeGenError::Builder(e.to_string()))?,
3218 BinaryOp::Multiply => self
3219 .builder
3220 .build_int_mul(left_int, right_int, "mul")
3221 .map_err(|e| CodeGenError::Builder(e.to_string()))?,
3222 BinaryOp::Divide => {
3223 if let Some(bit_width) = integer_semantics.unsigned_division_width {
3224 let (left_int, right_int) = self.normalize_ints_for_unsigned_width(
3225 left_int,
3226 right_int,
3227 bit_width,
3228 "div",
3229 )?;
3230 let result = self
3231 .builder
3232 .build_int_unsigned_div(left_int, right_int, "div")
3233 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3234 self.zero_extend_int_to_i64_if_needed(result, "div_zext_i64")?
3235 } else {
3236 self.build_signed_int_div_via_udiv(left_int, right_int, "div")?
3237 }
3238 }
3239 BinaryOp::Modulo => {
3240 if let Some(bit_width) = integer_semantics.unsigned_division_width {
3241 let (left_int, right_int) = self.normalize_ints_for_unsigned_width(
3242 left_int,
3243 right_int,
3244 bit_width,
3245 "mod",
3246 )?;
3247 let result = self
3248 .builder
3249 .build_int_unsigned_rem(left_int, right_int, "mod")
3250 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3251 self.zero_extend_int_to_i64_if_needed(result, "mod_zext_i64")?
3252 } else {
3253 self.build_signed_int_rem_via_urem(left_int, right_int, "mod")?
3254 }
3255 }
3256 BinaryOp::BitAnd => {
3257 let (left_int, right_int) =
3258 if let Some(bit_width) = integer_semantics.unsigned_bitwise_width {
3259 self.normalize_ints_for_unsigned_width(
3260 left_int,
3261 right_int,
3262 bit_width,
3263 "bitand",
3264 )?
3265 } else {
3266 (left_int, right_int)
3267 };
3268 let result = self
3269 .builder
3270 .build_and(left_int, right_int, "bitand")
3271 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3272 if integer_semantics.unsigned_bitwise_width.is_some() {
3273 self.zero_extend_int_to_i64_if_needed(result, "bitand_zext_i64")?
3274 } else {
3275 result
3276 }
3277 }
3278 BinaryOp::BitXor => {
3279 let (left_int, right_int) =
3280 if let Some(bit_width) = integer_semantics.unsigned_bitwise_width {
3281 self.normalize_ints_for_unsigned_width(
3282 left_int,
3283 right_int,
3284 bit_width,
3285 "bitxor",
3286 )?
3287 } else {
3288 (left_int, right_int)
3289 };
3290 let result = self
3291 .builder
3292 .build_xor(left_int, right_int, "bitxor")
3293 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3294 if integer_semantics.unsigned_bitwise_width.is_some() {
3295 self.zero_extend_int_to_i64_if_needed(result, "bitxor_zext_i64")?
3296 } else {
3297 result
3298 }
3299 }
3300 BinaryOp::BitOr => {
3301 let (left_int, right_int) =
3302 if let Some(bit_width) = integer_semantics.unsigned_bitwise_width {
3303 self.normalize_ints_for_unsigned_width(
3304 left_int,
3305 right_int,
3306 bit_width,
3307 "bitor",
3308 )?
3309 } else {
3310 (left_int, right_int)
3311 };
3312 let result = self
3313 .builder
3314 .build_or(left_int, right_int, "bitor")
3315 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3316 if integer_semantics.unsigned_bitwise_width.is_some() {
3317 self.zero_extend_int_to_i64_if_needed(result, "bitor_zext_i64")?
3318 } else {
3319 result
3320 }
3321 }
3322 BinaryOp::ShiftLeft => {
3323 let right_int = self.mask_shift_amount(right_int, "shl_rhs_mask")?;
3324 self.builder
3325 .build_left_shift(left_int, right_int, "shl")
3326 .map_err(|e| CodeGenError::Builder(e.to_string()))?
3327 }
3328 BinaryOp::ShiftRight => {
3329 let right_int = self.mask_shift_amount(right_int, "shr_rhs_mask")?;
3330 self.builder
3331 .build_right_shift(
3332 left_int,
3333 right_int,
3334 integer_semantics.unsigned_right_shift_width.is_none(),
3335 "shr",
3336 )
3337 .map_err(|e| CodeGenError::Builder(e.to_string()))?
3338 }
3339 BinaryOp::Equal => {
3341 let result = self
3342 .builder
3343 .build_int_compare(inkwell::IntPredicate::EQ, left_int, right_int, "eq")
3344 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3345 return Ok(result.into());
3346 }
3347 BinaryOp::NotEqual => {
3348 let result = self
3349 .builder
3350 .build_int_compare(inkwell::IntPredicate::NE, left_int, right_int, "ne")
3351 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3352 return Ok(result.into());
3353 }
3354 BinaryOp::LessThan => {
3355 let predicate = if integer_semantics.unsigned_ordering_width.is_some() {
3356 inkwell::IntPredicate::ULT
3357 } else {
3358 inkwell::IntPredicate::SLT
3359 };
3360 let (left_cmp, right_cmp) =
3361 unsigned_cmp_values.unwrap_or((left_int, right_int));
3362 let result = self
3363 .builder
3364 .build_int_compare(predicate, left_cmp, right_cmp, "lt")
3365 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3366 return Ok(result.into());
3367 }
3368 BinaryOp::LessEqual => {
3369 let predicate = if integer_semantics.unsigned_ordering_width.is_some() {
3370 inkwell::IntPredicate::ULE
3371 } else {
3372 inkwell::IntPredicate::SLE
3373 };
3374 let (left_cmp, right_cmp) =
3375 unsigned_cmp_values.unwrap_or((left_int, right_int));
3376 let result = self
3377 .builder
3378 .build_int_compare(predicate, left_cmp, right_cmp, "le")
3379 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3380 return Ok(result.into());
3381 }
3382 BinaryOp::GreaterThan => {
3383 let predicate = if integer_semantics.unsigned_ordering_width.is_some() {
3384 inkwell::IntPredicate::UGT
3385 } else {
3386 inkwell::IntPredicate::SGT
3387 };
3388 let (left_cmp, right_cmp) =
3389 unsigned_cmp_values.unwrap_or((left_int, right_int));
3390 let result = self
3391 .builder
3392 .build_int_compare(predicate, left_cmp, right_cmp, "gt")
3393 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3394 return Ok(result.into());
3395 }
3396 BinaryOp::GreaterEqual => {
3397 let predicate = if integer_semantics.unsigned_ordering_width.is_some() {
3398 inkwell::IntPredicate::UGE
3399 } else {
3400 inkwell::IntPredicate::SGE
3401 };
3402 let (left_cmp, right_cmp) =
3403 unsigned_cmp_values.unwrap_or((left_int, right_int));
3404 let result = self
3405 .builder
3406 .build_int_compare(predicate, left_cmp, right_cmp, "ge")
3407 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3408 return Ok(result.into());
3409 }
3410 BinaryOp::LogicalAnd => {
3412 let lz = left_int.get_type().const_zero();
3413 let rz = right_int.get_type().const_zero();
3414 let lbool = self
3415 .builder
3416 .build_int_compare(inkwell::IntPredicate::NE, left_int, lz, "lhs_nz")
3417 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3418 let rbool = self
3419 .builder
3420 .build_int_compare(inkwell::IntPredicate::NE, right_int, rz, "rhs_nz")
3421 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3422 let result = self
3423 .builder
3424 .build_and(lbool, rbool, "and_bool")
3425 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3426 return Ok(result.into());
3427 }
3428 BinaryOp::LogicalOr => {
3429 let lz = left_int.get_type().const_zero();
3430 let rz = right_int.get_type().const_zero();
3431 let lbool = self
3432 .builder
3433 .build_int_compare(inkwell::IntPredicate::NE, left_int, lz, "lhs_nz")
3434 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3435 let rbool = self
3436 .builder
3437 .build_int_compare(inkwell::IntPredicate::NE, right_int, rz, "rhs_nz")
3438 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3439 let result = self
3440 .builder
3441 .build_or(lbool, rbool, "or_bool")
3442 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3443 return Ok(result.into());
3444 }
3445 };
3446 Ok(result.into())
3447 }
3448 (PointerValue(lp), IntValue(ri)) | (IntValue(ri), PointerValue(lp)) => {
3450 match op {
3451 BinaryOp::Equal | BinaryOp::NotEqual => {
3452 let lpi64 = self
3453 .builder
3454 .build_ptr_to_int(lp, self.context.i64_type(), "ptr_as_i64")
3455 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3456 let rbw = ri.get_type().get_bit_width();
3458 let ri64 = if rbw < 64 {
3459 self.builder
3460 .build_int_z_extend(ri, self.context.i64_type(), "rhs_zext_i64")
3461 .map_err(|e| CodeGenError::Builder(e.to_string()))?
3462 } else if rbw > 64 {
3463 self.builder
3464 .build_int_truncate(ri, self.context.i64_type(), "rhs_trunc_i64")
3465 .map_err(|e| CodeGenError::Builder(e.to_string()))?
3466 } else {
3467 ri
3468 };
3469 let pred = if matches!(op, BinaryOp::Equal) {
3470 inkwell::IntPredicate::EQ
3471 } else {
3472 inkwell::IntPredicate::NE
3473 };
3474 let cmp = self
3475 .builder
3476 .build_int_compare(pred, lpi64, ri64, "ptr_cmp")
3477 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3478 Ok(cmp.into())
3479 }
3480 _ => Err(CodeGenError::TypeError(
3481 "Unsupported operation between aggregate address/pointer and integer: only '==' and '!=' are allowed. If you meant to offset an address, use '&expr +/- <integer literal>' in an alias/address context, or access a scalar field.".to_string(),
3482 )),
3483 }
3484 }
3485 (PointerValue(lp), PointerValue(rp)) => match op {
3486 BinaryOp::Equal | BinaryOp::NotEqual => {
3487 let lpi64 = self
3488 .builder
3489 .build_ptr_to_int(lp, self.context.i64_type(), "l_ptr_as_i64")
3490 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3491 let rpi64 = self
3492 .builder
3493 .build_ptr_to_int(rp, self.context.i64_type(), "r_ptr_as_i64")
3494 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3495 let pred = if matches!(op, BinaryOp::Equal) {
3496 inkwell::IntPredicate::EQ
3497 } else {
3498 inkwell::IntPredicate::NE
3499 };
3500 let cmp = self
3501 .builder
3502 .build_int_compare(pred, lpi64, rpi64, "ptr_ptr_cmp")
3503 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3504 Ok(cmp.into())
3505 }
3506 _ => Err(CodeGenError::TypeError(
3507 "Pointer ordered comparison ('<', '<=', '>', '>=') is not supported. Use '==' or '!=' to compare addresses. If you need to adjust an address, use '&expr +/- <integer literal>' in an alias/address context; to compare values, select a scalar field (e.g., 'obj.field')."
3508 .to_string(),
3509 )),
3510 },
3511 (FloatValue(left_float), FloatValue(right_float)) => match op {
3512 BinaryOp::Add => {
3513 let result = self
3514 .builder
3515 .build_float_add(left_float, right_float, "add")
3516 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3517 Ok(result.into())
3518 }
3519 BinaryOp::Subtract => {
3520 let result = self
3521 .builder
3522 .build_float_sub(left_float, right_float, "sub")
3523 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3524 Ok(result.into())
3525 }
3526 BinaryOp::Multiply => {
3527 let result = self
3528 .builder
3529 .build_float_mul(left_float, right_float, "mul")
3530 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3531 Ok(result.into())
3532 }
3533 BinaryOp::Divide => {
3534 let result = self
3535 .builder
3536 .build_float_div(left_float, right_float, "div")
3537 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3538 Ok(result.into())
3539 }
3540 BinaryOp::Equal => {
3542 let result = self
3543 .builder
3544 .build_float_compare(
3545 inkwell::FloatPredicate::OEQ,
3546 left_float,
3547 right_float,
3548 "eq",
3549 )
3550 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3551 Ok(result.into())
3552 }
3553 BinaryOp::NotEqual => {
3554 let result = self
3555 .builder
3556 .build_float_compare(
3557 inkwell::FloatPredicate::ONE,
3558 left_float,
3559 right_float,
3560 "ne",
3561 )
3562 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3563 Ok(result.into())
3564 }
3565 BinaryOp::LessThan => {
3566 let result = self
3567 .builder
3568 .build_float_compare(
3569 inkwell::FloatPredicate::OLT,
3570 left_float,
3571 right_float,
3572 "lt",
3573 )
3574 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3575 Ok(result.into())
3576 }
3577 BinaryOp::LessEqual => {
3578 let result = self
3579 .builder
3580 .build_float_compare(
3581 inkwell::FloatPredicate::OLE,
3582 left_float,
3583 right_float,
3584 "le",
3585 )
3586 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3587 Ok(result.into())
3588 }
3589 BinaryOp::GreaterThan => {
3590 let result = self
3591 .builder
3592 .build_float_compare(
3593 inkwell::FloatPredicate::OGT,
3594 left_float,
3595 right_float,
3596 "gt",
3597 )
3598 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3599 Ok(result.into())
3600 }
3601 BinaryOp::GreaterEqual => {
3602 let result = self
3603 .builder
3604 .build_float_compare(
3605 inkwell::FloatPredicate::OGE,
3606 left_float,
3607 right_float,
3608 "ge",
3609 )
3610 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
3611 Ok(result.into())
3612 }
3613 _ => Err(CodeGenError::NotImplemented(format!(
3614 "Float binary operation {op:?} not implemented"
3615 ))),
3616 },
3617 _ => Err(CodeGenError::TypeError(format!(
3618 "Type mismatch in binary operation {op:?}"
3619 ))),
3620 }
3621 }
3622
3623 pub fn compile_member_access(
3625 &mut self,
3626 obj_expr: &Expr,
3627 field: &str,
3628 ) -> Result<BasicValueEnum<'ctx>> {
3629 let member_access_expr = Expr::MemberAccess(Box::new(obj_expr.clone()), field.to_string());
3631 self.compile_dwarf_expression(&member_access_expr)
3632 }
3633
3634 pub fn compile_pointer_deref(&mut self, expr: &Expr) -> Result<BasicValueEnum<'ctx>> {
3636 let pointer_deref_expr = Expr::PointerDeref(Box::new(expr.clone()));
3638 self.compile_dwarf_expression(&pointer_deref_expr)
3639 }
3640
3641 pub fn compile_array_access(
3643 &mut self,
3644 array_expr: &Expr,
3645 index_expr: &Expr,
3646 ) -> Result<BasicValueEnum<'ctx>> {
3647 if let Some((value, _element_type)) =
3648 self.compile_dynamic_array_access_value(array_expr, index_expr)?
3649 {
3650 return Ok(value);
3651 }
3652
3653 let array_access_expr =
3655 Expr::ArrayAccess(Box::new(array_expr.clone()), Box::new(index_expr.clone()));
3656 self.compile_dwarf_expression(&array_access_expr)
3657 }
3658
3659 pub fn compile_chain_access(&mut self, chain: &[String]) -> Result<BasicValueEnum<'ctx>> {
3661 let chain_access_expr = Expr::ChainAccess(chain.to_vec());
3663 self.compile_dwarf_expression(&chain_access_expr)
3664 }
3665
3666 pub fn compile_dwarf_expression(
3668 &mut self,
3669 expr: &crate::script::Expr,
3670 ) -> Result<BasicValueEnum<'ctx>> {
3671 debug!(
3672 "compile_dwarf_expression: Compiling complex expression: {:?}",
3673 expr
3674 );
3675
3676 if let crate::script::Expr::Cast {
3677 expr: inner,
3678 target_type,
3679 } = expr
3680 {
3681 return self.compile_cast_expr_value(inner, target_type);
3682 }
3683
3684 if let crate::script::Expr::ArrayAccess(array_expr, index_expr) = expr {
3685 if let Some((value, _element_type)) =
3686 self.compile_dynamic_array_access_value(array_expr, index_expr)?
3687 {
3688 return Ok(value);
3689 }
3690 }
3691 if let crate::script::Expr::MemberAccess(obj_expr, field) = expr {
3692 if let Some((value, _member_type)) =
3693 self.compile_dynamic_member_access_value(obj_expr, field)?
3694 {
3695 return Ok(value);
3696 }
3697 }
3698 if matches!(expr, crate::script::Expr::PointerDeref(_)) {
3699 if let Some(lvalue) = self.dynamic_lvalue_address_and_type(expr)? {
3700 return self
3701 .read_dynamic_address_value(lvalue.address, &lvalue.type_info.dwarf_type);
3702 }
3703 }
3704
3705 let compile_context = self.get_compile_time_context()?.clone();
3707 let variable_plan = match self.query_dwarf_for_complex_expr(expr)? {
3708 Some(var) => var,
3709 None => {
3710 let expr_str = Self::expr_to_debug_string(expr);
3711 return Err(CodeGenError::VariableNotFound(expr_str));
3712 }
3713 };
3714
3715 let materialized =
3716 self.variable_read_plan_to_materialization(variable_plan, compile_context.pc_address)?;
3717 let dwarf_type = materialized.dwarf_type.as_ref().ok_or_else(|| {
3718 CodeGenError::DwarfError("Expression has no DWARF type information".to_string())
3719 })?;
3720
3721 debug!(
3722 "compile_dwarf_expression: Found DWARF info for expression '{}' with type: {:?}",
3723 materialized.name, dwarf_type
3724 );
3725
3726 self.variable_materialization_to_llvm_value(&materialized, compile_context.pc_address, None)
3727 }
3728
3729 pub(super) fn compile_dynamic_array_access_value(
3730 &mut self,
3731 array_expr: &Expr,
3732 index_expr: &Expr,
3733 ) -> Result<Option<(BasicValueEnum<'ctx>, DwarfType)>> {
3734 let Some(element_lvalue) =
3735 self.compile_dynamic_array_element_address(array_expr, index_expr)?
3736 else {
3737 return Ok(None);
3738 };
3739
3740 let value = self.read_dynamic_address_value(
3741 element_lvalue.address,
3742 &element_lvalue.type_info.dwarf_type,
3743 )?;
3744 Ok(Some((value, element_lvalue.type_info.dwarf_type)))
3745 }
3746
3747 pub(super) fn compile_dynamic_member_access_value(
3748 &mut self,
3749 obj_expr: &Expr,
3750 field: &str,
3751 ) -> Result<Option<(BasicValueEnum<'ctx>, DwarfType)>> {
3752 let Some(object_lvalue) = self.dynamic_lvalue_address_and_type(obj_expr)? else {
3753 return Ok(None);
3754 };
3755
3756 let Some(element_lvalue) = self.dynamic_member_base_address_and_type(object_lvalue)? else {
3757 return Ok(None);
3758 };
3759 let (member_offset, member_type) =
3760 self.dynamic_member_offset_and_type(&element_lvalue.type_info, field)?;
3761
3762 let member_offset = self.context.i64_type().const_int(member_offset, false);
3763 let member_address = self
3764 .builder
3765 .build_int_add(
3766 element_lvalue.address.value,
3767 member_offset,
3768 "dynamic_member_address",
3769 )
3770 .map_err(|err| CodeGenError::Builder(err.to_string()))?;
3771 let value = self.read_dynamic_address_value(
3772 element_lvalue.address.with_value(member_address),
3773 &member_type,
3774 )?;
3775 Ok(Some((value, member_type)))
3776 }
3777
3778 pub(super) fn dynamic_lvalue_address_and_type(
3779 &mut self,
3780 expr: &Expr,
3781 ) -> Result<Option<DynamicLvalue<'ctx>>> {
3782 if let Expr::Variable(name) = expr {
3783 if self.alias_variable_exists(name) {
3784 let expanded = self.expand_alias_variable_expr(expr)?;
3785 return self.dynamic_lvalue_address_and_type(&expanded);
3786 }
3787 }
3788
3789 if let Expr::Cast {
3790 expr: inner,
3791 target_type,
3792 } = expr
3793 {
3794 return self
3795 .cast_lvalue_address_and_type(inner, target_type)
3796 .map(Some);
3797 }
3798
3799 if let Expr::PointerDeref(inner) = expr {
3800 let expanded_inner = self.expand_alias_variable_expr(inner)?;
3801 if matches!(expanded_inner, Expr::Cast { .. }) {
3802 return self.dynamic_lvalue_address_and_type(&expanded_inner);
3803 }
3804 if let Expr::BinaryOp { .. } = expanded_inner {
3805 if let Some(lvalue) = self.dynamic_lvalue_address_and_type(&expanded_inner)? {
3806 return Ok(Some(lvalue));
3807 }
3808 }
3809 }
3810
3811 if let Expr::ArrayAccess(array_expr, index_expr) = expr {
3812 return self.compile_dynamic_array_element_address(array_expr, index_expr);
3813 }
3814
3815 if let Some(lvalue) = self.dynamic_lvalue_from_const_pointer_arithmetic(expr)? {
3816 return Ok(Some(lvalue));
3817 }
3818
3819 if self.expands_to_nonliteral_pointer_arithmetic(expr)? {
3820 let Some(element_info) = self.indexable_element_type_and_stride(expr)? else {
3821 return Ok(None);
3822 };
3823 let element_address = self.resolve_runtime_address_from_expr(expr)?;
3824 return Ok(Some(DynamicLvalue {
3825 address: element_address,
3826 type_info: DynamicTypeInfo {
3827 dwarf_type: element_info.element_type,
3828 module_path: element_info.module_path,
3829 },
3830 }));
3831 }
3832
3833 if let Expr::MemberAccess(obj_expr, field) = expr {
3834 let Some(object_lvalue) = self.dynamic_lvalue_address_and_type(obj_expr)? else {
3835 return Ok(None);
3836 };
3837 let Some(base_lvalue) = self.dynamic_member_base_address_and_type(object_lvalue)?
3838 else {
3839 return Ok(None);
3840 };
3841 let (member_offset, member_type) =
3842 self.dynamic_member_offset_and_type(&base_lvalue.type_info, field)?;
3843 let member_offset = self.context.i64_type().const_int(member_offset, false);
3844 let member_address = self
3845 .builder
3846 .build_int_add(
3847 base_lvalue.address.value,
3848 member_offset,
3849 "dynamic_member_lvalue_address",
3850 )
3851 .map_err(|err| CodeGenError::Builder(err.to_string()))?;
3852 return Ok(Some(DynamicLvalue {
3853 address: base_lvalue.address.with_value(member_address),
3854 type_info: DynamicTypeInfo {
3855 dwarf_type: member_type,
3856 module_path: base_lvalue.type_info.module_path,
3857 },
3858 }));
3859 }
3860
3861 Ok(None)
3862 }
3863
3864 fn dynamic_member_base_address_and_type(
3865 &mut self,
3866 object: DynamicLvalue<'ctx>,
3867 ) -> Result<Option<DynamicLvalue<'ctx>>> {
3868 let module_path = object.type_info.module_path.clone();
3869 let object_type = self.complete_dynamic_member_element_type(
3870 object.type_info.dwarf_type,
3871 module_path.as_deref(),
3872 );
3873 match ghostscope_dwarf::strip_type_aliases(&object_type) {
3874 DwarfType::StructType { .. } | DwarfType::UnionType { .. } => Ok(Some(DynamicLvalue {
3875 address: object.address,
3876 type_info: DynamicTypeInfo {
3877 dwarf_type: object_type,
3878 module_path,
3879 },
3880 })),
3881 DwarfType::PointerType { target_type, .. } => {
3882 let pointer_value =
3883 self.read_dynamic_address_value(object.address, &object_type)?;
3884 let pointer_value = match pointer_value {
3885 BasicValueEnum::IntValue(value) => {
3886 self.normalize_int_to_i64(value, "dynamic_member_pointer_i64")?
3887 }
3888 BasicValueEnum::PointerValue(value) => self
3889 .builder
3890 .build_ptr_to_int(
3891 value,
3892 self.context.i64_type(),
3893 "dynamic_member_pointer_ptr",
3894 )
3895 .map_err(|err| CodeGenError::Builder(err.to_string()))?,
3896 _ => {
3897 return Err(CodeGenError::TypeError(
3898 "dynamic member pointer base did not compile to an address".to_string(),
3899 ))
3900 }
3901 };
3902 let target_type = self.complete_dynamic_member_element_type(
3903 target_type.as_ref().clone(),
3904 module_path.as_deref(),
3905 );
3906 Ok(Some(DynamicLvalue {
3907 address: RuntimeAddress::available(pointer_value, self.context),
3908 type_info: DynamicTypeInfo {
3909 dwarf_type: target_type,
3910 module_path,
3911 },
3912 }))
3913 }
3914 _ => Ok(None),
3915 }
3916 }
3917
3918 fn dynamic_member_offset_and_type(
3919 &self,
3920 aggregate: &DynamicTypeInfo,
3921 field: &str,
3922 ) -> Result<(u64, DwarfType)> {
3923 let aggregate_type = self.complete_dynamic_member_element_type(
3924 aggregate.dwarf_type.clone(),
3925 aggregate.module_path.as_deref(),
3926 );
3927 match ghostscope_dwarf::member_layout(&aggregate_type, field) {
3928 Ok(layout) => Ok((layout.offset, layout.member_type)),
3929 Err(err @ TypeLayoutError::UnknownMember { .. }) => {
3930 Err(CodeGenError::DwarfError(err.to_string()))
3931 }
3932 Err(err @ TypeLayoutError::InvalidMemberBase { .. }) => {
3933 Err(CodeGenError::TypeError(err.to_string()))
3934 }
3935 }
3936 }
3937
3938 fn dynamic_array_base_from_plan(
3939 &mut self,
3940 array_plan: &VariableReadPlan,
3941 pc_address: u64,
3942 status_ptr: Option<PointerValue<'ctx>>,
3943 static_index: i64,
3944 ) -> Result<(IndexableElementInfo, RuntimeAddress<'ctx>, i64)> {
3945 let module_path = array_plan.module_path.clone();
3946 let array_type = array_plan.dwarf_type.as_ref().ok_or_else(|| {
3947 CodeGenError::DwarfError("Array expression has no DWARF type information".to_string())
3948 })?;
3949 let element_info =
3950 Self::indexable_info_from_type(array_type, module_path).ok_or_else(|| {
3951 CodeGenError::TypeError(format!(
3952 "dynamic array index requires array or pointer type, got '{}'",
3953 array_type.type_name()
3954 ))
3955 })?;
3956
3957 match ghostscope_dwarf::strip_type_aliases(array_type) {
3958 DwarfType::ArrayType { .. } => {
3959 let base_address =
3960 self.variable_read_plan_to_runtime_address(array_plan, pc_address, status_ptr)?;
3961 Ok((element_info, base_address, static_index))
3962 }
3963 DwarfType::PointerType { .. } => {
3964 let pointer_value =
3965 self.variable_read_plan_to_llvm_value(array_plan, pc_address, status_ptr)?;
3966 let base_address = self.compiled_pointer_value_to_runtime_address(
3967 pointer_value,
3968 "dynamic_array_base_i64",
3969 "dynamic_array_base_ptr",
3970 "array base pointer did not compile to an address",
3971 )?;
3972 Ok((element_info, base_address, static_index))
3973 }
3974 _ => unreachable!("indexable_info_from_type accepts only array or pointer types"),
3975 }
3976 }
3977
3978 fn compile_dynamic_array_element_address(
3979 &mut self,
3980 array_expr: &Expr,
3981 index_expr: &Expr,
3982 ) -> Result<Option<DynamicLvalue<'ctx>>> {
3983 let literal_index = Self::integer_literal_value(index_expr);
3984 let expanded_array_expr = self.expand_alias_variable_expr(array_expr)?;
3985 let has_dynamic_base =
3986 self.expands_to_nonliteral_pointer_arithmetic(&expanded_array_expr)?;
3987 let cast_base = self.cast_index_base(&expanded_array_expr)?;
3988
3989 if literal_index.is_some() && !has_dynamic_base && cast_base.is_none() {
3990 return Ok(None);
3991 }
3992
3993 let compile_context = self.get_compile_time_context()?.clone();
3994 let status_ptr = if self.condition_context_active {
3995 Some(self.get_or_create_cond_error_global())
3996 } else {
3997 None
3998 };
3999
4000 let (element_info, base_address, static_index) = if let Some((element_info, base_address)) =
4001 cast_base
4002 {
4003 (element_info, base_address, 0)
4004 } else {
4005 match self.query_dwarf_for_complex_expr(array_expr)? {
4006 Some(array_plan) => self.dynamic_array_base_from_plan(
4007 &array_plan,
4008 compile_context.pc_address,
4009 status_ptr,
4010 0,
4011 )?,
4012 None => {
4013 if let Some((base_expr, static_index)) =
4014 self.pointer_arithmetic_parts_expanding_aliases(&expanded_array_expr)?
4015 {
4016 let array_plan = self
4017 .query_dwarf_for_complex_expr(&base_expr)?
4018 .ok_or_else(|| {
4019 CodeGenError::VariableNotFound(Self::expr_to_debug_string(
4020 &base_expr,
4021 ))
4022 })?;
4023 self.dynamic_array_base_from_plan(
4024 &array_plan,
4025 compile_context.pc_address,
4026 status_ptr,
4027 static_index,
4028 )?
4029 } else if has_dynamic_base {
4030 let element_info = self
4031 .indexable_element_type_and_stride(&expanded_array_expr)?
4032 .ok_or_else(|| {
4033 CodeGenError::VariableNotFound(Self::expr_to_debug_string(
4034 array_expr,
4035 ))
4036 })?;
4037 let base_address =
4038 self.resolve_runtime_address_from_expr(&expanded_array_expr)?;
4039 (element_info, base_address, 0)
4040 } else if let Some(array_lvalue) =
4041 self.dynamic_lvalue_address_and_type(&expanded_array_expr)?
4042 {
4043 let module_path = array_lvalue.type_info.module_path.clone();
4044 let element_info = Self::indexable_info_from_type(
4045 &array_lvalue.type_info.dwarf_type,
4046 module_path,
4047 )
4048 .ok_or_else(|| {
4049 CodeGenError::TypeError(format!(
4050 "dynamic array index requires array or pointer type, got '{}'",
4051 array_lvalue.type_info.dwarf_type.type_name()
4052 ))
4053 })?;
4054 match ghostscope_dwarf::strip_type_aliases(
4055 &array_lvalue.type_info.dwarf_type,
4056 ) {
4057 DwarfType::ArrayType { .. } => (element_info, array_lvalue.address, 0),
4058 DwarfType::PointerType { .. } => {
4059 let pointer_value = self.read_dynamic_address_value(
4060 array_lvalue.address,
4061 &array_lvalue.type_info.dwarf_type,
4062 )?;
4063 let base_address = self.compiled_pointer_value_to_runtime_address(
4064 pointer_value,
4065 "dynamic_array_member_ptr_i64",
4066 "dynamic_array_member_ptr",
4067 "array member pointer did not compile to an address",
4068 )?;
4069 (element_info, base_address, 0)
4070 }
4071 _ => unreachable!(
4072 "indexable_info_from_type accepts only array or pointer types"
4073 ),
4074 }
4075 } else {
4076 return Err(CodeGenError::VariableNotFound(Self::expr_to_debug_string(
4077 array_expr,
4078 )));
4079 }
4080 }
4081 }
4082 };
4083
4084 let index_value = if let Some(index) = literal_index {
4085 self.context.i64_type().const_int(index as u64, true)
4086 } else {
4087 match self.compile_expr(index_expr)? {
4088 BasicValueEnum::IntValue(value) => {
4089 self.normalize_int_to_i64(value, "dynamic_array_index_i64")?
4090 }
4091 _ => {
4092 return Err(CodeGenError::TypeError(
4093 "array index expression must compile to an integer".to_string(),
4094 ))
4095 }
4096 }
4097 };
4098 let index_value = if static_index == 0 {
4099 index_value
4100 } else {
4101 let static_index_value = self.context.i64_type().const_int(static_index as u64, true);
4102 self.builder
4103 .build_int_add(
4104 index_value,
4105 static_index_value,
4106 "dynamic_array_static_index",
4107 )
4108 .map_err(|err| CodeGenError::Builder(err.to_string()))?
4109 };
4110 let stride_value = self
4111 .context
4112 .i64_type()
4113 .const_int(element_info.stride, false);
4114 let byte_offset = self
4115 .builder
4116 .build_int_mul(index_value, stride_value, "dynamic_array_byte_offset")
4117 .map_err(|err| CodeGenError::Builder(err.to_string()))?;
4118 let element_address = self
4119 .builder
4120 .build_int_add(
4121 base_address.value,
4122 byte_offset,
4123 "dynamic_array_element_address",
4124 )
4125 .map_err(|err| CodeGenError::Builder(err.to_string()))?;
4126
4127 Ok(Some(DynamicLvalue {
4128 address: base_address.with_value(element_address),
4129 type_info: DynamicTypeInfo {
4130 dwarf_type: element_info.element_type,
4131 module_path: element_info.module_path,
4132 },
4133 }))
4134 }
4135
4136 fn indexable_element_type_and_stride(
4137 &mut self,
4138 expr: &Expr,
4139 ) -> Result<Option<IndexableElementInfo>> {
4140 use crate::script::ast::BinaryOp as BO;
4141 use crate::script::ast::Expr as E;
4142
4143 let expanded = self.expand_alias_variable_expr(expr)?;
4144
4145 if let Some((element_info, _base_address)) = self.cast_index_base(&expanded)? {
4146 return Ok(Some(element_info));
4147 }
4148
4149 if let Some(plan) = self.query_dwarf_for_complex_expr(&expanded)? {
4150 if let Some(dwarf_type) = plan.dwarf_type.as_ref() {
4151 if let Some(info) =
4152 Self::indexable_info_from_type(dwarf_type, plan.module_path.clone())
4153 {
4154 return Ok(Some(info));
4155 }
4156 }
4157 }
4158
4159 if let Some((base_expr, _static_index)) =
4160 self.pointer_arithmetic_parts_expanding_aliases(&expanded)?
4161 {
4162 if let Some(plan) = self.query_dwarf_for_complex_expr(&base_expr)? {
4163 if let Some(dwarf_type) = plan.dwarf_type.as_ref() {
4164 if let Some(info) =
4165 Self::indexable_info_from_type(dwarf_type, plan.module_path.clone())
4166 {
4167 return Ok(Some(info));
4168 }
4169 }
4170 }
4171 }
4172
4173 match expanded {
4174 E::BinaryOp {
4175 ref left,
4176 op: BO::Add,
4177 ref right,
4178 } => {
4179 if let Some(info) = self.indexable_element_type_and_stride(left)? {
4180 return Ok(Some(info));
4181 }
4182 self.indexable_element_type_and_stride(right)
4183 }
4184 E::BinaryOp {
4185 ref left,
4186 op: BO::Subtract,
4187 ..
4188 } => self.indexable_element_type_and_stride(left),
4189 _ => Ok(None),
4190 }
4191 }
4192
4193 fn complete_dynamic_member_element_type(
4194 &self,
4195 element_type: DwarfType,
4196 module_path: Option<&Path>,
4197 ) -> DwarfType {
4198 let Some(analyzer) = self.process_analyzer else {
4199 return element_type;
4200 };
4201 let fallback_module_path = self
4202 .current_compile_time_context
4203 .as_ref()
4204 .map(|ctx| PathBuf::from(&ctx.module_path));
4205 let lookup_module_path = module_path.or(fallback_module_path.as_deref());
4206
4207 if let Some(module_path) = lookup_module_path {
4208 analyzer.complete_shallow_unknown_aggregate_type_in_module(module_path, element_type)
4209 } else {
4210 analyzer.complete_shallow_unknown_aggregate_type(element_type)
4211 }
4212 }
4213
4214 fn read_dynamic_address_value(
4215 &mut self,
4216 address: RuntimeAddress<'ctx>,
4217 dwarf_type: &DwarfType,
4218 ) -> Result<BasicValueEnum<'ctx>> {
4219 if ghostscope_dwarf::is_c_aggregate_type(dwarf_type) {
4220 let ptr_ty = self.context.ptr_type(AddressSpace::default());
4221 let as_ptr = self
4222 .builder
4223 .build_int_to_ptr(address.value, ptr_ty, "dynamic_aggregate_ptr")
4224 .map_err(|err| CodeGenError::Builder(err.to_string()))?;
4225 return Ok(as_ptr.into());
4226 }
4227
4228 let access_size = self.dwarf_type_to_memory_access_size(dwarf_type);
4229 let value = if self.condition_context_active {
4230 self.generate_memory_read_with_status(address, access_size)?
4231 } else {
4232 self.generate_memory_read(address, access_size, None)?
4233 };
4234 self.sign_extend_memory_read_if_needed(value, dwarf_type, access_size)
4235 }
4236
4237 fn expand_alias_variable_expr(&self, expr: &Expr) -> Result<Expr> {
4238 let mut expanded = expr.clone();
4239 let mut visited = std::collections::HashSet::new();
4240
4241 loop {
4242 let Expr::Variable(name) = &expanded else {
4243 return Ok(expanded);
4244 };
4245 if !self.alias_variable_exists(name) {
4246 return Ok(expanded);
4247 }
4248 if !visited.insert(name.clone()) {
4249 return Err(CodeGenError::TypeError(format!(
4250 "alias cycle detected for '{name}'"
4251 )));
4252 }
4253 let Some(target) = self.get_alias_variable(name) else {
4254 return Ok(expanded);
4255 };
4256 expanded = target;
4257 }
4258 }
4259
4260 fn normalize_int_to_i64(&self, value: IntValue<'ctx>, name: &str) -> Result<IntValue<'ctx>> {
4261 let width = value.get_type().get_bit_width();
4262 if width == 64 {
4263 return Ok(value);
4264 }
4265
4266 if width < 64 {
4267 return self
4268 .builder
4269 .build_int_s_extend(value, self.context.i64_type(), name)
4270 .map_err(|err| CodeGenError::Builder(err.to_string()));
4271 }
4272
4273 self.builder
4274 .build_int_truncate(value, self.context.i64_type(), name)
4275 .map_err(|err| CodeGenError::Builder(err.to_string()))
4276 }
4277
4278 pub(crate) fn dwarf_expression_unavailable_error(
4279 name: &str,
4280 availability: &Availability,
4281 pc_address: u64,
4282 ) -> CodeGenError {
4283 let reason = Self::format_availability_reason(availability);
4284 CodeGenError::VariableUnavailable(format!(
4285 "'{name}' is {reason}; cannot use it as a value expression at PC 0x{pc_address:x}"
4286 ))
4287 }
4288
4289 pub(crate) fn dwarf_lvalue_address_unavailable_error(
4290 name: &str,
4291 availability: &Availability,
4292 pc_address: u64,
4293 ) -> CodeGenError {
4294 let reason = Self::format_availability_reason(availability);
4295 CodeGenError::VariableUnavailable(format!(
4296 "'{name}' is {reason}; cannot take its address at PC 0x{pc_address:x}"
4297 ))
4298 }
4299
4300 fn format_availability_reason(availability: &Availability) -> String {
4301 match availability {
4302 Availability::OptimizedOut => "optimized out at the selected probe PC".to_string(),
4303 Availability::NotInScope => "not in scope at the selected probe PC".to_string(),
4304 Availability::Unsupported(reason) => {
4305 format!(
4306 "unsupported DWARF semantic shape: {}",
4307 Self::format_unsupported_reason(reason)
4308 )
4309 }
4310 Availability::Requires(requirement) => {
4311 format!(
4312 "requires unavailable runtime support: {}",
4313 Self::format_runtime_requirement(requirement)
4314 )
4315 }
4316 Availability::Ambiguous(reason) => {
4317 format!(
4318 "ambiguous DWARF semantic result: {}",
4319 Self::format_ambiguity_reason(reason)
4320 )
4321 }
4322 Availability::Available | Availability::PartiallyAvailable => "available".to_string(),
4323 }
4324 }
4325
4326 fn format_unsupported_reason(reason: &UnsupportedReason) -> String {
4327 match reason {
4328 UnsupportedReason::DwarfOp { op } => format!("unsupported DWARF op {op}"),
4329 UnsupportedReason::ExpressionShape { detail } => {
4330 format!("unsupported DWARF expression shape: {detail}")
4331 }
4332 UnsupportedReason::TypeLayout { detail } => {
4333 format!("unsupported type layout: {detail}")
4334 }
4335 UnsupportedReason::AddressClass { detail } => {
4336 format!("unsupported address class: {detail}")
4337 }
4338 UnsupportedReason::RegisterMapping { dwarf_reg } => {
4339 format!("unsupported DWARF register mapping for register {dwarf_reg}")
4340 }
4341 }
4342 }
4343
4344 fn format_runtime_requirement(requirement: &RuntimeRequirement) -> &'static str {
4345 match requirement {
4346 RuntimeRequirement::CallerFrame => "caller-frame recovery",
4347 RuntimeRequirement::SleepableUprobe => "sleepable uprobe support",
4348 RuntimeRequirement::UserMemoryRead => "user-memory read support",
4349 RuntimeRequirement::DwarfCfiRecovery => "DWARF CFI recovery",
4350 }
4351 }
4352
4353 fn format_ambiguity_reason(reason: &AmbiguityReason) -> String {
4354 match reason {
4355 AmbiguityReason::InlineContext { detail } => {
4356 format!("ambiguous inline context: {detail}")
4357 }
4358 AmbiguityReason::VariableDeclaration { detail } => {
4359 format!("ambiguous variable declaration: {detail}")
4360 }
4361 AmbiguityReason::TypeResolution { detail } => {
4362 format!("ambiguous type resolution: {detail}")
4363 }
4364 }
4365 }
4366
4367 fn expr_to_debug_string(expr: &crate::script::Expr) -> String {
4369 use crate::script::Expr;
4370
4371 match expr {
4372 Expr::Variable(name) => name.clone(),
4373 Expr::MemberAccess(obj, field) => {
4374 format!("{}.{}", Self::expr_to_debug_string(obj), field)
4375 }
4376 Expr::ArrayAccess(arr, _) => format!("{}[index]", Self::expr_to_debug_string(arr)),
4377 Expr::Cast { expr, target_type } => format!(
4378 "cast({}, \"{}\")",
4379 Self::expr_to_debug_string(expr),
4380 target_type
4381 ),
4382 Expr::ChainAccess(chain) => chain.join("."),
4383 Expr::PointerDeref(expr) => format!("*{}", Self::expr_to_debug_string(expr)),
4384 _ => "expr".to_string(),
4385 }
4386 }
4387}
4388
4389impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> {
4390 fn compile_string_comparison(
4393 &mut self,
4394 dwarf_expr: &Expr,
4395 lit: &str,
4396 is_equal: bool,
4397 ) -> Result<BasicValueEnum<'ctx>> {
4398 use ghostscope_dwarf::TypeInfo as TI;
4399
4400 let var = self
4402 .query_dwarf_for_complex_expr(dwarf_expr)?
4403 .ok_or_else(|| {
4404 CodeGenError::TypeError(
4405 "string comparison requires DWARF variable/expression".into(),
4406 )
4407 })?;
4408 let dwarf_type_opt = var.dwarf_type.as_ref();
4410
4411 enum ParsedKind {
4412 PtrChar,
4413 ArrChar(Option<u32>),
4414 Other,
4415 }
4416 fn parse_type_name(name: &str) -> ParsedKind {
4417 let lower = name.to_lowercase();
4418 let has_char = lower.contains("char");
4419 let is_ptr = lower.contains('*');
4420 if has_char && is_ptr {
4421 return ParsedKind::PtrChar;
4422 }
4423 if has_char && lower.contains('[') {
4424 let mut n: Option<u32> = None;
4426 if let Some(start) = lower.find('[') {
4427 if let Some(end) = lower[start + 1..].find(']') {
4428 let inside = &lower[start + 1..start + 1 + end];
4429 let digits: String =
4430 inside.chars().filter(|c| c.is_ascii_digit()).collect();
4431 if !digits.is_empty() {
4432 if let Ok(v) = digits.parse::<u32>() {
4433 n = Some(v);
4434 }
4435 }
4436 }
4437 }
4438 return ParsedKind::ArrChar(n);
4439 }
4440 ParsedKind::Other
4441 }
4442
4443 let lit_bytes = lit.as_bytes();
4444 let lit_len = lit_bytes.len() as u32;
4445 let one = self.context.bool_type().const_int(1, false);
4446 let zero = self.context.bool_type().const_zero();
4447
4448 let result = match dwarf_type_opt.map(ghostscope_dwarf::strip_type_aliases) {
4450 Some(TI::PointerType { target_type, .. }) => {
4452 let base = ghostscope_dwarf::strip_type_aliases(target_type.as_ref());
4454 let is_char_like = matches!(base, TI::BaseType { name, size, .. } if name.contains("char") && *size == 1);
4455 if !is_char_like {
4456 return Err(CodeGenError::TypeError(
4457 "automatic string comparison only supports char*".into(),
4458 ));
4459 }
4460
4461 let pc_address = self.get_compile_time_context()?.pc_address;
4463 let val_any = self.variable_read_plan_to_llvm_value(&var, pc_address, None)?;
4464 let ptr_i64 = match val_any {
4465 BasicValueEnum::IntValue(iv) => iv,
4466 BasicValueEnum::PointerValue(pv) => self
4467 .builder
4468 .build_ptr_to_int(pv, self.context.i64_type(), "ptr_as_i64")
4469 .map_err(|e| CodeGenError::Builder(e.to_string()))?,
4470 _ => {
4471 return Err(CodeGenError::TypeError(
4472 "pointer value must be integer or pointer".into(),
4473 ))
4474 }
4475 };
4476 let need = lit_len + 1;
4477 let (buf_global, ret_len, arr_ty) = self.read_user_cstr_into_buffer(
4478 RuntimeAddress::available(ptr_i64, self.context),
4479 need,
4480 "_gs_strbuf",
4481 )?;
4482
4483 let i64_ty = self.context.i64_type();
4485 let expect_len = i64_ty.const_int(need as u64, false);
4486 let len_ok = self
4487 .builder
4488 .build_int_compare(inkwell::IntPredicate::EQ, ret_len, expect_len, "str_len_ok")
4489 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4490
4491 let i32_ty = self.context.i32_type();
4493 let idx0 = i32_ty.const_zero();
4494 let idx_l = i32_ty.const_int(lit_len as u64, false);
4495 let char_ptr = unsafe {
4498 self.builder
4499 .build_gep(arr_ty, buf_global, &[idx0, idx_l], "nul_ptr")
4500 .map_err(|e| CodeGenError::Builder(e.to_string()))?
4501 };
4502 let c = self
4503 .builder
4504 .build_load(self.context.i8_type(), char_ptr, "c_l")
4505 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4506 let c = match c {
4507 BasicValueEnum::IntValue(iv) => iv,
4508 _ => return Err(CodeGenError::LLVMError("load did not return i8".into())),
4509 };
4510 let nul_ok = self
4511 .builder
4512 .build_int_compare(
4513 inkwell::IntPredicate::EQ,
4514 c,
4515 self.context.i8_type().const_zero(),
4516 "nul_ok",
4517 )
4518 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4519
4520 let mut acc = self.context.i8_type().const_zero();
4522 for (i, b) in lit_bytes.iter().enumerate() {
4523 let idx_i = i32_ty.const_int(i as u64, false);
4524 let ptr_i = unsafe {
4526 self.builder
4527 .build_gep(arr_ty, buf_global, &[idx0, idx_i], "ch_ptr")
4528 .map_err(|e| CodeGenError::Builder(e.to_string()))?
4529 };
4530 let ch = self
4531 .builder
4532 .build_load(self.context.i8_type(), ptr_i, "ch")
4533 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4534 let ch = match ch {
4535 BasicValueEnum::IntValue(iv) => iv,
4536 _ => return Err(CodeGenError::LLVMError("load did not return i8".into())),
4537 };
4538 let expect = self.context.i8_type().const_int(*b as u64, false);
4539 let diff = self
4540 .builder
4541 .build_xor(ch, expect, "diff")
4542 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4543 acc = self
4544 .builder
4545 .build_or(acc, diff, "acc_or")
4546 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4547 }
4548 let eq_bytes = self
4549 .builder
4550 .build_int_compare(
4551 inkwell::IntPredicate::EQ,
4552 acc,
4553 self.context.i8_type().const_zero(),
4554 "acc_zero",
4555 )
4556 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4557 let ok1 = self
4558 .builder
4559 .build_and(len_ok, nul_ok, "ok_len_nul")
4560 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4561 self.builder
4562 .build_and(ok1, eq_bytes, "str_eq")
4563 .map_err(|e| CodeGenError::Builder(e.to_string()))?
4564 }
4565 Some(TI::ArrayType {
4567 element_type,
4568 element_count,
4569 total_size,
4570 }) => {
4571 let elem = ghostscope_dwarf::strip_type_aliases(element_type.as_ref());
4572 let is_char_like = matches!(elem, TI::BaseType { name, size, .. } if name.contains("char") && *size == 1);
4573 if !is_char_like {
4574 return Err(CodeGenError::TypeError(
4575 "automatic string comparison only supports char[N]".into(),
4576 ));
4577 }
4578 let n_opt = element_count.or_else(|| total_size.map(|ts| ts));
4580 let n = if let Some(nv) = n_opt { nv as u32 } else { 0 };
4581 if n == 0 {
4582 return Err(CodeGenError::TypeError(
4583 "array size unknown for char[N] comparison".into(),
4584 ));
4585 }
4586 if lit_len + 1 > n {
4588 return Ok((if is_equal { zero } else { one }).into());
4590 }
4591 let status_ptr = if self.condition_context_active {
4592 Some(self.get_or_create_cond_error_global())
4593 } else {
4594 None
4595 };
4596 let pc_address = self.get_compile_time_context()?.pc_address;
4597 let addr =
4598 self.variable_read_plan_to_runtime_address(&var, pc_address, status_ptr)?;
4599 let (buf_global, status, arr_ty) =
4601 self.read_user_bytes_into_buffer(addr, lit_len + 1, "_gs_arrbuf")?;
4602 let status_ok = self
4604 .builder
4605 .build_int_compare(
4606 inkwell::IntPredicate::EQ,
4607 status,
4608 self.context.i64_type().const_zero(),
4609 "rd_ok",
4610 )
4611 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4612 let i32_ty = self.context.i32_type();
4614 let idx0 = i32_ty.const_zero();
4615 let idx_l = i32_ty.const_int(lit_len as u64, false);
4616 let char_ptr = unsafe {
4619 self.builder
4620 .build_gep(arr_ty, buf_global, &[idx0, idx_l], "nul_ptr")
4621 .map_err(|e| CodeGenError::Builder(e.to_string()))?
4622 };
4623 let c = self
4624 .builder
4625 .build_load(self.context.i8_type(), char_ptr, "c_l")
4626 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4627 let c = match c {
4628 BasicValueEnum::IntValue(iv) => iv,
4629 _ => return Err(CodeGenError::LLVMError("load did not return i8".into())),
4630 };
4631 let nul_ok = self
4632 .builder
4633 .build_int_compare(
4634 inkwell::IntPredicate::EQ,
4635 c,
4636 self.context.i8_type().const_zero(),
4637 "nul_ok",
4638 )
4639 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4640 let mut acc = self.context.i8_type().const_zero();
4642 for (i, b) in lit_bytes.iter().enumerate() {
4643 let idx_i = i32_ty.const_int(i as u64, false);
4644 let ptr_i = unsafe {
4646 self.builder
4647 .build_gep(arr_ty, buf_global, &[idx0, idx_i], "ch_ptr")
4648 .map_err(|e| CodeGenError::Builder(e.to_string()))?
4649 };
4650 let ch = self
4651 .builder
4652 .build_load(self.context.i8_type(), ptr_i, "ch")
4653 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4654 let ch = match ch {
4655 BasicValueEnum::IntValue(iv) => iv,
4656 _ => return Err(CodeGenError::LLVMError("load did not return i8".into())),
4657 };
4658 let expect = self.context.i8_type().const_int(*b as u64, false);
4659 let diff = self
4660 .builder
4661 .build_xor(ch, expect, "diff")
4662 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4663 acc = self
4664 .builder
4665 .build_or(acc, diff, "acc_or")
4666 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4667 }
4668 let eq_bytes = self
4669 .builder
4670 .build_int_compare(
4671 inkwell::IntPredicate::EQ,
4672 acc,
4673 self.context.i8_type().const_zero(),
4674 "acc_zero",
4675 )
4676 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4677 let ok1 = self
4678 .builder
4679 .build_and(status_ok, nul_ok, "ok_len_nul")
4680 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4681 self.builder
4682 .build_and(ok1, eq_bytes, "arr_eq")
4683 .map_err(|e| CodeGenError::Builder(e.to_string()))?
4684 }
4685 None => {
4686 let status_ptr = if self.condition_context_active {
4687 Some(self.get_or_create_cond_error_global())
4688 } else {
4689 None
4690 };
4691 let pc_address = self.get_compile_time_context()?.pc_address;
4692 let addr =
4693 self.variable_read_plan_to_runtime_address(&var, pc_address, status_ptr)?;
4694 match parse_type_name(&var.type_name) {
4696 ParsedKind::PtrChar => {
4697 let ptr_any = self.generate_memory_read(
4699 addr,
4700 ghostscope_dwarf::MemoryAccessSize::U64,
4701 None,
4702 )?;
4703 let ptr_i64 = match ptr_any {
4704 BasicValueEnum::IntValue(iv) => iv,
4705 _ => {
4706 return Err(CodeGenError::LLVMError(
4707 "pointer load did not return integer".to_string(),
4708 ))
4709 }
4710 };
4711 let need = lit_len + 1;
4712 let (buf_global, ret_len, arr_ty) = self.read_user_cstr_into_buffer(
4713 RuntimeAddress::available(ptr_i64, self.context),
4714 need,
4715 "_gs_strbuf",
4716 )?;
4717
4718 let i64_ty = self.context.i64_type();
4719 let expect_len = i64_ty.const_int(need as u64, false);
4720 let len_ok = self
4721 .builder
4722 .build_int_compare(
4723 inkwell::IntPredicate::EQ,
4724 ret_len,
4725 expect_len,
4726 "str_len_ok",
4727 )
4728 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4729
4730 let i32_ty = self.context.i32_type();
4731 let idx0 = i32_ty.const_zero();
4732 let idx_l = i32_ty.const_int(lit_len as u64, false);
4733 let char_ptr = unsafe {
4736 self.builder
4737 .build_gep(arr_ty, buf_global, &[idx0, idx_l], "nul_ptr")
4738 .map_err(|e| CodeGenError::Builder(e.to_string()))?
4739 };
4740 let c = self
4741 .builder
4742 .build_load(self.context.i8_type(), char_ptr, "c_l")
4743 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4744 let c = match c {
4745 BasicValueEnum::IntValue(iv) => iv,
4746 _ => {
4747 return Err(CodeGenError::LLVMError(
4748 "load did not return i8".into(),
4749 ))
4750 }
4751 };
4752 let nul_ok = self
4753 .builder
4754 .build_int_compare(
4755 inkwell::IntPredicate::EQ,
4756 c,
4757 self.context.i8_type().const_zero(),
4758 "nul_ok",
4759 )
4760 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4761
4762 let mut acc = self.context.i8_type().const_zero();
4763 for (i, b) in lit_bytes.iter().enumerate() {
4764 let idx_i = i32_ty.const_int(i as u64, false);
4765 let ptr_i = unsafe {
4767 self.builder
4768 .build_gep(arr_ty, buf_global, &[idx0, idx_i], "ch_ptr")
4769 .map_err(|e| CodeGenError::Builder(e.to_string()))?
4770 };
4771 let ch = self
4772 .builder
4773 .build_load(self.context.i8_type(), ptr_i, "ch")
4774 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4775 let ch = match ch {
4776 BasicValueEnum::IntValue(iv) => iv,
4777 _ => {
4778 return Err(CodeGenError::LLVMError(
4779 "load did not return i8".into(),
4780 ))
4781 }
4782 };
4783 let expect = self.context.i8_type().const_int(*b as u64, false);
4784 let diff = self
4785 .builder
4786 .build_xor(ch, expect, "diff")
4787 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4788 acc = self
4789 .builder
4790 .build_or(acc, diff, "acc_or")
4791 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4792 }
4793 let eq_bytes = self
4794 .builder
4795 .build_int_compare(
4796 inkwell::IntPredicate::EQ,
4797 acc,
4798 self.context.i8_type().const_zero(),
4799 "acc_zero",
4800 )
4801 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4802 let ok1 = self
4803 .builder
4804 .build_and(len_ok, nul_ok, "ok_len_nul")
4805 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4806 self.builder
4807 .build_and(ok1, eq_bytes, "str_eq")
4808 .map_err(|e| CodeGenError::Builder(e.to_string()))?
4809 }
4810 ParsedKind::ArrChar(n_opt) => {
4811 if let Some(n) = n_opt {
4813 if lit_len + 1 > n {
4814 return Ok((if is_equal { zero } else { one }).into());
4815 }
4816 }
4817 let (buf_global, status, arr_ty) =
4818 self.read_user_bytes_into_buffer(addr, lit_len + 1, "_gs_arrbuf")?;
4819 let status_ok = self
4820 .builder
4821 .build_int_compare(
4822 inkwell::IntPredicate::EQ,
4823 status,
4824 self.context.i64_type().const_zero(),
4825 "rd_ok",
4826 )
4827 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4828 let i32_ty = self.context.i32_type();
4829 let idx0 = i32_ty.const_zero();
4830 let idx_l = i32_ty.const_int(lit_len as u64, false);
4831 let char_ptr = unsafe {
4834 self.builder
4835 .build_gep(arr_ty, buf_global, &[idx0, idx_l], "nul_ptr")
4836 .map_err(|e| CodeGenError::Builder(e.to_string()))?
4837 };
4838 let c = self
4839 .builder
4840 .build_load(self.context.i8_type(), char_ptr, "c_l")
4841 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4842 let c = match c {
4843 BasicValueEnum::IntValue(iv) => iv,
4844 _ => {
4845 return Err(CodeGenError::LLVMError(
4846 "load did not return i8".into(),
4847 ))
4848 }
4849 };
4850 let nul_ok = self
4851 .builder
4852 .build_int_compare(
4853 inkwell::IntPredicate::EQ,
4854 c,
4855 self.context.i8_type().const_zero(),
4856 "nul_ok",
4857 )
4858 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4859 let mut acc = self.context.i8_type().const_zero();
4860 for (i, b) in lit_bytes.iter().enumerate() {
4861 let idx_i = i32_ty.const_int(i as u64, false);
4862 let ptr_i = unsafe {
4864 self.builder
4865 .build_gep(arr_ty, buf_global, &[idx0, idx_i], "ch_ptr")
4866 .map_err(|e| CodeGenError::Builder(e.to_string()))?
4867 };
4868 let ch = self
4869 .builder
4870 .build_load(self.context.i8_type(), ptr_i, "ch")
4871 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4872 let ch = match ch {
4873 BasicValueEnum::IntValue(iv) => iv,
4874 _ => {
4875 return Err(CodeGenError::LLVMError(
4876 "load did not return i8".into(),
4877 ))
4878 }
4879 };
4880 let expect = self.context.i8_type().const_int(*b as u64, false);
4881 let diff = self
4882 .builder
4883 .build_xor(ch, expect, "diff")
4884 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4885 acc = self
4886 .builder
4887 .build_or(acc, diff, "acc_or")
4888 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4889 }
4890 let eq_bytes = self
4891 .builder
4892 .build_int_compare(
4893 inkwell::IntPredicate::EQ,
4894 acc,
4895 self.context.i8_type().const_zero(),
4896 "acc_zero",
4897 )
4898 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4899 let ok1 = self
4900 .builder
4901 .build_and(status_ok, nul_ok, "ok_len_nul")
4902 .map_err(|e| CodeGenError::Builder(e.to_string()))?;
4903 self.builder
4904 .build_and(ok1, eq_bytes, "arr_eq")
4905 .map_err(|e| CodeGenError::Builder(e.to_string()))?
4906 }
4907 ParsedKind::Other => {
4908 return Err(CodeGenError::TypeError(format!(
4909 "string comparison unsupported for type name '{}' without DWARF type",
4910 var.type_name
4911 )));
4912 }
4913 }
4914 }
4915 Some(_) => {
4916 return Err(CodeGenError::TypeError(
4917 "string comparison only supports char* or char[N]".into(),
4918 ));
4919 }
4920 };
4921
4922 let final_bool = if is_equal {
4924 result
4925 } else {
4926 self.builder
4927 .build_not(result, "not_eq")
4928 .map_err(|e| CodeGenError::Builder(e.to_string()))?
4929 };
4930 Ok(final_bool.into())
4931 }
4932}