Skip to main content

ghostscope_compiler/ebpf/codegen/
print_variable_index.rs

1use super::*;
2
3impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> {
4    /// Generate eBPF code for PrintVariableIndex instruction
5    pub fn generate_print_variable_index(
6        &mut self,
7        var_name_index: u16,
8        type_encoding: TypeKind,
9        var_name: &str,
10    ) -> Result<()> {
11        info!(
12            "Generating PrintVariableIndex instruction: var_name_index={}, type={:?}, var_name={}",
13            var_name_index, type_encoding, var_name
14        );
15
16        // Resolve type_index from DWARF if available; otherwise synthesize from TypeKind
17        let type_index = match self.query_dwarf_for_variable(var_name)? {
18            Some(var) => match var.dwarf_type {
19                Some(ref t) => self.trace_context.add_type(t.clone()),
20                None => self.add_synthesized_type_index_for_kind(type_encoding),
21            },
22            None => {
23                // Variable not found via DWARF; fall back to synthesized type info based on TypeKind
24                self.add_synthesized_type_index_for_kind(type_encoding)
25            }
26        };
27
28        self.generate_successful_variable_instruction(
29            var_name_index,
30            type_encoding,
31            type_index,
32            var_name,
33        )
34    }
35
36    /// Generate successful variable instruction with data
37    pub(super) fn generate_successful_variable_instruction(
38        &mut self,
39        var_name_index: u16,
40        type_encoding: TypeKind,
41        type_index: u16,
42        var_name: &str,
43    ) -> Result<()> {
44        // Determine data size based on type
45        let data_size = match type_encoding {
46            TypeKind::U8 | TypeKind::I8 | TypeKind::Bool | TypeKind::Char => 1,
47            TypeKind::U16 | TypeKind::I16 => 2,
48            TypeKind::U32 | TypeKind::I32 | TypeKind::F32 => 4,
49            TypeKind::U64 | TypeKind::I64 | TypeKind::F64 | TypeKind::Pointer => 8,
50            _ => 8, // Default to 8 bytes for complex types
51        };
52
53        // Reserve space directly in per-CPU accumulation buffer
54        let inst_buffer = self
55            .reserve_instruction_region_or_return_zero(
56                (std::mem::size_of::<InstructionHeader>()
57                    + std::mem::size_of::<PrintVariableIndexData>()
58                    + data_size as usize) as u64,
59            )?
60            .into_value_after_runtime_returns();
61
62        // Avoid memset; global buffer is zero-initialized
63
64        // Store instruction type at offset 0
65        let inst_type_val = self
66            .context
67            .i8_type()
68            .const_int(InstructionType::PrintVariableIndex as u64, false);
69        self.builder
70            .build_store(inst_buffer, inst_type_val)
71            .map_err(|e| CodeGenError::LLVMError(format!("Failed to store inst_type: {e}")))?;
72
73        // Store data_length field of InstructionHeader
74        // SAFETY: inst_buffer points at a reserved PrintVariableIndex instruction
75        // region and data_length is within InstructionHeader.
76        let data_length_ptr = unsafe {
77            self.builder
78                .build_gep(
79                    self.context.i8_type(),
80                    inst_buffer,
81                    &[self.context.i32_type().const_int(
82                        std::mem::offset_of!(InstructionHeader, data_length) as u64,
83                        false,
84                    )],
85                    "data_length_ptr",
86                )
87                .map_err(|e| {
88                    CodeGenError::LLVMError(format!("Failed to get data_length GEP: {e}"))
89                })?
90        };
91        let data_length_i16_ptr = self
92            .builder
93            .build_pointer_cast(
94                data_length_ptr,
95                self.context.ptr_type(AddressSpace::default()),
96                "data_length_i16_ptr",
97            )
98            .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast data_length ptr: {e}")))?;
99        let total_data_length = std::mem::size_of::<PrintVariableIndexData>() + data_size as usize;
100        let data_length_val = self
101            .context
102            .i16_type()
103            .const_int(total_data_length as u64, false);
104        self.builder
105            .build_store(data_length_i16_ptr, data_length_val)
106            .map_err(|e| CodeGenError::LLVMError(format!("Failed to store data_length: {e}")))?;
107
108        // Write PrintVariableIndexData after InstructionHeader
109        // SAFETY: variable_data_start is exactly after InstructionHeader in the
110        // reserved instruction region.
111        let variable_data_start = unsafe {
112            self.builder
113                .build_gep(
114                    self.context.i8_type(),
115                    inst_buffer,
116                    &[self
117                        .context
118                        .i32_type()
119                        .const_int(std::mem::size_of::<InstructionHeader>() as u64, false)],
120                    "variable_data_start",
121                )
122                .map_err(|e| {
123                    CodeGenError::LLVMError(format!("Failed to get variable_data_start GEP: {e}"))
124                })?
125        };
126
127        // Store var_name_index using correct offset
128        // SAFETY: var_name_index offset is within PrintVariableIndexData.
129        let var_name_index_ptr = unsafe {
130            self.builder
131                .build_gep(
132                    self.context.i8_type(),
133                    variable_data_start,
134                    &[self.context.i32_type().const_int(
135                        std::mem::offset_of!(PrintVariableIndexData, var_name_index) as u64,
136                        false,
137                    )],
138                    "var_name_index_ptr",
139                )
140                .map_err(|e| {
141                    CodeGenError::LLVMError(format!("Failed to get var_name_index GEP: {e}"))
142                })?
143        };
144        let var_name_index_i16_ptr = self
145            .builder
146            .build_pointer_cast(
147                var_name_index_ptr,
148                self.context.ptr_type(AddressSpace::default()),
149                "var_name_index_i16_ptr",
150            )
151            .map_err(|e| {
152                CodeGenError::LLVMError(format!("Failed to cast var_name_index ptr: {e}"))
153            })?;
154        let var_name_index_val = self
155            .context
156            .i16_type()
157            .const_int(var_name_index as u64, false);
158        self.builder
159            .build_store(var_name_index_i16_ptr, var_name_index_val)
160            .map_err(|e| CodeGenError::LLVMError(format!("Failed to store var_name_index: {e}")))?;
161
162        // Store type_encoding using correct offset
163        // SAFETY: type_encoding offset is within PrintVariableIndexData.
164        let type_encoding_ptr = unsafe {
165            self.builder
166                .build_gep(
167                    self.context.i8_type(),
168                    variable_data_start,
169                    &[self.context.i32_type().const_int(
170                        std::mem::offset_of!(PrintVariableIndexData, type_encoding) as u64,
171                        false,
172                    )],
173                    "type_encoding_ptr",
174                )
175                .map_err(|e| {
176                    CodeGenError::LLVMError(format!("Failed to get type_encoding GEP: {e}"))
177                })?
178        };
179        let type_encoding_val = self
180            .context
181            .i8_type()
182            .const_int(type_encoding as u8 as u64, false);
183        self.builder
184            .build_store(type_encoding_ptr, type_encoding_val)
185            .map_err(|e| CodeGenError::LLVMError(format!("Failed to store type_encoding: {e}")))?;
186
187        // Store data_len using correct offset
188        // SAFETY: data_len offset is within PrintVariableIndexData.
189        let data_len_ptr = unsafe {
190            self.builder
191                .build_gep(
192                    self.context.i8_type(),
193                    variable_data_start,
194                    &[self.context.i32_type().const_int(
195                        std::mem::offset_of!(PrintVariableIndexData, data_len) as u64,
196                        false,
197                    )],
198                    "data_len_ptr",
199                )
200                .map_err(|e| CodeGenError::LLVMError(format!("Failed to get data_len GEP: {e}")))?
201        };
202        let data_len_i16_ptr = self
203            .builder
204            .build_pointer_cast(
205                data_len_ptr,
206                self.context.ptr_type(AddressSpace::default()),
207                "data_len_i16_ptr",
208            )
209            .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast data_len ptr: {e}")))?;
210        let data_len_val = self.context.i16_type().const_int(data_size as u64, false); // Store as u16
211        self.builder
212            .build_store(data_len_i16_ptr, data_len_val)
213            .map_err(|e| CodeGenError::LLVMError(format!("Failed to store data_len: {e}")))?;
214
215        // Store type_index using correct offset
216        // SAFETY: type_index offset is within PrintVariableIndexData.
217        let type_index_ptr = unsafe {
218            self.builder
219                .build_gep(
220                    self.context.i8_type(),
221                    variable_data_start,
222                    &[self.context.i32_type().const_int(
223                        std::mem::offset_of!(PrintVariableIndexData, type_index) as u64,
224                        false,
225                    )],
226                    "type_index_ptr",
227                )
228                .map_err(|e| {
229                    CodeGenError::LLVMError(format!("Failed to get type_index GEP: {e}"))
230                })?
231        };
232        let type_index_i16_ptr = self
233            .builder
234            .build_pointer_cast(
235                type_index_ptr,
236                self.context.ptr_type(AddressSpace::default()),
237                "type_index_i16_ptr",
238            )
239            .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast type_index ptr: {e}")))?;
240        let type_index_val = self.context.i16_type().const_int(type_index as u64, false);
241        self.builder
242            .build_store(type_index_i16_ptr, type_index_val)
243            .map_err(|e| CodeGenError::LLVMError(format!("Failed to store type_index: {e}")))?;
244
245        // Store status (set to 0)
246        // SAFETY: status offset is within PrintVariableIndexData.
247        let status_ptr = unsafe {
248            self.builder
249                .build_gep(
250                    self.context.i8_type(),
251                    variable_data_start,
252                    &[self.context.i32_type().const_int(
253                        std::mem::offset_of!(PrintVariableIndexData, status) as u64,
254                        false,
255                    )],
256                    "status_ptr",
257                )
258                .map_err(|e| CodeGenError::LLVMError(format!("Failed to get status GEP: {e}")))?
259        };
260        let status_val = self
261            .context
262            .i8_type()
263            .const_int(VariableStatus::Ok as u64, false);
264        self.builder
265            .build_store(status_ptr, status_val)
266            .map_err(|e| CodeGenError::LLVMError(format!("Failed to store status: {e}")))?;
267
268        let var_data = self.resolve_variable_value(var_name, type_encoding, Some(status_ptr))?;
269
270        // Store actual variable data after PrintVariableIndexData structure
271        // SAFETY: var_data_ptr starts after PrintVariableIndexData inside the
272        // reserved instruction region, which included data_size bytes.
273        let var_data_ptr = unsafe {
274            self.builder
275                .build_gep(
276                    self.context.i8_type(),
277                    variable_data_start,
278                    &[self
279                        .context
280                        .i32_type()
281                        .const_int(std::mem::size_of::<PrintVariableIndexData>() as u64, false)],
282                    "var_data_ptr",
283                )
284                .map_err(|e| CodeGenError::LLVMError(format!("Failed to get var_data GEP: {e}")))?
285        };
286
287        // Store the runtime variable value based on data size
288        // The var_data contains the LLVM IR value (from register/memory access)
289        match data_size {
290            1 => {
291                // Store as i8
292                let truncated = match var_data {
293                    BasicValueEnum::IntValue(int_val) => self
294                        .builder
295                        .build_int_truncate(int_val, self.context.i8_type(), "truncated_i8")
296                        .map_err(|e| {
297                            CodeGenError::LLVMError(format!("Failed to truncate to i8: {e}"))
298                        })?,
299                    _ => {
300                        return Err(CodeGenError::LLVMError(
301                            "Expected integer value for integer type".to_string(),
302                        ));
303                    }
304                };
305                self.builder
306                    .build_store(var_data_ptr, truncated)
307                    .map_err(|e| {
308                        CodeGenError::LLVMError(format!("Failed to store i8 data: {e}"))
309                    })?;
310            }
311            2 => {
312                // Store as i16
313                let truncated = match var_data {
314                    BasicValueEnum::IntValue(int_val) => self
315                        .builder
316                        .build_int_truncate(int_val, self.context.i16_type(), "truncated_i16")
317                        .map_err(|e| {
318                            CodeGenError::LLVMError(format!("Failed to truncate to i16: {e}"))
319                        })?,
320                    _ => {
321                        return Err(CodeGenError::LLVMError(
322                            "Expected integer value for integer type".to_string(),
323                        ));
324                    }
325                };
326                let i16_ptr = self
327                    .builder
328                    .build_pointer_cast(
329                        var_data_ptr,
330                        self.context.ptr_type(AddressSpace::default()),
331                        "i16_ptr",
332                    )
333                    .map_err(|e| {
334                        CodeGenError::LLVMError(format!("Failed to cast to i16 ptr: {e}"))
335                    })?;
336                self.builder.build_store(i16_ptr, truncated).map_err(|e| {
337                    CodeGenError::LLVMError(format!("Failed to store i16 data: {e}"))
338                })?;
339            }
340            4 => {
341                // Store as i32 or f32
342                match var_data {
343                    BasicValueEnum::IntValue(int_val) => {
344                        let truncated = self
345                            .builder
346                            .build_int_truncate(int_val, self.context.i32_type(), "truncated_i32")
347                            .map_err(|e| {
348                                CodeGenError::LLVMError(format!("Failed to truncate to i32: {e}"))
349                            })?;
350                        let i32_ptr = self
351                            .builder
352                            .build_pointer_cast(
353                                var_data_ptr,
354                                self.context.ptr_type(AddressSpace::default()),
355                                "i32_ptr",
356                            )
357                            .map_err(|e| {
358                                CodeGenError::LLVMError(format!("Failed to cast to i32 ptr: {e}"))
359                            })?;
360                        self.builder.build_store(i32_ptr, truncated).map_err(|e| {
361                            CodeGenError::LLVMError(format!("Failed to store i32 data: {e}"))
362                        })?;
363                    }
364                    BasicValueEnum::FloatValue(float_val) => {
365                        let f32_ptr = self
366                            .builder
367                            .build_pointer_cast(
368                                var_data_ptr,
369                                self.context.ptr_type(AddressSpace::default()),
370                                "f32_ptr",
371                            )
372                            .map_err(|e| {
373                                CodeGenError::LLVMError(format!("Failed to cast to f32 ptr: {e}"))
374                            })?;
375                        self.builder.build_store(f32_ptr, float_val).map_err(|e| {
376                            CodeGenError::LLVMError(format!("Failed to store f32 data: {e}"))
377                        })?;
378                    }
379                    _ => {
380                        return Err(CodeGenError::LLVMError(
381                            "Expected integer or float value for 4-byte type".to_string(),
382                        ));
383                    }
384                }
385            }
386            8 => {
387                // Store as i64, f64, or pointer
388                match var_data {
389                    BasicValueEnum::IntValue(int_val) => {
390                        let i64_ptr = self
391                            .builder
392                            .build_pointer_cast(
393                                var_data_ptr,
394                                self.context.ptr_type(AddressSpace::default()),
395                                "i64_ptr",
396                            )
397                            .map_err(|e| {
398                                CodeGenError::LLVMError(format!("Failed to cast to i64 ptr: {e}"))
399                            })?;
400                        self.builder.build_store(i64_ptr, int_val).map_err(|e| {
401                            CodeGenError::LLVMError(format!("Failed to store i64 data: {e}"))
402                        })?;
403                    }
404                    BasicValueEnum::FloatValue(float_val) => {
405                        let f64_ptr = self
406                            .builder
407                            .build_pointer_cast(
408                                var_data_ptr,
409                                self.context.ptr_type(AddressSpace::default()),
410                                "f64_ptr",
411                            )
412                            .map_err(|e| {
413                                CodeGenError::LLVMError(format!("Failed to cast to f64 ptr: {e}"))
414                            })?;
415                        self.builder.build_store(f64_ptr, float_val).map_err(|e| {
416                            CodeGenError::LLVMError(format!("Failed to store f64 data: {e}"))
417                        })?;
418                    }
419                    BasicValueEnum::PointerValue(ptr_val) => {
420                        // Store pointer as u64
421                        let ptr_int = self
422                            .builder
423                            .build_ptr_to_int(ptr_val, self.context.i64_type(), "ptr_as_int")
424                            .map_err(|e| {
425                                CodeGenError::LLVMError(format!(
426                                    "Failed to convert ptr to int: {e}"
427                                ))
428                            })?;
429                        let i64_ptr = self
430                            .builder
431                            .build_pointer_cast(
432                                var_data_ptr,
433                                self.context.ptr_type(AddressSpace::default()),
434                                "i64_ptr",
435                            )
436                            .map_err(|e| {
437                                CodeGenError::LLVMError(format!("Failed to cast to i64 ptr: {e}"))
438                            })?;
439                        self.builder.build_store(i64_ptr, ptr_int).map_err(|e| {
440                            CodeGenError::LLVMError(format!("Failed to store pointer data: {e}"))
441                        })?;
442                    }
443                    _ => {
444                        return Err(CodeGenError::LLVMError(
445                            "Expected integer, float, or pointer value for 8-byte type".to_string(),
446                        ));
447                    }
448                }
449            }
450            _ => {
451                return Err(CodeGenError::LLVMError(format!(
452                    "Unsupported data size: {data_size}"
453                )));
454            }
455        }
456
457        // Already accumulated; EndInstruction will send the whole event
458        Ok(())
459    }
460    /// Resolve variable value from script variables first, then DWARF
461    pub(super) fn resolve_variable_value(
462        &mut self,
463        var_name: &str,
464        type_encoding: TypeKind,
465        status_ptr: Option<inkwell::values::PointerValue<'ctx>>,
466    ) -> Result<BasicValueEnum<'ctx>> {
467        info!(
468            "Resolving variable value: {} ({:?})",
469            var_name, type_encoding
470        );
471
472        // 1) Script variable first
473        if self.variable_exists(var_name) {
474            info!("Found script variable for '{}', loading value", var_name);
475            return self.load_variable(var_name);
476        }
477
478        // 2) DWARF variable as fallback
479        match self.query_dwarf_for_variable(var_name)? {
480            Some(var_info) => {
481                info!(
482                    "Found DWARF variable read plan: {} availability={:?}",
483                    var_name, var_info.availability
484                );
485
486                // Require DWARF type information
487                var_info.dwarf_type.as_ref().ok_or_else(|| {
488                    CodeGenError::DwarfError(format!(
489                        "Variable '{var_name}' has no type information in DWARF"
490                    ))
491                })?;
492
493                let compile_context = self.get_compile_time_context()?;
494                self.variable_read_plan_to_llvm_value(
495                    &var_info,
496                    compile_context.pc_address,
497                    status_ptr,
498                )
499            }
500            None => {
501                let compile_context = self.get_compile_time_context()?;
502                warn!(
503                    "Variable '{}' not found in DWARF at address 0x{:x}",
504                    var_name, compile_context.pc_address
505                );
506                Err(CodeGenError::VariableNotFound(var_name.to_string()))
507            }
508        }
509    }
510}