@@ -173,6 +173,15 @@ def _populate_fval(ftype, node, fmt_parts, exprs):
173173 raise NotImplementedError (
174174 f"Unsupported pointer target type in f-string: { target } "
175175 )
176+ elif isinstance (ftype , ir .ArrayType ):
177+ if isinstance (ftype .element , ir .IntType ) and ftype .element .width == 8 :
178+ # Char array
179+ fmt_parts .append ("%s" )
180+ exprs .append (node )
181+ else :
182+ raise NotImplementedError (
183+ f"Unsupported array element type in f-string: { ftype .element } "
184+ )
176185 else :
177186 raise NotImplementedError (f"Unsupported field type in f-string: { ftype } " )
178187
@@ -197,44 +206,100 @@ def _create_format_string_global(fmt_str, func, module, builder):
197206
198207def _prepare_expr_args (expr , func , module , builder , local_sym_tab , struct_sym_tab ):
199208 """Evaluate and prepare an expression to use as an arg for bpf_printk."""
200- val , _ = eval_expr (
201- func ,
202- module ,
203- builder ,
204- expr ,
205- local_sym_tab ,
206- None ,
207- struct_sym_tab ,
209+
210+ # Special case: struct field char array needs pointer to first element
211+ char_array_ptr = _get_struct_char_array_ptr (
212+ expr , builder , local_sym_tab , struct_sym_tab
208213 )
214+ if char_array_ptr :
215+ return char_array_ptr
209216
210- if val :
211- if isinstance (val .type , ir .PointerType ):
212- target , depth = get_base_type_and_depth (val .type )
213- if isinstance (target , ir .IntType ):
214- if target .width >= 32 :
215- val = deref_to_depth (func , builder , val , depth )
216- val = builder .sext (val , ir .IntType (64 ))
217- elif target .width == 8 and depth == 1 :
218- # NOTE: i8* is string, no need to deref
219- pass
217+ # Regular expression evaluation
218+ val , _ = eval_expr (func , module , builder , expr , local_sym_tab , None , struct_sym_tab )
220219
221- else :
222- logger .warning (
223- "Only int and ptr supported in bpf_printk args. Others default to 0."
224- )
225- val = ir .Constant (ir .IntType (64 ), 0 )
226- elif isinstance (val .type , ir .IntType ):
227- if val .type .width < 64 :
228- val = builder .sext (val , ir .IntType (64 ))
229- else :
230- logger .warning (
231- "Only int and ptr supported in bpf_printk args. Others default to 0."
232- )
233- val = ir .Constant (ir .IntType (64 ), 0 )
234- return val
220+ if not val :
221+ logger .warning ("Failed to evaluate expression for bpf_printk, defaulting to 0" )
222+ return ir .Constant (ir .IntType (64 ), 0 )
223+
224+ # Convert value to bpf_printk compatible type
225+ if isinstance (val .type , ir .PointerType ):
226+ return _handle_pointer_arg (val , func , builder )
227+ elif isinstance (val .type , ir .IntType ):
228+ return _handle_int_arg (val , builder )
235229 else :
236- logger .warning (
237- "Failed to evaluate expression for bpf_printk argument. "
238- "It will be converted to 0."
239- )
230+ logger .warning (f"Unsupported type { val .type } in bpf_printk, defaulting to 0" )
240231 return ir .Constant (ir .IntType (64 ), 0 )
232+
233+
234+ def _get_struct_char_array_ptr (expr , builder , local_sym_tab , struct_sym_tab ):
235+ """Get pointer to first element of char array in struct field, or None."""
236+ if not (isinstance (expr , ast .Attribute ) and isinstance (expr .value , ast .Name )):
237+ return None
238+
239+ var_name = expr .value .id
240+ field_name = expr .attr
241+
242+ # Check if it's a valid struct field
243+ if not (
244+ local_sym_tab
245+ and var_name in local_sym_tab
246+ and struct_sym_tab
247+ and local_sym_tab [var_name ].metadata in struct_sym_tab
248+ ):
249+ return None
250+
251+ struct_type = local_sym_tab [var_name ].metadata
252+ struct_info = struct_sym_tab [struct_type ]
253+
254+ if field_name not in struct_info .fields :
255+ return None
256+
257+ field_type = struct_info .field_type (field_name )
258+
259+ # Check if it's a char array
260+ is_char_array = (
261+ isinstance (field_type , ir .ArrayType )
262+ and isinstance (field_type .element , ir .IntType )
263+ and field_type .element .width == 8
264+ )
265+
266+ if not is_char_array :
267+ return None
268+
269+ # Get field pointer and GEP to first element: [N x i8]* -> i8*
270+ struct_ptr = local_sym_tab [var_name ].var
271+ field_ptr = struct_info .gep (builder , struct_ptr , field_name )
272+
273+ return builder .gep (
274+ field_ptr ,
275+ [ir .Constant (ir .IntType (32 ), 0 ), ir .Constant (ir .IntType (32 ), 0 )],
276+ inbounds = True ,
277+ )
278+
279+
280+ def _handle_pointer_arg (val , func , builder ):
281+ """Convert pointer type for bpf_printk."""
282+ target , depth = get_base_type_and_depth (val .type )
283+
284+ if not isinstance (target , ir .IntType ):
285+ logger .warning ("Only int pointers supported in bpf_printk, defaulting to 0" )
286+ return ir .Constant (ir .IntType (64 ), 0 )
287+
288+ # i8* is string - use as-is
289+ if target .width == 8 and depth == 1 :
290+ return val
291+
292+ # Integer pointers: dereference and sign-extend to i64
293+ if target .width >= 32 :
294+ val = deref_to_depth (func , builder , val , depth )
295+ return builder .sext (val , ir .IntType (64 ))
296+
297+ logger .warning ("Unsupported pointer width in bpf_printk, defaulting to 0" )
298+ return ir .Constant (ir .IntType (64 ), 0 )
299+
300+
301+ def _handle_int_arg (val , builder ):
302+ """Convert integer type for bpf_printk (sign-extend to i64)."""
303+ if val .type .width < 64 :
304+ return builder .sext (val , ir .IntType (64 ))
305+ return val
0 commit comments