python-cc 0.0.6__py3-none-any.whl → 0.0.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. pcc/codegen/c_codegen.py +2483 -470
  2. pcc/evaluater/c_evaluator.py +1059 -96
  3. pcc/lex/c_lexer.py +24 -5
  4. pcc/parse/c_parser.py +124 -51
  5. pcc/pcc.py +161 -18
  6. pcc/ply/yacc.py +1 -1
  7. pcc/project.py +842 -58
  8. python_cc-0.0.7.dist-info/METADATA +429 -0
  9. {python_cc-0.0.6.dist-info → python_cc-0.0.7.dist-info}/RECORD +38 -33
  10. utils/fake_libc_include/TargetConditionals.h +16 -0
  11. utils/fake_libc_include/_fake_typedefs.h +46 -42
  12. utils/fake_libc_include/assert.h +4 -0
  13. utils/fake_libc_include/errno.h +82 -0
  14. utils/fake_libc_include/fcntl.h +132 -0
  15. utils/fake_libc_include/langinfo.h +13 -0
  16. utils/fake_libc_include/limits.h +13 -0
  17. utils/fake_libc_include/locale.h +43 -0
  18. utils/fake_libc_include/poll.h +23 -0
  19. utils/fake_libc_include/pwd.h +21 -0
  20. utils/fake_libc_include/setjmp.h +2 -0
  21. utils/fake_libc_include/signal.h +71 -0
  22. utils/fake_libc_include/strings.h +12 -0
  23. utils/fake_libc_include/sys/ioctl.h +2 -0
  24. utils/fake_libc_include/sys/mman.h +27 -0
  25. utils/fake_libc_include/sys/select.h +30 -0
  26. utils/fake_libc_include/sys/stat.h +77 -0
  27. utils/fake_libc_include/sys/time.h +10 -0
  28. utils/fake_libc_include/termios.h +56 -0
  29. utils/fake_libc_include/time.h +9 -0
  30. utils/fake_libc_include/unistd.h +37 -0
  31. utils/fake_libc_include/utime.h +7 -0
  32. utils/fake_libc_include/wchar.h +21 -0
  33. utils/fake_libc_include/wctype.h +18 -0
  34. utils/internal/refresh_clang_c_manifest.py +88 -0
  35. utils/internal/refresh_gcc_torture_manifest.py +145 -0
  36. python_cc-0.0.6.dist-info/METADATA +0 -200
  37. {python_cc-0.0.6.dist-info → python_cc-0.0.7.dist-info}/WHEEL +0 -0
  38. {python_cc-0.0.6.dist-info → python_cc-0.0.7.dist-info}/entry_points.txt +0 -0
  39. {python_cc-0.0.6.dist-info → python_cc-0.0.7.dist-info}/licenses/LICENSE +0 -0
pcc/codegen/c_codegen.py CHANGED
@@ -1,9 +1,11 @@
1
1
  import llvmlite.ir as ir
2
+ import math
2
3
  import re
3
4
  import struct
4
5
  from collections import ChainMap
5
- from contextlib import contextmanager
6
+ from contextlib import contextmanager, nullcontext
6
7
  from dataclasses import dataclass
8
+ from itertools import count
7
9
  from llvmlite.ir import IRBuilder
8
10
  from ..ast import c_ast as c_ast
9
11
 
@@ -11,6 +13,7 @@ bool_t = ir.IntType(1)
11
13
  int8_t = ir.IntType(8)
12
14
  int32_t = ir.IntType(32)
13
15
  int64_t = ir.IntType(64)
16
+ int128_t = ir.IntType(128)
14
17
  voidptr_t = int8_t.as_pointer()
15
18
  int64ptr_t = int64_t.as_pointer()
16
19
  true_bit = bool_t(1)
@@ -19,6 +22,7 @@ true_byte = int8_t(1)
19
22
  false_byte = int8_t(0)
20
23
  cstring = voidptr_t
21
24
  struct_types = {}
25
+ _aggregate_namespace_counter = count(1)
22
26
 
23
27
 
24
28
  class SemanticError(ValueError):
@@ -31,6 +35,7 @@ class FileScopeObjectState:
31
35
  linkage: str
32
36
  definition_kind: str
33
37
  symbol_name: str
38
+ ir_type: object
34
39
 
35
40
 
36
41
  @dataclass
@@ -40,6 +45,30 @@ class FileScopeFunctionState:
40
45
  defined: bool
41
46
  symbol_name: str
42
47
 
48
+
49
+ @dataclass
50
+ class StructFieldLayout:
51
+ name: object
52
+ byte_offset: int
53
+ semantic_ir_type: object
54
+ decl_type: object
55
+ is_bitfield: bool = False
56
+ storage_byte_offset: int = 0
57
+ storage_ir_type: object = None
58
+ bit_offset: int = 0
59
+ bit_width: int = 0
60
+ is_unsigned: bool = False
61
+
62
+
63
+ @dataclass
64
+ class BitFieldRef:
65
+ container_ptr: object
66
+ storage_ir_type: object
67
+ bit_offset: int
68
+ bit_width: int
69
+ semantic_ir_type: object
70
+ is_unsigned: bool
71
+
43
72
  # Libc function signature registry: name -> (return_type, [param_types], var_arg)
44
73
  # Covers: stdio.h, stdlib.h, string.h, ctype.h, math.h, unistd.h, time.h
45
74
  _VOID = ir.VoidType()
@@ -113,6 +142,15 @@ LIBC_FUNCTIONS = {
113
142
  "qsort": (_VOID, [voidptr_t, _size_t, _size_t, voidptr_t], False),
114
143
  "bsearch": (voidptr_t, [voidptr_t, voidptr_t, _size_t, _size_t, voidptr_t], False),
115
144
  "getenv": (cstring, [cstring], False),
145
+ "getlogin": (cstring, [], False),
146
+ "getpwent": (voidptr_t, [], False),
147
+ "getpwnam": (voidptr_t, [cstring], False),
148
+ "getpwuid": (voidptr_t, [int32_t], False),
149
+ "setpwent": (_VOID, [], False),
150
+ "endpwent": (_VOID, [], False),
151
+ "setenv": (int32_t, [cstring, cstring, int32_t], False),
152
+ "putenv": (int32_t, [cstring], False),
153
+ "unsetenv": (int32_t, [cstring], False),
116
154
  "system": (int32_t, [cstring], False),
117
155
  # === string.h ===
118
156
  "strlen": (_size_t, [cstring], False),
@@ -122,6 +160,8 @@ LIBC_FUNCTIONS = {
122
160
  "strncpy": (cstring, [cstring, cstring, _size_t], False),
123
161
  "strcat": (cstring, [cstring, cstring], False),
124
162
  "strncat": (cstring, [cstring, cstring, _size_t], False),
163
+ "strlcpy": (_size_t, [cstring, cstring, _size_t], False),
164
+ "strlcat": (_size_t, [cstring, cstring, _size_t], False),
125
165
  "strchr": (cstring, [cstring, int32_t], False),
126
166
  "strrchr": (cstring, [cstring, int32_t], False),
127
167
  "strstr": (cstring, [cstring, cstring], False),
@@ -149,6 +189,34 @@ LIBC_FUNCTIONS = {
149
189
  "isgraph": (int32_t, [int32_t], False),
150
190
  "toupper": (int32_t, [int32_t], False),
151
191
  "tolower": (int32_t, [int32_t], False),
192
+ # === wchar.h / wctype.h ===
193
+ "mbtowc": (int32_t, [int32_t.as_pointer(), cstring, _size_t], False),
194
+ "wctomb": (int32_t, [cstring, int32_t], False),
195
+ "mbrlen": (_size_t, [cstring, _size_t, voidptr_t], False),
196
+ "mbrtowc": (_size_t, [int32_t.as_pointer(), cstring, _size_t, voidptr_t], False),
197
+ "mbsrtowcs": (_size_t, [int32_t.as_pointer(), voidptr_t, _size_t, voidptr_t], False),
198
+ "mbstowcs": (_size_t, [int32_t.as_pointer(), cstring, _size_t], False),
199
+ "wcrtomb": (_size_t, [cstring, int32_t, voidptr_t], False),
200
+ "wcsrtombs": (_size_t, [cstring, voidptr_t, _size_t, voidptr_t], False),
201
+ "wcstombs": (_size_t, [cstring, voidptr_t, _size_t], False),
202
+ "wcwidth": (int32_t, [int32_t], False),
203
+ "wcswidth": (int32_t, [voidptr_t, _size_t], False),
204
+ "wmemchr": (voidptr_t, [voidptr_t, int32_t, _size_t], False),
205
+ "wmemcpy": (voidptr_t, [voidptr_t, voidptr_t, _size_t], False),
206
+ "wmemmove": (voidptr_t, [voidptr_t, voidptr_t, _size_t], False),
207
+ "wmemcmp": (int32_t, [voidptr_t, voidptr_t, _size_t], False),
208
+ "iswalnum": (int32_t, [int32_t], False),
209
+ "iswalpha": (int32_t, [int32_t], False),
210
+ "iswcntrl": (int32_t, [int32_t], False),
211
+ "iswctype": (int32_t, [int32_t, int32_t], False),
212
+ "iswgraph": (int32_t, [int32_t], False),
213
+ "iswlower": (int32_t, [int32_t], False),
214
+ "iswprint": (int32_t, [int32_t], False),
215
+ "iswspace": (int32_t, [int32_t], False),
216
+ "iswupper": (int32_t, [int32_t], False),
217
+ "towlower": (int32_t, [int32_t], False),
218
+ "towupper": (int32_t, [int32_t], False),
219
+ "wctype": (int32_t, [cstring], False),
152
220
  # === math.h ===
153
221
  "sin": (_double, [_double], False),
154
222
  "cos": (_double, [_double], False),
@@ -174,7 +242,9 @@ LIBC_FUNCTIONS = {
174
242
  "round": (_double, [_double], False),
175
243
  "trunc": (_double, [_double], False),
176
244
  "fmod": (_double, [_double, _double], False),
245
+ "fabsf": (_float, [_float], False),
177
246
  "fabs": (_double, [_double], False),
247
+ "fabsl": (_double, [_double], False),
178
248
  "ldexp": (_double, [_double, int32_t], False),
179
249
  # === time.h ===
180
250
  "time": (_time_t, [voidptr_t], False),
@@ -182,36 +252,75 @@ LIBC_FUNCTIONS = {
182
252
  "difftime": (_double, [_time_t, _time_t], False),
183
253
  "gmtime_r": (voidptr_t, [voidptr_t, voidptr_t], False),
184
254
  "localtime_r": (voidptr_t, [voidptr_t, voidptr_t], False),
255
+ "nanosleep": (int32_t, [voidptr_t, voidptr_t], False),
185
256
  # === unistd.h (POSIX) ===
186
257
  "sleep": (int32_t, [int32_t], False),
258
+ "alarm": (int32_t, [int32_t], False),
187
259
  "usleep": (int32_t, [int32_t], False),
188
260
  "read": (int64_t, [int32_t, voidptr_t, _size_t], False),
189
261
  "write": (int64_t, [int32_t, voidptr_t, _size_t], False),
262
+ "getuid": (int32_t, [], False),
263
+ "geteuid": (int32_t, [], False),
190
264
  "open": (int32_t, [cstring, int32_t], True),
191
265
  "close": (int32_t, [int32_t], False),
266
+ "access": (int32_t, [cstring, int32_t], False),
267
+ "fcntl": (int32_t, [int32_t, int32_t], True),
268
+ "fsync": (int32_t, [int32_t], False),
269
+ "ftruncate": (int32_t, [int32_t, int64_t], False),
270
+ "pread": (int64_t, [int32_t, voidptr_t, _size_t, int64_t], False),
271
+ "pwrite": (int64_t, [int32_t, voidptr_t, _size_t, int64_t], False),
272
+ "unlink": (int32_t, [cstring], False),
273
+ "readlink": (int64_t, [cstring, cstring, _size_t], False),
192
274
  "getpid": (int32_t, [], False),
193
275
  "getppid": (int32_t, [], False),
276
+ "sysconf": (int64_t, [int32_t], False),
194
277
  "isatty": (int32_t, [int32_t], False),
195
278
  "mkstemp": (int32_t, [cstring], False),
279
+ "tcdrain": (int32_t, [int32_t], False),
280
+ "tcflow": (int32_t, [int32_t, int32_t], False),
281
+ "tcgetattr": (int32_t, [int32_t, voidptr_t], False),
282
+ "tcsetattr": (int32_t, [int32_t, int32_t, voidptr_t], False),
283
+ "select": (int32_t, [int32_t, voidptr_t, voidptr_t, voidptr_t, voidptr_t], False),
284
+ "pselect": (int32_t, [int32_t, voidptr_t, voidptr_t, voidptr_t, voidptr_t, voidptr_t], False),
196
285
  # === setjmp.h ===
197
286
  "setjmp": (int32_t, [voidptr_t], False),
198
287
  "longjmp": (_VOID, [voidptr_t, int32_t], False),
199
288
  "_setjmp": (int32_t, [voidptr_t], False),
200
289
  "_longjmp": (_VOID, [voidptr_t, int32_t], False),
290
+ "sigsetjmp": (int32_t, [voidptr_t, int32_t], False),
291
+ "siglongjmp": (_VOID, [voidptr_t, int32_t], False),
201
292
  # === signal.h ===
202
293
  "signal": (voidptr_t, [int32_t, voidptr_t], False),
203
294
  "sigaction": (int32_t, [int32_t, voidptr_t, voidptr_t], False),
295
+ "sigaddset": (int32_t, [voidptr_t, int32_t], False),
296
+ "sigdelset": (int32_t, [voidptr_t, int32_t], False),
204
297
  "sigemptyset": (int32_t, [voidptr_t], False),
298
+ "sigismember": (int32_t, [voidptr_t, int32_t], False),
299
+ "sigprocmask": (int32_t, [int32_t, voidptr_t, voidptr_t], False),
300
+ "sigsuspend": (int32_t, [voidptr_t], False),
301
+ "kill": (int32_t, [int32_t, int32_t], False),
205
302
  "raise": (int32_t, [int32_t], False),
206
303
  # === errno ===
207
304
  "__errno_location": (ir.IntType(32).as_pointer(), [], False),
208
305
  # === locale.h ===
209
306
  "setlocale": (cstring, [int32_t, cstring], False),
210
307
  "localeconv": (voidptr_t, [], False),
308
+ "nl_langinfo": (cstring, [int32_t], False),
211
309
  # === misc ===
212
310
  "tmpnam": (cstring, [cstring], False),
213
311
  "tmpfile": (voidptr_t, [], False),
214
312
  "__errno_location": (int32_t.as_pointer(), [], False),
313
+ "stat": (int32_t, [cstring, voidptr_t], False),
314
+ "fstat": (int32_t, [int32_t, voidptr_t], False),
315
+ "lstat": (int32_t, [cstring, voidptr_t], False),
316
+ "chmod": (int32_t, [cstring, int32_t], False),
317
+ "fchmod": (int32_t, [int32_t, int32_t], False),
318
+ "mkdir": (int32_t, [cstring, int32_t], False),
319
+ "umask": (int32_t, [int32_t], False),
320
+ "utime": (int32_t, [cstring, voidptr_t], False),
321
+ "utimes": (int32_t, [cstring, voidptr_t], False),
322
+ "futimes": (int32_t, [int32_t, voidptr_t], False),
323
+ "gettimeofday": (int32_t, [voidptr_t, voidptr_t], False),
215
324
  "gmtime": (voidptr_t, [voidptr_t], False),
216
325
  "localtime": (voidptr_t, [voidptr_t], False),
217
326
  "mktime": (_time_t, [voidptr_t], False),
@@ -223,10 +332,28 @@ LIBC_FUNCTIONS = {
223
332
  "__builtin_va_start": (_VOID, [voidptr_t], False),
224
333
  "__builtin_va_end": (_VOID, [voidptr_t], False),
225
334
  "__builtin_va_copy": (_VOID, [voidptr_t, voidptr_t], False),
335
+ "__builtin_alloca": (voidptr_t, [int64_t], False),
226
336
  "__builtin_expect": (int64_t, [int64_t, int64_t], False),
337
+ "__builtin_assume": (_VOID, [int64_t], False),
338
+ "__builtin_prefetch": (_VOID, [voidptr_t, int32_t, int32_t], False),
227
339
  "__builtin_unreachable": (_VOID, [], False),
340
+ "__builtin_add_overflow": (int32_t, [int64_t, int64_t, voidptr_t], False),
341
+ "__builtin_sub_overflow": (int32_t, [int64_t, int64_t, voidptr_t], False),
342
+ "__builtin_mul_overflow": (int32_t, [int64_t, int64_t, voidptr_t], False),
343
+ "__builtin_bswap16": (int32_t, [int32_t], False),
344
+ "__builtin_bswap32": (int32_t, [int32_t], False),
345
+ "__builtin_bswap64": (int64_t, [int64_t], False),
228
346
  "__builtin_clz": (int32_t, [int32_t], False),
347
+ "__builtin_clzll": (int32_t, [int64_t], False),
229
348
  "__builtin_ctz": (int32_t, [int32_t], False),
349
+ "__builtin_ctzll": (int32_t, [int64_t], False),
350
+ "__builtin_rotateleft32": (int32_t, [int32_t, int32_t], False),
351
+ "__builtin_rotateleft64": (int64_t, [int64_t, int64_t], False),
352
+ "__builtin_rotateright32": (int32_t, [int32_t, int32_t], False),
353
+ "__builtin_rotateright64": (int64_t, [int64_t, int64_t], False),
354
+ "__sync_synchronize": (_VOID, [], False),
355
+ "__atomic_load_n": (int64_t, [voidptr_t, int32_t], False),
356
+ "__atomic_store_n": (_VOID, [voidptr_t, int64_t, int32_t], False),
230
357
  "modf": (_double, [_double, ir.DoubleType().as_pointer()], False),
231
358
  "ldexp": (_double, [_double, int32_t], False),
232
359
  "__builtin_va_arg": (voidptr_t, [voidptr_t, int64_t], False),
@@ -302,25 +429,10 @@ _PCC_VAARG_CALL_RE = re.compile(
302
429
  )
303
430
 
304
431
 
305
- _INVALID_VOID_INSTR_RE = re.compile(
306
- r"^(?:%\S+\s*=\s*)?(?:alloca|load|bitcast) void(?! \()([,\s]|$)|^store void(?! \()([,\s]|$)"
307
- )
308
-
309
- _LABEL_RE = re.compile(r"^[A-Za-z$._][A-Za-z0-9$._-]*:$")
310
-
311
-
312
432
  def postprocess_ir_text(text):
313
- """Apply textual IR rewrites that llvmlite cannot express directly."""
433
+ """Apply the minimal textual lowering that llvmlite cannot express directly."""
314
434
 
315
- # --- simple regex rewrites ---
316
435
  text = _PCC_VAARG_DECL_RE.sub("", text)
317
- text = re.sub(
318
- r"bitcast i64 (%\S+) to (i8\*|[^,\n]+\*)", r"inttoptr i64 \1 to \2", text
319
- )
320
- text = re.sub(
321
- r"bitcast i8 (%\S+) to (i8\*|[^,\n]+\*)", r"inttoptr i8 \1 to \2", text
322
- )
323
- text = re.sub(r"ptrtoint \[\d+ x i8\] [^\n]+ to i64", "add i64 0, 0", text)
324
436
 
325
437
  def repl(match):
326
438
  lhs = match.group("lhs")
@@ -330,78 +442,7 @@ def postprocess_ir_text(text):
330
442
  return f"{lhs} = va_arg {argtype} {argval}, {rettype}"
331
443
 
332
444
  text = _PCC_VAARG_CALL_RE.sub(repl, text)
333
-
334
- # --- line-level fixups ---
335
- lines = []
336
- for line in text.splitlines():
337
- # Fix Python repr leak in array initializers → zeroinitializer
338
- if "<ir.Constant" in line:
339
- m = re.match(r'(@"[^"]*"\s*=\s*(?:global|constant)\s*\[[^\]]*\]).*', line)
340
- if m:
341
- line = m.group(1) + " zeroinitializer"
342
- else:
343
- continue
344
- s = line.strip()
345
- # Drop invalid void instructions (alloca void, load void, store void)
346
- if _INVALID_VOID_INSTR_RE.match(s):
347
- continue
348
- lines.append(line)
349
-
350
- # Deduplicate switch case values
351
- deduped = []
352
- for line in lines:
353
- s = line.strip()
354
- if s.startswith("switch i64 ") and "[" in line and "]" in line:
355
- prefix, rest = line.split("[", 1)
356
- case_text, suffix = rest.rsplit("]", 1)
357
- cases = re.findall(r'(i64 -?\d+, label %"[^"]*")', case_text)
358
- if cases:
359
- seen = set()
360
- unique = []
361
- for case in cases:
362
- val = re.match(r"i64 (-?\d+)", case).group(1)
363
- if val not in seen:
364
- seen.add(val)
365
- unique.append(case)
366
- line = prefix + "[" + " ".join(unique) + "]" + suffix
367
- deduped.append(line)
368
-
369
- # Repair control flow: drop dead code after terminators, bridge empty labels
370
- def _is_label(s):
371
- return bool(_LABEL_RE.match(s))
372
-
373
- def _is_terminator(s):
374
- return s.startswith(("br ", "ret ", "switch ", "unreachable", "resume "))
375
-
376
- repaired = []
377
- skip_dead = False
378
- for raw in deduped:
379
- s = raw.strip()
380
- if skip_dead:
381
- if _is_label(s):
382
- skip_dead = False
383
- else:
384
- continue
385
- if (
386
- repaired
387
- and _is_terminator(repaired[-1].strip())
388
- and s
389
- and raw.startswith(" ")
390
- and not _is_label(s)
391
- ):
392
- skip_dead = True
393
- continue
394
- if repaired and _is_label(s):
395
- prev = repaired[-1].strip()
396
- if _is_label(prev):
397
- repaired.append(f' br label %"{s[:-1]}"')
398
- elif prev not in {"", "{"} and not _is_terminator(prev):
399
- repaired.append(f' br label %"{s[:-1]}"')
400
- if s == "}" and repaired and _is_label(repaired[-1].strip()):
401
- repaired.append(" unreachable")
402
- repaired.append(raw)
403
-
404
- return "\n".join(repaired)
445
+ return text
405
446
 
406
447
 
407
448
  def get_ir_type_from_names(names):
@@ -431,12 +472,14 @@ def get_ir_type_from_names(names):
431
472
  "void": ir.VoidType(),
432
473
  "double": _double,
433
474
  "float": _float,
475
+ "_Float16": _float,
434
476
  "short": int16_t,
435
477
  "long": int64_t,
436
478
  "int short": int16_t,
437
479
  "int long": int64_t,
438
480
  "long long": int64_t,
439
481
  "int long long": int64_t,
482
+ "__int128": int128_t,
440
483
  "char unsigned": int8_t,
441
484
  "int unsigned": int32_t,
442
485
  "unsigned": int32_t,
@@ -445,6 +488,7 @@ def get_ir_type_from_names(names):
445
488
  "int long unsigned": int64_t,
446
489
  "long unsigned": int64_t,
447
490
  "long long unsigned": int64_t,
491
+ "__int128 unsigned": int128_t,
448
492
  # size_t, etc.
449
493
  "size_t": int64_t,
450
494
  "ssize_t": int64_t,
@@ -466,6 +510,8 @@ def get_ir_type_from_names(names):
466
510
  return _double
467
511
  if "float" in names:
468
512
  return _float
513
+ if "_Float16" in names:
514
+ return _float
469
515
  # If it contains 'char', return i8
470
516
  if "char" in names:
471
517
  return int8_t
@@ -490,14 +536,20 @@ def _resolve_node_type(node_type):
490
536
  if isinstance(inner, c_ast.FuncDecl):
491
537
  ret_type = _resolve_node_type(inner.type)
492
538
  param_types = []
539
+ is_var_arg = False
493
540
  if inner.args:
494
541
  for p in inner.args.params:
495
542
  if isinstance(p, c_ast.EllipsisParam):
543
+ is_var_arg = True
496
544
  continue
497
545
  t = get_ir_type_from_node(p)
498
546
  if not isinstance(t, ir.VoidType):
499
547
  param_types.append(t)
500
- return ir.FunctionType(ret_type, param_types).as_pointer()
548
+ return ir.FunctionType(
549
+ ret_type,
550
+ param_types,
551
+ var_arg=is_var_arg,
552
+ ).as_pointer()
501
553
  pointee = _resolve_node_type(inner)
502
554
  if isinstance(pointee, ir.VoidType):
503
555
  return voidptr_t
@@ -507,7 +559,7 @@ def _resolve_node_type(node_type):
507
559
  return get_ir_type(node_type.type.names)
508
560
  elif isinstance(node_type.type, c_ast.Struct):
509
561
  snode = node_type.type
510
- if snode.decls:
562
+ if snode.decls is not None:
511
563
  # Inline struct with declarations — build real type
512
564
  member_types = []
513
565
  for decl in snode.decls:
@@ -519,8 +571,15 @@ def _resolve_node_type(node_type):
519
571
  return int8_t # opaque/forward-declared struct
520
572
  elif isinstance(node_type.type, c_ast.Union):
521
573
  unode = node_type.type
522
- if unode.decls:
574
+ if unode.decls is not None:
523
575
  # Inline union with declarations — compute max size
576
+ if len(unode.decls) == 0:
577
+ ut = ir.LiteralStructType([])
578
+ ut.members = []
579
+ ut.member_types = {}
580
+ ut.member_decl_types = {}
581
+ ut.is_union = True
582
+ return ut
524
583
  max_size = 0
525
584
  max_align = 1
526
585
  member_types = {}
@@ -548,7 +607,7 @@ def _resolve_node_type(node_type):
548
607
  ir_t = _resolve_union_member_type(decl.type)
549
608
  member_types[decl.name] = ir_t
550
609
  sz = ir_t.width // 8 if isinstance(ir_t, ir.IntType) else 8
551
- if isinstance(ir_t, ir.LiteralStructType):
610
+ if _is_struct_ir_type(ir_t):
552
611
  sz = sum(
553
612
  e.width // 8 if isinstance(e, ir.IntType) else 8
554
613
  for e in ir_t.elements
@@ -576,12 +635,18 @@ def _resolve_node_type(node_type):
576
635
  ut.is_union = True
577
636
  return ut
578
637
  return int8_t # opaque/forward-declared union
638
+ elif isinstance(node_type.type, c_ast.Enum):
639
+ return int32_t
579
640
  return int64_t
580
641
  elif isinstance(node_type, c_ast.ArrayDecl):
581
642
  return voidptr_t # array params decay to pointer
582
643
  return int64_t
583
644
 
584
645
 
646
+ def _is_struct_ir_type(ir_type):
647
+ return isinstance(ir_type, (ir.LiteralStructType, ir.IdentifiedStructType))
648
+
649
+
585
650
  class LLVMCodeGenerator(object):
586
651
 
587
652
  def __init__(self, translation_unit_name=None):
@@ -601,6 +666,8 @@ class LLVMCodeGenerator(object):
601
666
  self.env = ChainMap()
602
667
  self.nlabels = 0
603
668
  self.function = None
669
+ self._function_display_name = None
670
+ self._frame_address_marker = None
604
671
  self.in_global = True
605
672
  self._declared_libc = set()
606
673
  self._unsigned_bindings = set() # alloca/global ids with unsigned type
@@ -609,11 +676,18 @@ class LLVMCodeGenerator(object):
609
676
  self._expr_ir_types = {}
610
677
  self._labels = {}
611
678
  self._vaarg_counter = 0
679
+ self._anon_type_counter = 0
612
680
  self._file_scope_object_states = {}
613
681
  self._file_scope_function_states = {}
682
+ self._future_file_scope_object_ir_types = {}
683
+ self._future_file_scope_function_ir_types = {}
614
684
  self.translation_unit_name = self._sanitize_translation_unit_name(
615
685
  translation_unit_name
616
686
  )
687
+ base_namespace = self.translation_unit_name or "pcc"
688
+ self._aggregate_namespace = (
689
+ f"{base_namespace}_{next(_aggregate_namespace_counter)}"
690
+ )
617
691
 
618
692
  def define(self, name, val):
619
693
  self.env[name] = val
@@ -624,6 +698,29 @@ class LLVMCodeGenerator(object):
624
698
  return None
625
699
  return re.sub(r"\W+", "_", name)
626
700
 
701
+ @staticmethod
702
+ def _tag_type_key(name):
703
+ return f"__struct_{name}"
704
+
705
+ def _next_anon_struct_name(self, kind):
706
+ self._anon_type_counter += 1
707
+ return (
708
+ f"__pcc_{self._aggregate_namespace}_{kind}_{self._anon_type_counter}"
709
+ )
710
+
711
+ def _aggregate_type_name(self, kind, name=None):
712
+ if name:
713
+ return f"__pcc_{self._aggregate_namespace}_{kind}_{name}"
714
+ return self._next_anon_struct_name(kind)
715
+
716
+ def _identified_aggregate_type(self, kind, name, body):
717
+ aggregate_type = self.module.context.get_identified_type(
718
+ self._aggregate_type_name(kind, name)
719
+ )
720
+ if aggregate_type.is_opaque:
721
+ aggregate_type.set_body(*body)
722
+ return aggregate_type
723
+
627
724
  def _is_file_scope_static(self, storage=None):
628
725
  return (
629
726
  self.translation_unit_name
@@ -632,8 +729,17 @@ class LLVMCodeGenerator(object):
632
729
  and "static" in storage
633
730
  )
634
731
 
635
- def _file_scope_symbol_name(self, name, storage=None):
636
- if self._is_file_scope_static(storage):
732
+ def _has_internal_inline_linkage(self, storage=None, funcspec=None):
733
+ return (
734
+ self.translation_unit_name
735
+ and self.in_global
736
+ and funcspec
737
+ and "inline" in funcspec
738
+ and not storage
739
+ )
740
+
741
+ def _file_scope_symbol_name(self, name, storage=None, funcspec=None, linkage=None):
742
+ if linkage == "internal" or self._is_file_scope_static(storage) or self._has_internal_inline_linkage(storage, funcspec):
637
743
  return f"__pcc_internal_{self.translation_unit_name}_{name}"
638
744
  return name
639
745
 
@@ -656,16 +762,28 @@ class LLVMCodeGenerator(object):
656
762
  self.define(bind_name, (ir_type, gv))
657
763
  return gv
658
764
 
659
- def _decl_linkage(self, storage=None, existing_state=None):
765
+ def _decl_linkage(self, storage=None, funcspec=None, existing_state=None):
660
766
  if storage and "static" in storage:
661
767
  return "internal"
768
+ if self._has_internal_inline_linkage(storage, funcspec):
769
+ return "internal"
662
770
  if existing_state is not None:
663
771
  return existing_state.linkage
664
772
  return "external"
665
773
 
666
- def _effective_file_scope_symbol_name(self, name, storage=None, existing_state=None):
667
- if storage and "static" in storage:
668
- return f"__pcc_internal_{self.translation_unit_name}_{name}"
774
+ def _effective_file_scope_symbol_name(
775
+ self, name, storage=None, funcspec=None, existing_state=None, linkage=None
776
+ ):
777
+ if linkage is None:
778
+ linkage = self._decl_linkage(
779
+ storage, funcspec=funcspec, existing_state=existing_state
780
+ )
781
+ if linkage == "internal":
782
+ if existing_state is not None and existing_state.linkage == "internal":
783
+ return existing_state.symbol_name
784
+ return self._file_scope_symbol_name(
785
+ name, storage=storage, funcspec=funcspec, linkage=linkage
786
+ )
669
787
  if existing_state is not None and existing_state.linkage == "internal":
670
788
  return existing_state.symbol_name
671
789
  return name
@@ -677,11 +795,54 @@ class LLVMCodeGenerator(object):
677
795
  return "definition"
678
796
  return "tentative"
679
797
 
798
+ def _is_incomplete_array_ir_type(self, ir_type):
799
+ return isinstance(ir_type, ir.ArrayType) and ir_type.count == 0
800
+
801
+ def _are_compatible_object_ir_types(self, existing_ir_type, new_ir_type):
802
+ if str(existing_ir_type) == str(new_ir_type):
803
+ return True
804
+ if not (
805
+ isinstance(existing_ir_type, ir.ArrayType)
806
+ and isinstance(new_ir_type, ir.ArrayType)
807
+ ):
808
+ return False
809
+ if str(existing_ir_type.element) != str(new_ir_type.element):
810
+ return False
811
+ return (
812
+ existing_ir_type.count == 0
813
+ or new_ir_type.count == 0
814
+ or existing_ir_type.count == new_ir_type.count
815
+ )
816
+
817
+ def _merge_object_ir_types(self, existing_ir_type, new_ir_type):
818
+ if self._is_incomplete_array_ir_type(existing_ir_type) and not self._is_incomplete_array_ir_type(new_ir_type):
819
+ return new_ir_type
820
+ return existing_ir_type
821
+
822
+ def _preferred_file_scope_object_ir_type(self, name, ir_type):
823
+ future_ir_type = self._future_file_scope_object_ir_types.get(name)
824
+ if future_ir_type is None:
825
+ return ir_type
826
+ if not self._are_compatible_object_ir_types(ir_type, future_ir_type):
827
+ return ir_type
828
+ return self._merge_object_ir_types(ir_type, future_ir_type)
829
+
830
+ def _preferred_file_scope_function_ir_type(self, name, function_type, has_prototype):
831
+ if has_prototype:
832
+ return function_type
833
+ future_type = self._future_file_scope_function_ir_types.get(name)
834
+ if future_type is None:
835
+ return function_type
836
+ if str(function_type.return_type) != str(future_type.return_type):
837
+ return function_type
838
+ return future_type
839
+
680
840
  def _prepare_file_scope_object(self, name, ir_type, storage=None, has_initializer=False):
681
841
  if not self.in_global or name is None:
682
842
  return None, True
683
843
  if name in self._file_scope_function_states:
684
844
  raise SemanticError(f"'{name}' redeclared as object after function declaration")
845
+ ir_type = self._preferred_file_scope_object_ir_type(name, ir_type)
685
846
 
686
847
  type_key = str(ir_type)
687
848
  definition_kind = self._file_scope_object_definition_kind(
@@ -699,6 +860,7 @@ class LLVMCodeGenerator(object):
699
860
  linkage=linkage,
700
861
  definition_kind=definition_kind,
701
862
  symbol_name=symbol_name,
863
+ ir_type=ir_type,
702
864
  )
703
865
  self._file_scope_object_states[name] = state
704
866
  if definition_kind == "extern":
@@ -709,8 +871,11 @@ class LLVMCodeGenerator(object):
709
871
  )
710
872
  return gv, True
711
873
 
712
- if state.type_key != type_key:
874
+ if not self._are_compatible_object_ir_types(state.ir_type, ir_type):
713
875
  raise SemanticError(f"conflicting types for global '{name}'")
876
+ merged_ir_type = self._merge_object_ir_types(state.ir_type, ir_type)
877
+ state.ir_type = merged_ir_type
878
+ state.type_key = str(merged_ir_type)
714
879
  if state.linkage != linkage:
715
880
  raise SemanticError(f"conflicting linkage for global '{name}'")
716
881
  if state.symbol_name != symbol_name:
@@ -719,9 +884,9 @@ class LLVMCodeGenerator(object):
719
884
  existing = self.module.globals.get(symbol_name)
720
885
  if definition_kind == "extern":
721
886
  if existing is not None:
722
- self.define(name, (ir_type, existing))
887
+ self.define(name, (merged_ir_type, existing))
723
888
  else:
724
- self._record_extern_global(name, ir_type, storage=storage)
889
+ self._record_extern_global(name, merged_ir_type, storage=storage)
725
890
  return None, False
726
891
 
727
892
  if existing is None:
@@ -747,7 +912,7 @@ class LLVMCodeGenerator(object):
747
912
  return gv, True
748
913
 
749
914
  def _register_file_scope_function(
750
- self, name, function_type, storage=None, is_definition=False
915
+ self, name, function_type, storage=None, funcspec=None, is_definition=False
751
916
  ):
752
917
  if not self.in_global or name is None:
753
918
  return
@@ -756,9 +921,13 @@ class LLVMCodeGenerator(object):
756
921
 
757
922
  type_key = str(function_type)
758
923
  state = self._file_scope_function_states.get(name)
759
- linkage = self._decl_linkage(storage, existing_state=state)
924
+ linkage = self._decl_linkage(storage, funcspec=funcspec, existing_state=state)
760
925
  symbol_name = self._effective_file_scope_symbol_name(
761
- name, storage=storage, existing_state=state
926
+ name,
927
+ storage=storage,
928
+ funcspec=funcspec,
929
+ existing_state=state,
930
+ linkage=linkage,
762
931
  )
763
932
 
764
933
  if state is None:
@@ -782,6 +951,12 @@ class LLVMCodeGenerator(object):
782
951
  state.defined = True
783
952
  return state.symbol_name
784
953
 
954
+ @staticmethod
955
+ def _function_arg_types_match(lhs_args, rhs_args):
956
+ if len(lhs_args) != len(rhs_args):
957
+ return False
958
+ return all(str(lhs) == str(rhs) for lhs, rhs in zip(lhs_args, rhs_args))
959
+
785
960
  def external_definitions(self):
786
961
  defs = []
787
962
  for name, state in self._file_scope_function_states.items():
@@ -805,12 +980,65 @@ class LLVMCodeGenerator(object):
805
980
  and node.name is not None
806
981
  )
807
982
 
808
- def _extern_decl_ir_type(self, node_type):
983
+ def _extern_decl_ir_type(self, name, node_type):
984
+ if isinstance(node_type, c_ast.ArrayDecl):
985
+ ir_type = self._build_array_ir_type(node_type)
986
+ else:
987
+ ir_type = self._resolve_ast_type(node_type)
988
+ return self._preferred_file_scope_object_ir_type(name, ir_type)
989
+
990
+ def _static_local_ir_type(self, node_type, init_node=None):
809
991
  if isinstance(node_type, c_ast.ArrayDecl):
810
- return self._build_array_ir_type(node_type)
992
+ return self._build_array_ir_type(node_type, init_node=init_node)
811
993
  return self._resolve_ast_type(node_type)
812
994
 
995
+ def _collect_file_scope_object_ir_types(self, ext_nodes):
996
+ merged_types = {}
997
+ for ext in ext_nodes:
998
+ if not (
999
+ isinstance(ext, c_ast.Decl)
1000
+ and ext.name is not None
1001
+ and not isinstance(ext.type, c_ast.FuncDecl)
1002
+ ):
1003
+ continue
1004
+ try:
1005
+ if isinstance(ext.type, c_ast.ArrayDecl):
1006
+ ir_type = self._build_array_ir_type(ext.type, init_node=ext.init)
1007
+ else:
1008
+ ir_type = self._resolve_ast_type(ext.type)
1009
+ except Exception:
1010
+ continue
1011
+ existing_ir_type = merged_types.get(ext.name)
1012
+ if existing_ir_type is None:
1013
+ merged_types[ext.name] = ir_type
1014
+ continue
1015
+ if self._are_compatible_object_ir_types(existing_ir_type, ir_type):
1016
+ merged_types[ext.name] = self._merge_object_ir_types(
1017
+ existing_ir_type, ir_type
1018
+ )
1019
+ self._future_file_scope_object_ir_types = merged_types
1020
+
1021
+ def _collect_file_scope_function_ir_types(self, ext_nodes):
1022
+ future_types = {}
1023
+ for ext in ext_nodes:
1024
+ decl = None
1025
+ if isinstance(ext, c_ast.FuncDef):
1026
+ decl = ext.decl
1027
+ elif isinstance(ext, c_ast.Decl) and isinstance(ext.type, c_ast.FuncDecl):
1028
+ decl = ext
1029
+ if decl is None or decl.name is None:
1030
+ continue
1031
+ if getattr(decl.type, "args", None) is None:
1032
+ continue
1033
+ try:
1034
+ function_type, _ = self._build_function_ir_type(decl.type)
1035
+ except Exception:
1036
+ continue
1037
+ future_types[decl.name] = function_type
1038
+ self._future_file_scope_function_ir_types = future_types
1039
+
813
1040
  def _record_extern_global(self, name, ir_type, storage=None):
1041
+ ir_type = self._preferred_file_scope_object_ir_type(name, ir_type)
814
1042
  self.define(
815
1043
  name,
816
1044
  (
@@ -937,6 +1165,34 @@ class LLVMCodeGenerator(object):
937
1165
  return self._convert_int_value(val, int32_t, result_unsigned=False)
938
1166
  return val
939
1167
 
1168
+ def _integer_promotion_ir_type(self, ir_type):
1169
+ if not isinstance(ir_type, ir.IntType):
1170
+ return ir_type
1171
+ if ir_type.width < int32_t.width:
1172
+ return int32_t
1173
+ return ir_type
1174
+
1175
+ def _usual_arithmetic_conversion_ir_type(self, lhs_type, rhs_type):
1176
+ lhs_type = self._integer_promotion_ir_type(lhs_type)
1177
+ rhs_type = self._integer_promotion_ir_type(rhs_type)
1178
+
1179
+ if self._is_floating_ir_type(lhs_type) or self._is_floating_ir_type(rhs_type):
1180
+ if self._is_floating_ir_type(lhs_type) and self._is_floating_ir_type(
1181
+ rhs_type
1182
+ ):
1183
+ return self._common_float_type(lhs_type, rhs_type)
1184
+ return lhs_type if self._is_floating_ir_type(lhs_type) else rhs_type
1185
+
1186
+ if isinstance(lhs_type, ir.IntType) and isinstance(rhs_type, ir.IntType):
1187
+ return lhs_type if lhs_type.width >= rhs_type.width else rhs_type
1188
+
1189
+ return lhs_type
1190
+
1191
+ def _decay_ir_type(self, ir_type):
1192
+ if isinstance(ir_type, ir.ArrayType):
1193
+ return ir.PointerType(ir_type.element)
1194
+ return ir_type
1195
+
940
1196
  def _usual_arithmetic_conversion(self, lhs, rhs):
941
1197
  lhs = self._integer_promotion(lhs)
942
1198
  rhs = self._integer_promotion(rhs)
@@ -986,10 +1242,18 @@ class LLVMCodeGenerator(object):
986
1242
  return _float
987
1243
 
988
1244
  def _parse_float_constant(self, raw):
1245
+ is_float32 = raw.endswith(("f", "F"))
989
1246
  value = raw.rstrip("fFlL")
990
1247
  if value.lower().startswith("0x") and "p" in value.lower():
991
- return float.fromhex(value)
992
- return float(value)
1248
+ parsed = float.fromhex(value)
1249
+ else:
1250
+ parsed = float(value)
1251
+ if is_float32:
1252
+ try:
1253
+ return struct.unpack("!f", struct.pack("!f", parsed))[0]
1254
+ except OverflowError:
1255
+ return math.copysign(float("inf"), parsed)
1256
+ return parsed
993
1257
 
994
1258
  def _float_literal_ir_type(self, raw):
995
1259
  if raw.endswith(("f", "F")):
@@ -1089,16 +1353,21 @@ class LLVMCodeGenerator(object):
1089
1353
  @contextmanager
1090
1354
  def new_function(self):
1091
1355
  oldfunc = self.function
1356
+ old_display_name = self._function_display_name
1357
+ old_frame_address_marker = self._frame_address_marker
1092
1358
  oldbuilder = self.builder
1093
1359
  oldenv = self.env
1094
1360
  oldlabels = self._labels
1095
1361
  self.in_global = False
1096
1362
  self.env = self.env.new_child()
1097
1363
  self._labels = {}
1364
+ self._frame_address_marker = None
1098
1365
  try:
1099
1366
  yield
1100
1367
  finally:
1101
1368
  self.function = oldfunc
1369
+ self._function_display_name = old_display_name
1370
+ self._frame_address_marker = old_frame_address_marker
1102
1371
  self.builder = oldbuilder
1103
1372
  self.env = oldenv
1104
1373
  self._labels = oldlabels
@@ -1200,9 +1469,11 @@ class LLVMCodeGenerator(object):
1200
1469
  for i, ext in enumerate(node.ext):
1201
1470
  is_type_def = False
1202
1471
  if isinstance(ext, c_ast.Decl):
1203
- if isinstance(ext.type, (c_ast.Struct, c_ast.Union, c_ast.Enum)):
1472
+ if ext.name is None and isinstance(
1473
+ ext.type, (c_ast.Struct, c_ast.Union, c_ast.Enum)
1474
+ ):
1204
1475
  is_type_def = True
1205
- elif isinstance(ext.type, c_ast.TypeDecl) and isinstance(
1476
+ elif ext.name is None and isinstance(ext.type, c_ast.TypeDecl) and isinstance(
1206
1477
  ext.type.type, (c_ast.Struct, c_ast.Union)
1207
1478
  ):
1208
1479
  is_type_def = True
@@ -1214,6 +1485,9 @@ class LLVMCodeGenerator(object):
1214
1485
  except Exception:
1215
1486
  pass
1216
1487
  pass1.add(i)
1488
+ remaining_exts = [ext for i, ext in enumerate(node.ext) if i not in pass1]
1489
+ self._collect_file_scope_function_ir_types(remaining_exts)
1490
+ self._collect_file_scope_object_ir_types(remaining_exts)
1217
1491
  for i, ext in enumerate(node.ext):
1218
1492
  if i not in pass1:
1219
1493
  try:
@@ -1297,15 +1571,37 @@ class LLVMCodeGenerator(object):
1297
1571
  if node.type == "int":
1298
1572
  # Support hex (0xFF), octal (077), and decimal literals
1299
1573
  raw = node.value
1300
- is_unsigned = "u" in raw.lower() or "U" in raw
1574
+ raw_lower = raw.lower()
1575
+ has_unsigned_suffix = "u" in raw_lower
1576
+ has_long_suffix = "l" in raw_lower
1301
1577
  val_str = raw.rstrip("uUlL")
1302
1578
  if val_str.startswith("0x") or val_str.startswith("0X"):
1303
1579
  int_val = int(val_str, 16)
1580
+ is_non_decimal = True
1304
1581
  elif val_str.startswith("0") and len(val_str) > 1 and val_str[1:].isdigit():
1305
1582
  int_val = int(val_str, 8)
1583
+ is_non_decimal = True
1306
1584
  else:
1307
1585
  int_val = int(val_str)
1308
- result = ir.values.Constant(ir.IntType(64), int_val)
1586
+ is_non_decimal = False
1587
+
1588
+ if has_long_suffix or int_val > 0xFFFFFFFF:
1589
+ ir_type = int64_t
1590
+ is_unsigned = has_unsigned_suffix
1591
+ elif has_unsigned_suffix:
1592
+ ir_type = int32_t
1593
+ is_unsigned = True
1594
+ elif is_non_decimal and int_val > 0x7FFFFFFF:
1595
+ ir_type = int32_t
1596
+ is_unsigned = True
1597
+ elif int_val > 0x7FFFFFFF:
1598
+ ir_type = int64_t
1599
+ is_unsigned = False
1600
+ else:
1601
+ ir_type = int32_t
1602
+ is_unsigned = False
1603
+
1604
+ result = ir.values.Constant(ir_type, int_val)
1309
1605
  if is_unsigned:
1310
1606
  self._tag_unsigned(result)
1311
1607
  return result, None
@@ -1339,6 +1635,7 @@ class LLVMCodeGenerator(object):
1339
1635
  if lv is None or rv is None:
1340
1636
  return ir.Constant(int64_t, 0), None
1341
1637
  result = None
1638
+ is_bitfield = isinstance(lv_addr, BitFieldRef)
1342
1639
 
1343
1640
  dispatch_type_double = 1
1344
1641
  dispatch_type_int = 0
@@ -1394,13 +1691,19 @@ class LLVMCodeGenerator(object):
1394
1691
 
1395
1692
  if node.op == "=":
1396
1693
  # Type coercion: match rv to the target's pointee type
1397
- if lv_addr and hasattr(lv_addr.type, "pointee"):
1694
+ if is_bitfield:
1695
+ target_type = lv_addr.semantic_ir_type
1696
+ elif lv_addr and hasattr(lv_addr.type, "pointee"):
1398
1697
  target_type = lv_addr.type.pointee
1399
1698
  else:
1400
1699
  target_type = lv.type
1401
1700
  if rv.type != target_type:
1402
1701
  rv = self._implicit_convert(rv, target_type)
1403
- self._safe_store(rv, lv_addr)
1702
+ if is_bitfield:
1703
+ self._store_bitfield(rv, lv_addr)
1704
+ rv = self._load_bitfield(lv_addr)
1705
+ else:
1706
+ self._safe_store(rv, lv_addr)
1404
1707
  return rv, lv_addr # return value for chained assignment
1405
1708
  else:
1406
1709
  # Pointer compound assignment: p += n, p -= n
@@ -1418,6 +1721,9 @@ class LLVMCodeGenerator(object):
1418
1721
  addresult = handle(lv, rv, "addtmp")
1419
1722
  if dispatch_type == dispatch_type_int and is_unsigned:
1420
1723
  self._tag_unsigned(addresult)
1724
+ if is_bitfield:
1725
+ self._store_bitfield(addresult, lv_addr)
1726
+ return self._load_bitfield(lv_addr), lv_addr
1421
1727
  self._safe_store(addresult, lv_addr)
1422
1728
  return addresult, lv_addr
1423
1729
 
@@ -1444,8 +1750,12 @@ class LLVMCodeGenerator(object):
1444
1750
  )
1445
1751
  if self._is_unsigned_val(lv):
1446
1752
  self._tag_unsigned(new_val)
1447
- self._safe_store(new_val, lv_addr)
1448
- result = lv if is_post else new_val
1753
+ if isinstance(lv_addr, BitFieldRef):
1754
+ self._store_bitfield(new_val, lv_addr)
1755
+ result = lv if is_post else self._load_bitfield(lv_addr)
1756
+ else:
1757
+ self._safe_store(new_val, lv_addr)
1758
+ result = lv if is_post else new_val
1449
1759
 
1450
1760
  elif node.op == "*":
1451
1761
  if (
@@ -1459,8 +1769,8 @@ class LLVMCodeGenerator(object):
1459
1769
  node.expr.expr.args.exprs if node.expr.expr.args is not None else []
1460
1770
  )
1461
1771
  if isinstance(target_ptr_type, ir.PointerType) and va_args:
1462
- ap_addr, _ = self.codegen(va_args[0])
1463
- if isinstance(getattr(ap_addr, "type", None), ir.PointerType):
1772
+ ap_addr = self._builtin_va_list_storage(va_args[0])
1773
+ if ap_addr is not None:
1464
1774
  self._vaarg_counter += 1
1465
1775
  name = f"__pcc_va_arg_{self._vaarg_counter}"
1466
1776
  placeholder = self.module.globals.get(name)
@@ -1483,6 +1793,11 @@ class LLVMCodeGenerator(object):
1483
1793
  result_ptr = self._decay_array_value_to_pointer(name_ir, "derefarray")
1484
1794
  else:
1485
1795
  result_ptr = name_ir
1796
+ if isinstance(getattr(result_ptr, "type", None), ir.PointerType) and isinstance(
1797
+ result_ptr.type.pointee, ir.ArrayType
1798
+ ):
1799
+ self._set_expr_ir_type(node, result_ptr.type.pointee)
1800
+ return result_ptr, result_ptr
1486
1801
  result = self._safe_load(result_ptr)
1487
1802
  if self._is_unsigned_pointee(name_ir) or self._is_unsigned_pointee(
1488
1803
  result_ptr
@@ -1556,12 +1871,15 @@ class LLVMCodeGenerator(object):
1556
1871
  if isinstance(expr, c_ast.Typename):
1557
1872
  ir_t = self._resolve_ast_type(expr.type)
1558
1873
  size = self._ir_type_size(ir_t)
1874
+ elif isinstance(expr, c_ast.Constant) and expr.type == "string":
1875
+ raw = expr.value[1:-1]
1876
+ processed = self._process_escapes(raw)
1877
+ size = len(self._string_bytes(processed + "\00"))
1559
1878
  elif isinstance(expr, c_ast.ID):
1560
1879
  ir_type, _ = self.lookup(expr.name)
1561
1880
  size = self._ir_type_size(ir_type)
1562
1881
  else:
1563
- val, _ = self.codegen(expr)
1564
- semantic_type = self._get_expr_ir_type(expr, getattr(val, "type", None))
1882
+ semantic_type = self._infer_sizeof_operand_ir_type(expr)
1565
1883
  size = self._ir_type_size(semantic_type)
1566
1884
  result = ir.Constant(int64_t, size)
1567
1885
  return self._tag_unsigned(result)
@@ -1580,9 +1898,8 @@ class LLVMCodeGenerator(object):
1580
1898
  if isinstance(resolved, str):
1581
1899
  # Could be a __struct_ reference or a base type name
1582
1900
  if resolved.startswith("__struct_"):
1583
- struct_name = resolved[len("__struct_") :]
1584
- if struct_name in self.env:
1585
- return self.env[struct_name][0]
1901
+ if resolved in self.env:
1902
+ return self.env[resolved][0]
1586
1903
  return int8_t # opaque
1587
1904
  # Recursively resolve further typedefs
1588
1905
  return self._resolve_type_str(resolved, depth + 1)
@@ -1725,11 +2042,162 @@ class LLVMCodeGenerator(object):
1725
2042
  return None
1726
2043
  return self._scalar_init_node(init_node.exprs[0])
1727
2044
 
2045
+ def _struct_field_names(self, ir_type):
2046
+ member_names = list(getattr(ir_type, "members", ()) or [])
2047
+ if member_names:
2048
+ return member_names
2049
+ member_types = getattr(ir_type, "member_types", None)
2050
+ if isinstance(member_types, dict):
2051
+ return list(member_types.keys())
2052
+ return []
2053
+
2054
+ def _ordered_struct_init_exprs(self, init_node, ir_type):
2055
+ exprs = list(getattr(init_node, "exprs", None) or [])
2056
+ field_names = self._struct_field_names(ir_type)
2057
+ if not exprs or not field_names:
2058
+ return exprs
2059
+ if not any(isinstance(expr, c_ast.NamedInitializer) for expr in exprs):
2060
+ return exprs
2061
+
2062
+ ordered = [None] * len(field_names)
2063
+ cursor = 0
2064
+ index_by_name = {name: i for i, name in enumerate(field_names)}
2065
+
2066
+ for expr in exprs:
2067
+ if isinstance(expr, c_ast.NamedInitializer):
2068
+ designators = getattr(expr, "name", None) or []
2069
+ if len(designators) == 1 and isinstance(designators[0], c_ast.ID):
2070
+ target = index_by_name.get(designators[0].name)
2071
+ if target is not None:
2072
+ ordered[target] = expr.expr
2073
+ cursor = target + 1
2074
+ continue
2075
+
2076
+ while cursor < len(ordered) and ordered[cursor] is not None:
2077
+ cursor += 1
2078
+ if cursor >= len(ordered):
2079
+ break
2080
+ ordered[cursor] = expr
2081
+ cursor += 1
2082
+
2083
+ return ordered
2084
+
2085
+ def _build_const_address(self, init_node):
2086
+ if isinstance(init_node, c_ast.ID):
2087
+ try:
2088
+ _, sym = self.lookup(init_node.name)
2089
+ except Exception:
2090
+ return None
2091
+ if isinstance(sym, (ir.Function, ir.GlobalVariable)):
2092
+ return sym
2093
+ return None
2094
+
2095
+ if isinstance(init_node, c_ast.Cast):
2096
+ return self._build_const_address(init_node.expr)
2097
+
2098
+ if isinstance(init_node, c_ast.ArrayRef):
2099
+ base_addr = self._build_const_address(init_node.name)
2100
+ if base_addr is None or not isinstance(
2101
+ getattr(base_addr, "type", None), ir.PointerType
2102
+ ):
2103
+ return None
2104
+ try:
2105
+ idx_val = int(self._eval_const_expr(init_node.subscript))
2106
+ except Exception:
2107
+ return None
2108
+ idx0 = ir.Constant(ir.IntType(32), 0)
2109
+ idx = ir.Constant(ir.IntType(32), idx_val)
2110
+ pointee = base_addr.type.pointee
2111
+ try:
2112
+ if isinstance(pointee, ir.ArrayType):
2113
+ return base_addr.gep([idx0, idx])
2114
+ return base_addr.gep([idx])
2115
+ except Exception:
2116
+ return None
2117
+
2118
+ if isinstance(init_node, c_ast.BinaryOp) and init_node.op in ("+", "-"):
2119
+ base_addr = self._build_const_address(init_node.left)
2120
+ offset_node = init_node.right
2121
+ offset_sign = 1
2122
+ if base_addr is None and init_node.op == "+":
2123
+ base_addr = self._build_const_address(init_node.right)
2124
+ offset_node = init_node.left
2125
+ elif base_addr is None:
2126
+ return None
2127
+ if base_addr is None or not isinstance(
2128
+ getattr(base_addr, "type", None), ir.PointerType
2129
+ ):
2130
+ return None
2131
+ try:
2132
+ idx_val = int(self._eval_const_expr(offset_node))
2133
+ except Exception:
2134
+ return None
2135
+ if init_node.op == "-":
2136
+ idx_val = -idx_val
2137
+ idx0 = ir.Constant(ir.IntType(32), 0)
2138
+ idx = ir.Constant(ir.IntType(32), idx_val)
2139
+ pointee = base_addr.type.pointee
2140
+ try:
2141
+ if isinstance(pointee, ir.ArrayType):
2142
+ return base_addr.gep([idx0, idx])
2143
+ return base_addr.gep([idx])
2144
+ except Exception:
2145
+ return None
2146
+
2147
+ if isinstance(init_node, c_ast.StructRef):
2148
+ base_addr = self._build_const_address(init_node.name)
2149
+ if base_addr is None or not isinstance(
2150
+ getattr(base_addr, "type", None), ir.PointerType
2151
+ ):
2152
+ return None
2153
+ if (
2154
+ init_node.type == "->"
2155
+ and isinstance(base_addr.type.pointee, ir.ArrayType)
2156
+ ):
2157
+ idx0 = ir.Constant(ir.IntType(32), 0)
2158
+ try:
2159
+ base_addr = base_addr.gep([idx0, idx0])
2160
+ except Exception:
2161
+ return None
2162
+ aggregate_type = base_addr.type.pointee
2163
+ try:
2164
+ offset, field_type = self._get_aggregate_field_info(
2165
+ aggregate_type, init_node.field.name
2166
+ )
2167
+ except Exception:
2168
+ return None
2169
+
2170
+ if (
2171
+ hasattr(aggregate_type, "members")
2172
+ and init_node.field.name in aggregate_type.members
2173
+ and not getattr(aggregate_type, "has_custom_layout", False)
2174
+ and not getattr(aggregate_type, "is_union", False)
2175
+ ):
2176
+ idx0 = ir.Constant(ir.IntType(32), 0)
2177
+ field_index = aggregate_type.members.index(init_node.field.name)
2178
+ try:
2179
+ return base_addr.gep(
2180
+ [idx0, ir.Constant(ir.IntType(32), field_index)]
2181
+ )
2182
+ except Exception:
2183
+ return None
2184
+
2185
+ try:
2186
+ byte_base = base_addr.bitcast(ir.PointerType(int8_t))
2187
+ byte_addr = byte_base.gep([ir.Constant(ir.IntType(32), offset)])
2188
+ return byte_addr.bitcast(ir.PointerType(field_type))
2189
+ except Exception:
2190
+ return None
2191
+
2192
+ return None
2193
+
1728
2194
  def _build_pointer_const(self, init_node, ir_type):
1729
2195
  if isinstance(init_node, c_ast.InitList):
1730
2196
  if init_node.exprs:
1731
2197
  return self._build_pointer_const(init_node.exprs[0], ir_type)
1732
2198
  return ir.Constant(ir_type, None)
2199
+ if isinstance(init_node, c_ast.Cast):
2200
+ return self._build_pointer_const(init_node.expr, ir_type)
1733
2201
  if (
1734
2202
  isinstance(init_node, c_ast.Constant)
1735
2203
  and getattr(init_node, "type", None) == "string"
@@ -1750,22 +2218,23 @@ class LLVMCodeGenerator(object):
1750
2218
  return sym
1751
2219
  if isinstance(sym.type, ir.PointerType):
1752
2220
  return sym.bitcast(ir_type)
2221
+ if isinstance(init_node, c_ast.ArrayRef):
2222
+ addr = self._build_const_address(init_node)
2223
+ if addr is not None:
2224
+ if addr.type == ir_type:
2225
+ return addr
2226
+ if isinstance(addr.type, ir.PointerType):
2227
+ return addr.bitcast(ir_type)
1753
2228
  if (
1754
2229
  isinstance(init_node, c_ast.UnaryOp)
1755
2230
  and init_node.op == "&"
1756
- and isinstance(init_node.expr, c_ast.ID)
1757
2231
  ):
1758
- try:
1759
- _, sym = self.lookup(init_node.expr.name)
1760
- except Exception:
1761
- sym = None
1762
- if isinstance(sym, ir.Function):
1763
- return sym if sym.type == ir_type else sym.bitcast(ir_type)
1764
- if isinstance(sym, ir.GlobalVariable):
1765
- if sym.type == ir_type:
1766
- return sym
1767
- if isinstance(sym.type, ir.PointerType):
1768
- return sym.bitcast(ir_type)
2232
+ addr = self._build_const_address(init_node.expr)
2233
+ if addr is not None:
2234
+ if addr.type == ir_type:
2235
+ return addr
2236
+ if isinstance(addr.type, ir.PointerType):
2237
+ return addr.bitcast(ir_type)
1769
2238
  try:
1770
2239
  val = self._eval_const_expr(init_node)
1771
2240
  if val == 0:
@@ -1846,6 +2315,17 @@ class LLVMCodeGenerator(object):
1846
2315
  result -= 1 << bits
1847
2316
  return ir.Constant(int_type, result)
1848
2317
 
2318
+ def _raw_bytes_to_unsigned_int(self, byte_values):
2319
+ result = 0
2320
+ width = len(byte_values)
2321
+ for i, byte_val in enumerate(byte_values):
2322
+ shift_bits = 8 * (i if self._is_little_endian() else (width - 1 - i))
2323
+ raw = getattr(byte_val, "constant", 0)
2324
+ if not isinstance(raw, int):
2325
+ raw = 0
2326
+ result |= (raw & 0xFF) << shift_bits
2327
+ return result
2328
+
1849
2329
  def _const_init_bytes(self, init_node, ir_type):
1850
2330
  size = self._ir_type_size(ir_type)
1851
2331
  if init_node is None:
@@ -1853,28 +2333,12 @@ class LLVMCodeGenerator(object):
1853
2333
 
1854
2334
  if getattr(ir_type, "is_union", False):
1855
2335
  raw = self._zero_bytes(size)
1856
- member_names = getattr(ir_type, "members", None) or list(
1857
- ir_type.member_types.keys()
2336
+ field_name, member_type, member_init = self._select_union_initializer(
2337
+ init_node, ir_type
1858
2338
  )
1859
- if not member_names:
2339
+ if field_name is None:
1860
2340
  return raw
1861
2341
 
1862
- first_name = member_names[0]
1863
- member_type = ir_type.member_types[first_name]
1864
- member_init = init_node
1865
- if isinstance(init_node, c_ast.InitList):
1866
- exprs = init_node.exprs or []
1867
- if not exprs:
1868
- member_init = None
1869
- elif isinstance(member_type, (ir.ArrayType, ir.LiteralStructType)):
1870
- member_init = (
1871
- exprs[0]
1872
- if len(exprs) == 1 and isinstance(exprs[0], c_ast.InitList)
1873
- else init_node
1874
- )
1875
- else:
1876
- member_init = exprs[0]
1877
-
1878
2342
  member_bytes = self._const_init_bytes(member_init, member_type)
1879
2343
  raw[: min(size, len(member_bytes))] = member_bytes[:size]
1880
2344
  return raw
@@ -1934,16 +2398,62 @@ class LLVMCodeGenerator(object):
1934
2398
 
1935
2399
  return self._zero_bytes(size)
1936
2400
 
1937
- if isinstance(ir_type, ir.LiteralStructType):
2401
+ if _is_struct_ir_type(ir_type):
2402
+ if getattr(ir_type, "has_custom_layout", False):
2403
+ raw = self._zero_bytes(size)
2404
+ if not isinstance(init_node, c_ast.InitList):
2405
+ return raw
2406
+
2407
+ exprs = self._ordered_struct_init_exprs(init_node, ir_type)
2408
+ for i, field_name in enumerate(getattr(ir_type, "members", ())):
2409
+ if i >= len(exprs):
2410
+ break
2411
+ expr = exprs[i]
2412
+ layout = ir_type.field_layouts.get(field_name)
2413
+ if layout is None:
2414
+ continue
2415
+
2416
+ if layout.is_bitfield:
2417
+ scalar_node = self._scalar_init_node(expr)
2418
+ if scalar_node is None:
2419
+ continue
2420
+ try:
2421
+ field_value = int(self._eval_const_expr(scalar_node))
2422
+ except Exception:
2423
+ continue
2424
+ storage_size = self._ir_type_size(layout.storage_ir_type)
2425
+ start = layout.storage_byte_offset
2426
+ current = self._raw_bytes_to_unsigned_int(
2427
+ raw[start : start + storage_size]
2428
+ )
2429
+ field_mask = self._bitfield_mask(layout.bit_width)
2430
+ clear_mask = ((1 << (storage_size * 8)) - 1) ^ (
2431
+ field_mask << layout.bit_offset
2432
+ )
2433
+ current = (current & clear_mask) | (
2434
+ (field_value & field_mask) << layout.bit_offset
2435
+ )
2436
+ raw[start : start + storage_size] = self._const_int_to_bytes(
2437
+ current, storage_size
2438
+ )
2439
+ continue
2440
+
2441
+ field_size = self._ir_type_size(layout.semantic_ir_type)
2442
+ field_bytes = self._const_init_bytes(expr, layout.semantic_ir_type)
2443
+ start = layout.byte_offset
2444
+ raw[start : start + field_size] = field_bytes[:field_size]
2445
+ return raw
2446
+
1938
2447
  raw = self._zero_bytes(size)
1939
2448
  if not isinstance(init_node, c_ast.InitList):
1940
2449
  return raw
1941
2450
 
2451
+ exprs = self._ordered_struct_init_exprs(init_node, ir_type)
1942
2452
  offset = 0
1943
2453
  for i, member_type in enumerate(ir_type.elements):
1944
2454
  align = self._ir_type_align(member_type)
1945
2455
  offset = (offset + align - 1) & ~(align - 1)
1946
- expr = init_node.exprs[i] if i < len(init_node.exprs) else None
2456
+ expr = exprs[i] if i < len(exprs) else None
1947
2457
  field_bytes = self._const_init_bytes(expr, member_type)
1948
2458
  field_size = self._ir_type_size(member_type)
1949
2459
  raw[offset : offset + field_size] = field_bytes[:field_size]
@@ -2015,11 +2525,36 @@ class LLVMCodeGenerator(object):
2015
2525
 
2016
2526
  return self._zero_initializer(ir_type)
2017
2527
 
2018
- if isinstance(ir_type, ir.LiteralStructType):
2528
+ if _is_struct_ir_type(ir_type):
2529
+ if getattr(ir_type, "has_custom_layout", False):
2530
+ try:
2531
+ raw = self._const_init_bytes(init_node, ir_type)
2532
+ values = []
2533
+ offset = 0
2534
+ for member_type in ir_type.elements:
2535
+ field_size = self._ir_type_size(member_type)
2536
+ field_bytes = raw[offset : offset + field_size]
2537
+ if isinstance(member_type, ir.IntType):
2538
+ values.append(
2539
+ self._bytes_to_int_constant(field_bytes, member_type)
2540
+ )
2541
+ elif (
2542
+ isinstance(member_type, ir.ArrayType)
2543
+ and isinstance(member_type.element, ir.IntType)
2544
+ and member_type.element.width == 8
2545
+ ):
2546
+ values.append(ir.Constant(member_type, field_bytes))
2547
+ else:
2548
+ values.append(self._zero_initializer(member_type))
2549
+ offset += field_size
2550
+ return ir.Constant(ir_type, values)
2551
+ except Exception:
2552
+ return self._zero_initializer(ir_type)
2019
2553
  if isinstance(init_node, c_ast.InitList):
2554
+ exprs = self._ordered_struct_init_exprs(init_node, ir_type)
2020
2555
  values = []
2021
2556
  for i, member_type in enumerate(ir_type.elements):
2022
- expr = init_node.exprs[i] if i < len(init_node.exprs) else None
2557
+ expr = exprs[i] if i < len(exprs) else None
2023
2558
  values.append(self._build_const_init(expr, member_type))
2024
2559
  try:
2025
2560
  return ir.Constant(ir_type, values)
@@ -2044,20 +2579,206 @@ class LLVMCodeGenerator(object):
2044
2579
  """Recursively initialize array elements from an InitList."""
2045
2580
  for i, expr in enumerate(init_list.exprs):
2046
2581
  idx = prefix_idx + [ir.Constant(ir.IntType(32), i)]
2047
- if isinstance(expr, c_ast.InitList):
2048
- self._init_array(base_addr, expr, elem_ir_type, idx)
2582
+ if isinstance(expr, c_ast.InitList) and isinstance(elem_ir_type, ir.ArrayType):
2583
+ self._init_array(base_addr, expr, elem_ir_type.element, idx)
2584
+ continue
2585
+ elem_ptr = self.builder.gep(base_addr, idx, inbounds=True)
2586
+ self._init_runtime_value(elem_ptr, elem_ir_type, expr)
2587
+
2588
+ def _init_runtime_value(self, dest_ptr, target_type, init_node):
2589
+ if dest_ptr is None or init_node is None:
2590
+ return
2591
+
2592
+ if isinstance(target_type, ir.ArrayType):
2593
+ if (
2594
+ isinstance(init_node, c_ast.Constant)
2595
+ and getattr(init_node, "type", None) == "string"
2596
+ and isinstance(target_type.element, ir.IntType)
2597
+ and target_type.element.width == 8
2598
+ ):
2599
+ raw = self._process_escapes(init_node.value[1:-1]) + "\00"
2600
+ idx0 = ir.Constant(ir.IntType(32), 0)
2601
+ for i, ch in enumerate(raw[: target_type.count]):
2602
+ elem_ptr = self.builder.gep(
2603
+ dest_ptr,
2604
+ [idx0, ir.Constant(ir.IntType(32), i)],
2605
+ inbounds=True,
2606
+ )
2607
+ self.builder.store(int8_t(ord(ch)), elem_ptr)
2608
+ return
2609
+ if isinstance(init_node, c_ast.InitList):
2610
+ self._init_array(
2611
+ dest_ptr,
2612
+ init_node,
2613
+ target_type.element,
2614
+ [ir.Constant(ir.IntType(32), 0)],
2615
+ )
2616
+ return
2617
+
2618
+ if getattr(target_type, "is_union", False) or _is_struct_ir_type(target_type):
2619
+ if isinstance(init_node, c_ast.InitList):
2620
+ self._init_runtime_aggregate(dest_ptr, init_node, target_type)
2621
+ return
2622
+ init_val, _ = self.codegen(init_node)
2623
+ if init_val is not None:
2624
+ if init_val.type != target_type:
2625
+ init_val = self._implicit_convert(init_val, target_type)
2626
+ self._safe_store(init_val, dest_ptr)
2627
+ return
2628
+
2629
+ scalar_node = self._scalar_init_node(init_node)
2630
+ if scalar_node is None:
2631
+ return
2632
+ init_val, _ = self.codegen(scalar_node)
2633
+ if init_val is None:
2634
+ return
2635
+ if init_val.type != target_type:
2636
+ init_val = self._implicit_convert(init_val, target_type)
2637
+ self._safe_store(init_val, dest_ptr)
2638
+
2639
+ def _init_runtime_aggregate(self, base_addr, init_node, ir_type):
2640
+ exprs = list(getattr(init_node, "exprs", None) or [])
2641
+ if getattr(ir_type, "is_union", False):
2642
+ field_name, field_type, member_init = self._select_union_initializer(
2643
+ init_node, ir_type
2644
+ )
2645
+ if field_name is None or member_init is None:
2646
+ return
2647
+ field_ptr = self.builder.bitcast(
2648
+ base_addr,
2649
+ ir.PointerType(field_type),
2650
+ name="unioninit",
2651
+ )
2652
+ self._init_runtime_value(field_ptr, field_type, member_init)
2653
+ return
2654
+
2655
+ if getattr(ir_type, "has_custom_layout", False):
2656
+ exprs = self._ordered_struct_init_exprs(init_node, ir_type)
2657
+ for i, field_name in enumerate(getattr(ir_type, "members", ())):
2658
+ if i >= len(exprs):
2659
+ break
2660
+ expr = exprs[i]
2661
+ layout = ir_type.field_layouts.get(field_name)
2662
+ if layout is None:
2663
+ continue
2664
+ if layout.is_bitfield:
2665
+ scalar_node = self._scalar_init_node(expr)
2666
+ if scalar_node is None:
2667
+ continue
2668
+ field_val, _ = self.codegen(scalar_node)
2669
+ if field_val is None:
2670
+ continue
2671
+ if field_val.type != layout.semantic_ir_type:
2672
+ field_val = self._implicit_convert(
2673
+ field_val,
2674
+ layout.semantic_ir_type,
2675
+ )
2676
+ ref = BitFieldRef(
2677
+ container_ptr=self._byte_offset_ptr(
2678
+ base_addr,
2679
+ layout.storage_byte_offset,
2680
+ ir.PointerType(layout.storage_ir_type),
2681
+ name="bitfieldptr",
2682
+ ),
2683
+ storage_ir_type=layout.storage_ir_type,
2684
+ bit_offset=layout.bit_offset,
2685
+ bit_width=layout.bit_width,
2686
+ semantic_ir_type=layout.semantic_ir_type,
2687
+ is_unsigned=layout.is_unsigned,
2688
+ )
2689
+ self._store_bitfield(field_val, ref)
2690
+ continue
2691
+
2692
+ field_ptr = self._byte_offset_ptr(
2693
+ base_addr,
2694
+ layout.byte_offset,
2695
+ ir.PointerType(layout.semantic_ir_type),
2696
+ name="fieldptr",
2697
+ )
2698
+ self._init_runtime_value(field_ptr, layout.semantic_ir_type, expr)
2699
+ return
2700
+
2701
+ exprs = self._ordered_struct_init_exprs(init_node, ir_type)
2702
+ for i, field_type in enumerate(ir_type.elements):
2703
+ if i >= len(exprs):
2704
+ break
2705
+ expr = exprs[i]
2706
+ field_addr = self.builder.gep(
2707
+ base_addr,
2708
+ [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), i)],
2709
+ inbounds=True,
2710
+ )
2711
+ semantic_field_type = self._refine_member_ir_type(ir_type, i, field_type)
2712
+ typed_field_addr = field_addr
2713
+ target_ptr_type = ir.PointerType(semantic_field_type)
2714
+ if field_addr.type != target_ptr_type:
2715
+ try:
2716
+ typed_field_addr = self.builder.bitcast(
2717
+ field_addr,
2718
+ target_ptr_type,
2719
+ )
2720
+ except Exception:
2721
+ typed_field_addr = field_addr
2722
+ self._init_runtime_value(typed_field_addr, semantic_field_type, expr)
2723
+
2724
+ def _select_union_initializer(self, init_node, ir_type):
2725
+ member_names = getattr(ir_type, "members", None) or list(
2726
+ getattr(ir_type, "member_types", {}).keys()
2727
+ )
2728
+ if not member_names:
2729
+ return None, None, None
2730
+
2731
+ field_name = member_names[0]
2732
+ field_type = self._refine_member_ir_type(
2733
+ ir_type,
2734
+ field_name,
2735
+ ir_type.member_types[field_name],
2736
+ )
2737
+ member_init = init_node
2738
+
2739
+ if isinstance(init_node, c_ast.InitList):
2740
+ exprs = init_node.exprs or []
2741
+ if not exprs:
2742
+ return field_name, field_type, None
2743
+
2744
+ first_expr = exprs[0]
2745
+ if isinstance(first_expr, c_ast.NamedInitializer):
2746
+ designators = getattr(first_expr, "name", None) or []
2747
+ if len(designators) == 1 and isinstance(designators[0], c_ast.ID):
2748
+ candidate = designators[0].name
2749
+ if candidate in ir_type.member_types:
2750
+ field_name = candidate
2751
+ field_type = self._refine_member_ir_type(
2752
+ ir_type,
2753
+ field_name,
2754
+ ir_type.member_types[field_name],
2755
+ )
2756
+ return field_name, field_type, first_expr.expr
2757
+ first_expr = first_expr.expr
2758
+
2759
+ if isinstance(field_type, (ir.ArrayType, ir.IdentifiedStructType, ir.LiteralStructType)):
2760
+ member_init = (
2761
+ first_expr
2762
+ if len(exprs) == 1 and isinstance(first_expr, c_ast.InitList)
2763
+ else init_node
2764
+ )
2049
2765
  else:
2050
- val, _ = self.codegen(expr)
2051
- val = self._implicit_convert(val, elem_ir_type)
2052
- elem_ptr = self.builder.gep(base_addr, idx, inbounds=True)
2053
- self._safe_store(val, elem_ptr)
2766
+ member_init = first_expr
2054
2767
 
2055
- def _build_array_ir_type(self, array_decl):
2768
+ return field_name, field_type, member_init
2769
+
2770
+ def _build_array_ir_type(self, array_decl, init_node=None):
2056
2771
  dims = []
2057
2772
  node = array_decl
2773
+ inferred_top_dim = self._infer_array_count_from_initializer(init_node)
2774
+ is_top_level = True
2058
2775
  while isinstance(node, c_ast.ArrayDecl):
2059
- dims.append(self._eval_dim(node.dim) if node.dim else 0)
2776
+ dim = self._eval_dim(node.dim) if node.dim else 0
2777
+ if dim == 0 and is_top_level and inferred_top_dim is not None:
2778
+ dim = inferred_top_dim
2779
+ dims.append(dim)
2060
2780
  node = node.type
2781
+ is_top_level = False
2061
2782
  elem_ir_type = self._resolve_ast_type(node)
2062
2783
  if isinstance(elem_ir_type, ir.VoidType):
2063
2784
  elem_ir_type = int8_t
@@ -2069,16 +2790,30 @@ class LLVMCodeGenerator(object):
2069
2790
 
2070
2791
  def _resolve_param_type(self, param):
2071
2792
  """Resolve a function parameter type, handling typedefs and pointers."""
2072
- if isinstance(param.type, c_ast.ArrayDecl):
2073
- arr_type = self._build_array_ir_type(param.type)
2074
- return ir.PointerType(arr_type.element)
2075
- t = self._resolve_ast_type(param.type)
2793
+ node_type = param.type if hasattr(param, "type") else param
2794
+ if isinstance(node_type, c_ast.ArrayDecl):
2795
+ inner = node_type.type
2796
+ if isinstance(inner, c_ast.ArrayDecl):
2797
+ return ir.PointerType(self._build_array_ir_type(inner))
2798
+ elem_ir_type = self._resolve_ast_type(inner)
2799
+ if isinstance(elem_ir_type, ir.VoidType):
2800
+ elem_ir_type = int8_t
2801
+ return ir.PointerType(elem_ir_type)
2802
+ t = self._resolve_ast_type(node_type)
2076
2803
  if isinstance(t, ir.ArrayType):
2077
2804
  return ir.PointerType(t.element)
2078
2805
  if isinstance(t, ir.VoidType):
2079
2806
  return None # void params mean "no params" in C
2080
2807
  return t
2081
2808
 
2809
+ def _emit_vla_param_bound_side_effects(self, node_type):
2810
+ current = node_type
2811
+ while isinstance(current, c_ast.ArrayDecl):
2812
+ dim = current.dim
2813
+ if dim is not None and not isinstance(dim, c_ast.Constant):
2814
+ self.codegen(dim)
2815
+ current = current.type
2816
+
2082
2817
  def _resolve_ast_type(self, node_type):
2083
2818
  """Recursively resolve an AST type to IR type, with typedef support."""
2084
2819
  if isinstance(node_type, c_ast.PtrDecl):
@@ -2096,6 +2831,9 @@ class LLVMCodeGenerator(object):
2096
2831
  return self.codegen_Struct(node_type.type)
2097
2832
  elif isinstance(node_type.type, c_ast.Union):
2098
2833
  return self.codegen_Union(node_type.type)
2834
+ elif isinstance(node_type.type, c_ast.Enum):
2835
+ self.codegen_Enum(node_type.type)
2836
+ return int32_t
2099
2837
  return int64_t
2100
2838
  elif isinstance(node_type, c_ast.ArrayDecl):
2101
2839
  return voidptr_t
@@ -2110,31 +2848,79 @@ class LLVMCodeGenerator(object):
2110
2848
  return int(v, 0) # handles hex/octal/decimal
2111
2849
  return self._eval_const_expr(dim_node)
2112
2850
 
2851
+ def _infer_array_count_from_initializer(self, init_node):
2852
+ if init_node is None:
2853
+ return None
2854
+ if isinstance(init_node, c_ast.InitList):
2855
+ return len(init_node.exprs)
2856
+ if (
2857
+ isinstance(init_node, c_ast.Constant)
2858
+ and getattr(init_node, "type", None) == "string"
2859
+ ):
2860
+ raw = init_node.value[1:-1]
2861
+ return len(self._process_escapes(raw)) + 1
2862
+ return None
2863
+
2113
2864
  def _build_func_ptr_type(self, func_decl_node):
2114
2865
  """Build an IR function pointer type from a FuncDecl AST node."""
2866
+ func_type, _ = self._build_function_ir_type(func_decl_node)
2867
+ return func_type.as_pointer()
2868
+
2869
+ def _build_function_ir_type(self, func_decl_node):
2870
+ """Build an IR function type from a FuncDecl AST node."""
2115
2871
  ret_ir, _ = self.codegen(func_decl_node)
2116
2872
  param_types = []
2873
+ is_var_arg = False
2117
2874
  if func_decl_node.args:
2118
2875
  for param in func_decl_node.args.params:
2119
2876
  if isinstance(param, c_ast.EllipsisParam):
2877
+ is_var_arg = True
2120
2878
  continue
2121
- if isinstance(param, c_ast.Typename):
2122
- t = self._resolve_ast_type(param.type)
2123
- if not isinstance(t, ir.VoidType):
2124
- param_types.append(t)
2125
- elif isinstance(param, c_ast.Decl):
2879
+ if isinstance(param, (c_ast.Typename, c_ast.Decl)):
2126
2880
  t = self._resolve_param_type(param)
2127
2881
  if t is not None:
2128
2882
  param_types.append(t)
2129
2883
  if isinstance(ret_ir, ir.VoidType):
2130
2884
  ret_ir = ir.VoidType()
2131
- func_type = ir.FunctionType(ret_ir, param_types)
2132
- return func_type.as_pointer()
2885
+ return ir.FunctionType(ret_ir, param_types, var_arg=is_var_arg), ret_ir
2133
2886
 
2134
- def _safe_load(self, ptr, name=""):
2135
- """Load from ptr, guard against non-pointer types."""
2136
- if not isinstance(ptr.type, ir.PointerType):
2137
- return ptr
2887
+ def _funcdef_param_infos(self, node):
2888
+ infos = []
2889
+ is_var_arg = False
2890
+ if not node.decl.type.args:
2891
+ return infos, is_var_arg
2892
+
2893
+ knr_param_decls = {
2894
+ decl.name: decl for decl in (getattr(node, "param_decls", None) or [])
2895
+ }
2896
+
2897
+ for index, param in enumerate(node.decl.type.args.params):
2898
+ if isinstance(param, c_ast.EllipsisParam):
2899
+ is_var_arg = True
2900
+ continue
2901
+
2902
+ decl = param
2903
+ if isinstance(param, c_ast.ID):
2904
+ decl = knr_param_decls.get(param.name)
2905
+ if decl is None:
2906
+ infos.append((param.name, int32_t, None))
2907
+ continue
2908
+
2909
+ t = self._resolve_param_type(decl)
2910
+ if t is None:
2911
+ continue
2912
+
2913
+ pname = getattr(decl, "name", None)
2914
+ if not isinstance(pname, str):
2915
+ pname = f"arg{index}"
2916
+ infos.append((pname, t, decl))
2917
+
2918
+ return infos, is_var_arg
2919
+
2920
+ def _safe_load(self, ptr, name=""):
2921
+ """Load from ptr, guard against non-pointer types."""
2922
+ if not isinstance(ptr.type, ir.PointerType):
2923
+ return ptr
2138
2924
  if isinstance(ptr.type.pointee, ir.FunctionType):
2139
2925
  return ptr # function pointers are first-class as pointers
2140
2926
  try:
@@ -2155,9 +2941,26 @@ class LLVMCodeGenerator(object):
2155
2941
  gv.global_constant = True
2156
2942
  gv.linkage = "internal"
2157
2943
  base = gv
2944
+ elif not isinstance(getattr(value, "type", None), ir.PointerType):
2945
+ base = self._alloca_in_entry(value.type, f"{name}.tmp")
2946
+ self._safe_store(value, base)
2158
2947
  idx0 = ir.Constant(ir.IntType(32), 0)
2159
2948
  return self.builder.gep(base, [idx0, idx0], name=name)
2160
2949
 
2950
+ def _decay_array_expr_to_pointer(self, expr_node, value, name="arrayexprdecay"):
2951
+ """Apply array-to-pointer decay using the expression's semantic type."""
2952
+ semantic_type = self._get_expr_ir_type(expr_node)
2953
+ if isinstance(semantic_type, ir.ArrayType):
2954
+ if isinstance(getattr(value, "type", None), ir.ArrayType):
2955
+ return self._decay_array_value_to_pointer(value, name)
2956
+ if (
2957
+ isinstance(getattr(value, "type", None), ir.PointerType)
2958
+ and value.type.pointee == semantic_type
2959
+ ):
2960
+ idx0 = ir.Constant(ir.IntType(32), 0)
2961
+ return self.builder.gep(value, [idx0, idx0], name=name)
2962
+ return self._decay_array_value_to_pointer(value, name)
2963
+
2161
2964
  def _safe_store(self, value, ptr):
2162
2965
  """Store value to ptr, auto-converting types if needed."""
2163
2966
  if value is None or ptr is None:
@@ -2177,11 +2980,9 @@ class LLVMCodeGenerator(object):
2177
2980
  """Convert val to target_type if needed (implicit C promotion/truncation)."""
2178
2981
  if val is None or isinstance(val.type, ir.VoidType):
2179
2982
  # Can't convert void — return a zero of target type
2180
- if isinstance(target_type, ir.PointerType):
2181
- return ir.Constant(target_type, None)
2182
- elif isinstance(target_type, ir.VoidType):
2983
+ if isinstance(target_type, ir.VoidType):
2183
2984
  return val
2184
- return ir.Constant(target_type, 0)
2985
+ return self._zero_initializer(target_type)
2185
2986
  if val.type == target_type:
2186
2987
  return val
2187
2988
  if isinstance(val.type, ir.IntType) and self._is_floating_ir_type(target_type):
@@ -2242,6 +3043,49 @@ class LLVMCodeGenerator(object):
2242
3043
  return self.builder.bitcast(ptr, target_type)
2243
3044
  return val
2244
3045
 
3046
+ def _is_scalar_ir_type(self, ir_type):
3047
+ return (
3048
+ isinstance(ir_type, (ir.IntType, ir.PointerType))
3049
+ or self._is_floating_ir_type(ir_type)
3050
+ )
3051
+
3052
+ def _is_aggregate_ir_type(self, ir_type):
3053
+ return _is_struct_ir_type(ir_type) or getattr(ir_type, "is_union", False)
3054
+
3055
+ def _validate_explicit_cast(self, source_type, target_type):
3056
+ if isinstance(target_type, ir.VoidType):
3057
+ return
3058
+
3059
+ if self._is_aggregate_ir_type(target_type) or isinstance(
3060
+ target_type, ir.ArrayType
3061
+ ):
3062
+ raise SemanticError("invalid cast to non-scalar type")
3063
+
3064
+ if self._is_aggregate_ir_type(source_type):
3065
+ raise SemanticError("invalid cast from non-scalar type")
3066
+
3067
+ if isinstance(source_type, ir.ArrayType):
3068
+ if not isinstance(target_type, ir.PointerType):
3069
+ raise SemanticError("invalid cast from array type")
3070
+ return
3071
+
3072
+ if self._is_floating_ir_type(source_type) and isinstance(
3073
+ target_type, ir.PointerType
3074
+ ):
3075
+ raise SemanticError("invalid cast from floating type to pointer type")
3076
+
3077
+ if isinstance(source_type, ir.PointerType) and self._is_floating_ir_type(
3078
+ target_type
3079
+ ):
3080
+ raise SemanticError("invalid cast from pointer type to floating type")
3081
+
3082
+ if self._is_scalar_ir_type(source_type) and self._is_scalar_ir_type(
3083
+ target_type
3084
+ ):
3085
+ return
3086
+
3087
+ raise SemanticError("invalid cast expression")
3088
+
2245
3089
  def _extend_call_result(self, result, returns_unsigned=False):
2246
3090
  if not isinstance(result.type, ir.IntType):
2247
3091
  return result
@@ -2267,6 +3111,9 @@ class LLVMCodeGenerator(object):
2267
3111
 
2268
3112
  def _ir_type_align(self, ir_type):
2269
3113
  """Return natural alignment of an IR type in bytes."""
3114
+ custom_align = getattr(ir_type, "custom_align", None)
3115
+ if custom_align is not None:
3116
+ return custom_align
2270
3117
  if isinstance(ir_type, ir.IntType):
2271
3118
  return min(ir_type.width // 8, 8)
2272
3119
  elif isinstance(ir_type, ir.FloatType):
@@ -2277,7 +3124,7 @@ class LLVMCodeGenerator(object):
2277
3124
  return 8
2278
3125
  elif isinstance(ir_type, ir.ArrayType):
2279
3126
  return self._ir_type_align(ir_type.element)
2280
- elif isinstance(ir_type, ir.LiteralStructType):
3127
+ elif _is_struct_ir_type(ir_type):
2281
3128
  if not ir_type.elements:
2282
3129
  return 1
2283
3130
  return max(self._ir_type_align(e) for e in ir_type.elements)
@@ -2285,6 +3132,9 @@ class LLVMCodeGenerator(object):
2285
3132
 
2286
3133
  def _ir_type_size(self, ir_type):
2287
3134
  """Compute byte size of an IR type with proper alignment/padding."""
3135
+ custom_size = getattr(ir_type, "custom_size", None)
3136
+ if custom_size is not None:
3137
+ return custom_size
2288
3138
  if isinstance(ir_type, ir.IntType):
2289
3139
  return ir_type.width // 8
2290
3140
  elif isinstance(ir_type, ir.FloatType):
@@ -2295,7 +3145,7 @@ class LLVMCodeGenerator(object):
2295
3145
  return 8
2296
3146
  elif isinstance(ir_type, ir.ArrayType):
2297
3147
  return int(ir_type.count) * self._ir_type_size(ir_type.element)
2298
- elif isinstance(ir_type, ir.LiteralStructType):
3148
+ elif _is_struct_ir_type(ir_type):
2299
3149
  offset = 0
2300
3150
  for elem in ir_type.elements:
2301
3151
  align = self._ir_type_align(elem)
@@ -2307,6 +3157,232 @@ class LLVMCodeGenerator(object):
2307
3157
  return offset
2308
3158
  return 8
2309
3159
 
3160
+ @staticmethod
3161
+ def _align_up(value, align):
3162
+ if align <= 1:
3163
+ return value
3164
+ return (value + align - 1) & ~(align - 1)
3165
+
3166
+ def _resolve_struct_member_ir_type(self, decl):
3167
+ if isinstance(decl.type, c_ast.TypeDecl) and isinstance(
3168
+ decl.type.type, c_ast.Struct
3169
+ ):
3170
+ return self.codegen_Struct(decl.type.type)
3171
+ if isinstance(decl.type, c_ast.TypeDecl) and isinstance(
3172
+ decl.type.type, c_ast.Union
3173
+ ):
3174
+ return self.codegen_Union(decl.type.type)
3175
+ if isinstance(decl.type, c_ast.ArrayDecl):
3176
+ def _build_array_type(arr_node):
3177
+ dim = self._eval_dim(arr_node.dim) if arr_node.dim else 0
3178
+ if isinstance(arr_node.type, c_ast.ArrayDecl):
3179
+ inner = _build_array_type(arr_node.type)
3180
+ else:
3181
+ inner = self._resolve_ast_type(arr_node.type)
3182
+ return ir.ArrayType(inner, dim)
3183
+
3184
+ return _build_array_type(decl.type)
3185
+ if isinstance(decl.type, c_ast.PtrDecl):
3186
+ return self._resolve_ast_type(decl.type)
3187
+ if isinstance(decl.type, c_ast.TypeDecl):
3188
+ return self._resolve_ast_type(decl.type)
3189
+ return int64_t
3190
+
3191
+ def _bitfield_storage_ir_type(self, decl):
3192
+ storage_ir_type = self._resolve_ast_type(decl.type)
3193
+ if not isinstance(storage_ir_type, ir.IntType):
3194
+ return int32_t
3195
+ return storage_ir_type
3196
+
3197
+ def _raw_layout_struct_type(
3198
+ self, size_bytes, align_bytes, type_name=None, existing_type=None
3199
+ ):
3200
+ align_map = {8: int64_t, 4: int32_t, 2: int16_t, 1: int8_t}
3201
+ identified_name = type_name or self._aggregate_type_name("layout")
3202
+ if size_bytes <= 0:
3203
+ body = []
3204
+ elif align_bytes <= 1:
3205
+ body = [ir.ArrayType(int8_t, size_bytes)]
3206
+ else:
3207
+ align_type = align_map.get(align_bytes)
3208
+ if align_type is None or size_bytes < align_bytes:
3209
+ body = [ir.ArrayType(int8_t, size_bytes)]
3210
+ else:
3211
+ pad_size = size_bytes - align_bytes
3212
+ if pad_size > 0:
3213
+ body = [align_type, ir.ArrayType(int8_t, pad_size)]
3214
+ else:
3215
+ body = [align_type]
3216
+ if existing_type is not None:
3217
+ storage_type = existing_type
3218
+ else:
3219
+ storage_type = self.module.context.get_identified_type(identified_name)
3220
+ if storage_type.is_opaque:
3221
+ storage_type.set_body(*body)
3222
+ storage_type.custom_size = size_bytes
3223
+ storage_type.custom_align = align_bytes
3224
+ storage_type.has_custom_layout = True
3225
+ return storage_type
3226
+
3227
+ def _build_layout_backed_struct(self, node):
3228
+ current_bit = 0
3229
+ max_align = 1
3230
+ member_names = []
3231
+ member_decl_types = []
3232
+ field_layouts = {}
3233
+
3234
+ for decl in node.decls:
3235
+ if decl.bitsize is not None:
3236
+ storage_ir_type = self._bitfield_storage_ir_type(decl)
3237
+ storage_bits = storage_ir_type.width
3238
+ bit_width = self._eval_const_expr(decl.bitsize)
3239
+ max_align = max(max_align, self._ir_type_align(storage_ir_type))
3240
+ if bit_width == 0:
3241
+ current_bit = self._align_up(current_bit, storage_bits)
3242
+ continue
3243
+ unit_start = (current_bit // storage_bits) * storage_bits
3244
+ if current_bit + bit_width > unit_start + storage_bits:
3245
+ current_bit = self._align_up(current_bit, storage_bits)
3246
+ unit_start = current_bit
3247
+ layout = StructFieldLayout(
3248
+ name=decl.name,
3249
+ byte_offset=current_bit // 8,
3250
+ semantic_ir_type=storage_ir_type,
3251
+ decl_type=decl.type,
3252
+ is_bitfield=True,
3253
+ storage_byte_offset=unit_start // 8,
3254
+ storage_ir_type=storage_ir_type,
3255
+ bit_offset=current_bit - unit_start,
3256
+ bit_width=bit_width,
3257
+ is_unsigned=self._is_unsigned_scalar_decl_type(decl.type),
3258
+ )
3259
+ if decl.name is not None:
3260
+ field_layouts[decl.name] = layout
3261
+ member_names.append(decl.name)
3262
+ member_decl_types.append(decl.type)
3263
+ current_bit += bit_width
3264
+ continue
3265
+
3266
+ semantic_ir_type = self._resolve_struct_member_ir_type(decl)
3267
+ align_bits = self._ir_type_align(semantic_ir_type) * 8
3268
+ current_bit = self._align_up(current_bit, align_bits)
3269
+ max_align = max(max_align, self._ir_type_align(semantic_ir_type))
3270
+ layout = StructFieldLayout(
3271
+ name=decl.name,
3272
+ byte_offset=current_bit // 8,
3273
+ semantic_ir_type=semantic_ir_type,
3274
+ decl_type=decl.type,
3275
+ )
3276
+ if decl.name is not None:
3277
+ field_layouts[decl.name] = layout
3278
+ member_names.append(decl.name)
3279
+ member_decl_types.append(decl.type)
3280
+ current_bit += self._ir_type_size(semantic_ir_type) * 8
3281
+
3282
+ size_bits = self._align_up(current_bit, max_align * 8)
3283
+ size_bytes = max(1, size_bits // 8)
3284
+ type_name = None
3285
+ existing_type = None
3286
+ if node.name:
3287
+ tag_key = self._tag_type_key(node.name)
3288
+ if tag_key in self.env:
3289
+ existing_type = self.env[tag_key][0]
3290
+ type_name = self._aggregate_type_name("struct", node.name)
3291
+ struct_type = self._raw_layout_struct_type(
3292
+ size_bytes,
3293
+ max_align,
3294
+ type_name,
3295
+ existing_type=existing_type,
3296
+ )
3297
+ struct_type.members = member_names
3298
+ struct_type.member_decl_types = member_decl_types
3299
+ struct_type.field_layouts = field_layouts
3300
+ if node.name:
3301
+ self.define(self._tag_type_key(node.name), (struct_type, None))
3302
+ return struct_type
3303
+
3304
+ def _byte_offset_ptr(self, base_ptr, byte_offset, target_ptr_type, name="fieldptr"):
3305
+ byte_ptr = self.builder.bitcast(base_ptr, voidptr_t, name=f"{name}.base")
3306
+ if byte_offset:
3307
+ byte_ptr = self.builder.gep(
3308
+ byte_ptr,
3309
+ [ir.Constant(ir.IntType(32), byte_offset)],
3310
+ name=f"{name}.offs",
3311
+ )
3312
+ if byte_ptr.type != target_ptr_type:
3313
+ return self.builder.bitcast(byte_ptr, target_ptr_type, name=name)
3314
+ return byte_ptr
3315
+
3316
+ @staticmethod
3317
+ def _bitfield_mask(bit_width):
3318
+ if bit_width <= 0:
3319
+ return 0
3320
+ return (1 << bit_width) - 1
3321
+
3322
+ def _load_bitfield(self, ref):
3323
+ align = max(1, self._ir_type_align(ref.storage_ir_type))
3324
+ raw = self.builder.load(ref.container_ptr, align=align)
3325
+ if ref.bit_offset:
3326
+ raw = self.builder.lshr(
3327
+ raw, ir.Constant(ref.storage_ir_type, ref.bit_offset), "bitshift"
3328
+ )
3329
+
3330
+ semantic_width = ref.semantic_ir_type.width
3331
+ if ref.is_unsigned:
3332
+ if ref.bit_width < ref.storage_ir_type.width:
3333
+ raw = self.builder.and_(
3334
+ raw,
3335
+ ir.Constant(ref.storage_ir_type, self._bitfield_mask(ref.bit_width)),
3336
+ "bitmask",
3337
+ )
3338
+ if raw.type.width > semantic_width:
3339
+ raw = self.builder.trunc(raw, ref.semantic_ir_type, "bittrunc")
3340
+ elif raw.type.width < semantic_width:
3341
+ raw = self.builder.zext(raw, ref.semantic_ir_type, "bitzext")
3342
+ self._tag_unsigned(raw)
3343
+ return raw
3344
+
3345
+ if ref.bit_width < ref.storage_ir_type.width:
3346
+ narrow_type = ir.IntType(ref.bit_width)
3347
+ raw = self.builder.trunc(raw, narrow_type, "bitsigned.trunc")
3348
+ return self.builder.sext(raw, ref.semantic_ir_type, "bitsigned.sext")
3349
+ if raw.type.width > semantic_width:
3350
+ return self.builder.trunc(raw, ref.semantic_ir_type, "bittrunc")
3351
+ if raw.type.width < semantic_width:
3352
+ return self.builder.sext(raw, ref.semantic_ir_type, "bitsext")
3353
+ return raw
3354
+
3355
+ def _store_bitfield(self, value, ref):
3356
+ if value is None:
3357
+ return
3358
+ align = max(1, self._ir_type_align(ref.storage_ir_type))
3359
+ storage_value = self.builder.load(ref.container_ptr, align=align)
3360
+ if value.type != ref.semantic_ir_type:
3361
+ value = self._implicit_convert(value, ref.semantic_ir_type)
3362
+ value = self._convert_int_value(
3363
+ value, ref.storage_ir_type, result_unsigned=ref.is_unsigned
3364
+ )
3365
+ field_mask = self._bitfield_mask(ref.bit_width)
3366
+ field_mask_const = ir.Constant(ref.storage_ir_type, field_mask)
3367
+ if ref.bit_width < ref.storage_ir_type.width:
3368
+ value = self.builder.and_(value, field_mask_const, "bitstore.mask")
3369
+ if ref.bit_offset:
3370
+ value = self.builder.shl(
3371
+ value,
3372
+ ir.Constant(ref.storage_ir_type, ref.bit_offset),
3373
+ "bitstore.shift",
3374
+ )
3375
+ clear_mask = ((1 << ref.storage_ir_type.width) - 1) ^ (
3376
+ field_mask << ref.bit_offset
3377
+ )
3378
+ storage_value = self.builder.and_(
3379
+ storage_value,
3380
+ ir.Constant(ref.storage_ir_type, clear_mask),
3381
+ "bitstore.clear",
3382
+ )
3383
+ value = self.builder.or_(storage_value, value, "bitstore.merge")
3384
+ self.builder.store(value, ref.container_ptr, align=align)
3385
+
2310
3386
  def _refine_member_ir_type(self, aggregate_type, member_key, field_type):
2311
3387
  """Prefer semantic member types over storage types when available."""
2312
3388
  semantic_field_type = field_type
@@ -2331,8 +3407,8 @@ class LLVMCodeGenerator(object):
2331
3407
  resolved, ir.PointerType
2332
3408
  ):
2333
3409
  return semantic_field_type
2334
- if isinstance(
2335
- resolved, (ir.ArrayType, ir.LiteralStructType, ir.PointerType)
3410
+ if isinstance(resolved, (ir.ArrayType, ir.PointerType)) or _is_struct_ir_type(
3411
+ resolved
2336
3412
  ):
2337
3413
  return resolved
2338
3414
  except Exception:
@@ -2349,6 +3425,14 @@ class LLVMCodeGenerator(object):
2349
3425
  )
2350
3426
  return 0, semantic_field_type
2351
3427
 
3428
+ if getattr(aggregate_type, "has_custom_layout", False):
3429
+ layout = aggregate_type.field_layouts.get(field_name)
3430
+ if layout is None:
3431
+ raise CodegenError(f"Field '{field_name}' not found in aggregate")
3432
+ if layout.is_bitfield:
3433
+ raise CodegenError("offsetof on bit-field is not supported")
3434
+ return layout.byte_offset, layout.semantic_ir_type
3435
+
2352
3436
  if not hasattr(aggregate_type, "members"):
2353
3437
  raise CodegenError(f"Aggregate has no named fields: {aggregate_type}")
2354
3438
 
@@ -2393,6 +3477,153 @@ class LLVMCodeGenerator(object):
2393
3477
 
2394
3478
  raise CodegenError(f"Not an offsetof base: {type(node).__name__}")
2395
3479
 
3480
+ def _infer_sizeof_operand_ir_type(self, node):
3481
+ """Infer the operand type for sizeof without emitting runtime IR."""
3482
+ cached = self._get_expr_ir_type(node)
3483
+ if cached is not None:
3484
+ return cached
3485
+
3486
+ if isinstance(node, c_ast.Constant):
3487
+ if node.type == "int":
3488
+ raw = node.value
3489
+ lower = raw.lower()
3490
+ val_str = raw.rstrip("uUlL")
3491
+ if val_str.startswith("0x") or val_str.startswith("0X"):
3492
+ value = int(val_str, 16)
3493
+ elif val_str.startswith("0") and len(val_str) > 1 and val_str[1:].isdigit():
3494
+ value = int(val_str, 8)
3495
+ else:
3496
+ value = int(val_str)
3497
+ if "l" in lower or value > 0x7FFFFFFF:
3498
+ return int64_t
3499
+ return int32_t
3500
+ if node.type == "char":
3501
+ return int32_t
3502
+ if node.type == "string":
3503
+ raw = node.value[1:-1]
3504
+ processed = self._process_escapes(raw)
3505
+ return ir.ArrayType(int8_t, len(self._string_bytes(processed + "\00")))
3506
+ return self._float_literal_ir_type(node.value)
3507
+
3508
+ if isinstance(node, c_ast.ID):
3509
+ ir_type, _ = self.lookup(node.name)
3510
+ return ir_type
3511
+
3512
+ if isinstance(node, c_ast.StructRef):
3513
+ base_type = self._infer_sizeof_operand_ir_type(node.name)
3514
+ aggregate_type = base_type
3515
+ if node.type == "->":
3516
+ if not isinstance(base_type, ir.PointerType):
3517
+ raise CodegenError(
3518
+ f"sizeof operand is not a pointer for '->': {base_type}"
3519
+ )
3520
+ aggregate_type = base_type.pointee
3521
+ _, field_type = self._get_aggregate_field_info(
3522
+ aggregate_type, node.field.name
3523
+ )
3524
+ return field_type
3525
+
3526
+ if isinstance(node, c_ast.ArrayRef):
3527
+ base_type = self._infer_sizeof_operand_ir_type(node.name)
3528
+ if isinstance(base_type, ir.ArrayType):
3529
+ return base_type.element
3530
+ if isinstance(base_type, ir.PointerType):
3531
+ return base_type.pointee
3532
+ raise CodegenError(
3533
+ f"sizeof operand is not indexable: {type(base_type).__name__}"
3534
+ )
3535
+
3536
+ if isinstance(node, c_ast.Cast):
3537
+ return self._resolve_ast_type(node.to_type.type)
3538
+
3539
+ if isinstance(node, c_ast.UnaryOp):
3540
+ if node.op == "&":
3541
+ return ir.PointerType(self._infer_sizeof_operand_ir_type(node.expr))
3542
+ if node.op == "*":
3543
+ base_type = self._infer_sizeof_operand_ir_type(node.expr)
3544
+ if isinstance(base_type, ir.ArrayType):
3545
+ return base_type.element
3546
+ if not isinstance(base_type, ir.PointerType):
3547
+ raise CodegenError(
3548
+ f"sizeof operand is not a pointer for '*': {base_type}"
3549
+ )
3550
+ return base_type.pointee
3551
+ if node.op in ("+", "-", "~"):
3552
+ base_type = self._infer_sizeof_operand_ir_type(node.expr)
3553
+ if isinstance(base_type, ir.IntType):
3554
+ return self._integer_promotion_ir_type(base_type)
3555
+ return base_type
3556
+ if node.op == "!":
3557
+ return int32_t
3558
+ if node.op in ("p++", "p--", "++", "--"):
3559
+ return self._infer_sizeof_operand_ir_type(node.expr)
3560
+ if node.op == "sizeof":
3561
+ return int64_t
3562
+
3563
+ if isinstance(node, c_ast.BinaryOp):
3564
+ lhs_type = self._decay_ir_type(
3565
+ self._infer_sizeof_operand_ir_type(node.left)
3566
+ )
3567
+ rhs_type = self._decay_ir_type(
3568
+ self._infer_sizeof_operand_ir_type(node.right)
3569
+ )
3570
+
3571
+ if node.op in ("&&", "||", "==", "!=", "<", "<=", ">", ">="):
3572
+ return int32_t
3573
+
3574
+ if (
3575
+ node.op in ("+", "-")
3576
+ and isinstance(lhs_type, ir.PointerType)
3577
+ and isinstance(rhs_type, ir.IntType)
3578
+ ):
3579
+ return lhs_type
3580
+ if (
3581
+ node.op == "+"
3582
+ and isinstance(rhs_type, ir.PointerType)
3583
+ and isinstance(lhs_type, ir.IntType)
3584
+ ):
3585
+ return rhs_type
3586
+ if (
3587
+ node.op == "-"
3588
+ and isinstance(lhs_type, ir.PointerType)
3589
+ and isinstance(rhs_type, ir.PointerType)
3590
+ ):
3591
+ return int64_t
3592
+
3593
+ if node.op in ("<<", ">>") and isinstance(lhs_type, ir.IntType):
3594
+ return self._integer_promotion_ir_type(lhs_type)
3595
+
3596
+ return self._usual_arithmetic_conversion_ir_type(lhs_type, rhs_type)
3597
+
3598
+ if isinstance(node, c_ast.TernaryOp):
3599
+ true_type = self._infer_sizeof_operand_ir_type(node.iftrue)
3600
+ false_type = self._infer_sizeof_operand_ir_type(node.iffalse)
3601
+ if true_type == false_type:
3602
+ return true_type
3603
+ if (
3604
+ isinstance(true_type, (ir.IntType, ir.FloatType, ir.DoubleType))
3605
+ and isinstance(false_type, (ir.IntType, ir.FloatType, ir.DoubleType))
3606
+ ):
3607
+ return self._usual_arithmetic_conversion_ir_type(
3608
+ true_type, false_type
3609
+ )
3610
+ if isinstance(true_type, ir.PointerType) and isinstance(
3611
+ false_type, ir.PointerType
3612
+ ):
3613
+ return true_type
3614
+ return true_type
3615
+
3616
+ if isinstance(node, c_ast.ExprList) and node.exprs:
3617
+ return self._infer_sizeof_operand_ir_type(node.exprs[-1])
3618
+
3619
+ if isinstance(node, c_ast.FuncCall):
3620
+ if isinstance(node.name, c_ast.ID):
3621
+ ret_type = getattr(self, "func_return_types", {}).get(node.name.name)
3622
+ if ret_type is not None:
3623
+ return ret_type
3624
+
3625
+ raise CodegenError(f"Cannot infer sizeof operand type: {type(node).__name__}")
3626
+
2396
3627
  def codegen_Typename(self, node):
2397
3628
  # Used inside sizeof(type) — not directly code-generated
2398
3629
  return None, None
@@ -2408,6 +3639,8 @@ class LLVMCodeGenerator(object):
2408
3639
  rhs, _ = self.codegen(node.right)
2409
3640
  if lhs is None or rhs is None:
2410
3641
  return ir.Constant(int64_t, 0), None
3642
+ lhs = self._decay_array_expr_to_pointer(node.left, lhs, "lhsarraydecay")
3643
+ rhs = self._decay_array_expr_to_pointer(node.right, rhs, "rhsarraydecay")
2411
3644
 
2412
3645
  # Pointer arithmetic: ptr + int or ptr - int
2413
3646
  if (
@@ -2769,8 +4002,8 @@ class LLVMCodeGenerator(object):
2769
4002
  cond_val = self.builder.ptrtoint(cond_val, int64_t)
2770
4003
  elif self._is_floating_ir_type(cond_val.type):
2771
4004
  cond_val = self.builder.fptosi(cond_val, int64_t)
2772
- elif isinstance(cond_val.type, ir.IntType) and cond_val.type.width != 64:
2773
- cond_val = self._implicit_convert(cond_val, int64_t)
4005
+ elif isinstance(cond_val.type, ir.IntType) and cond_val.type.width < int32_t.width:
4006
+ cond_val = self._integer_promotion(cond_val)
2774
4007
 
2775
4008
  after_bb = self.builder.function.append_basic_block("switch_end")
2776
4009
 
@@ -2783,11 +4016,57 @@ class LLVMCodeGenerator(object):
2783
4016
  switch_items = [node.stmt]
2784
4017
  else:
2785
4018
  switch_items = []
2786
- labels = [
2787
- item
2788
- for item in switch_items
2789
- if isinstance(item, (c_ast.Case, c_ast.Default))
2790
- ]
4019
+ prelabel_items = []
4020
+ hoisted_items = []
4021
+ labels = []
4022
+ label_bodies = {}
4023
+
4024
+ def contains_switch_label(item):
4025
+ if isinstance(item, (c_ast.Case, c_ast.Default)):
4026
+ return True
4027
+ if isinstance(item, c_ast.Compound):
4028
+ return any(
4029
+ contains_switch_label(child) for child in (item.block_items or [])
4030
+ )
4031
+ return False
4032
+
4033
+ def add_label(item):
4034
+ labels.append(item)
4035
+ label_bodies.setdefault(id(item), [])
4036
+
4037
+ def process_items(items, active_label):
4038
+ active = active_label
4039
+ items = list(items or [])
4040
+ for idx, item in enumerate(items):
4041
+ later_has_label = any(
4042
+ contains_switch_label(later) for later in items[idx + 1 :]
4043
+ )
4044
+ if isinstance(item, (c_ast.Case, c_ast.Default)):
4045
+ add_label(item)
4046
+ active = process_items(item.stmts or [], item)
4047
+ continue
4048
+ if isinstance(item, c_ast.Compound) and contains_switch_label(item):
4049
+ active = process_items(item.block_items or [], active)
4050
+ continue
4051
+ if active is None:
4052
+ prelabel_items.append(item)
4053
+ continue
4054
+ if isinstance(item, c_ast.Decl) and later_has_label:
4055
+ if item.init is not None:
4056
+ raise CodegenError(
4057
+ "switch-scope declaration before later case with initializer is not supported"
4058
+ )
4059
+ hoisted_items.append(item)
4060
+ continue
4061
+ if later_has_label and isinstance(
4062
+ item, (c_ast.Typedef, c_ast.EmptyStatement)
4063
+ ):
4064
+ hoisted_items.append(item)
4065
+ continue
4066
+ label_bodies[id(active)].append(item)
4067
+ return active
4068
+
4069
+ process_items(switch_items, None)
2791
4070
 
2792
4071
  label_blocks = {}
2793
4072
  default_bb = after_bb
@@ -2800,11 +4079,21 @@ class LLVMCodeGenerator(object):
2800
4079
  if isinstance(item, c_ast.Default):
2801
4080
  default_bb = bb
2802
4081
 
2803
- switch_inst = self.builder.switch(cond_val, default_bb)
2804
-
2805
4082
  with self.new_scope():
2806
4083
  self.define("break", after_bb)
2807
4084
 
4085
+ for item in prelabel_items + hoisted_items:
4086
+ if isinstance(item, c_ast.Decl):
4087
+ if item.init is not None:
4088
+ raise CodegenError(
4089
+ "switch-scope declaration before first case with initializer is not supported"
4090
+ )
4091
+ self.codegen(item)
4092
+ elif isinstance(item, (c_ast.Typedef, c_ast.EmptyStatement)):
4093
+ self.codegen(item)
4094
+
4095
+ switch_inst = self.builder.switch(cond_val, default_bb)
4096
+
2808
4097
  for item in labels:
2809
4098
  if not isinstance(item, c_ast.Case):
2810
4099
  continue
@@ -2825,10 +4114,12 @@ class LLVMCodeGenerator(object):
2825
4114
 
2826
4115
  for idx, item in enumerate(labels):
2827
4116
  self.builder.position_at_end(label_blocks[id(item)])
2828
- for stmt in item.stmts or []:
2829
- self.codegen(stmt)
4117
+ for stmt in label_bodies.get(id(item), []):
2830
4118
  if self.builder.block.is_terminated:
2831
- break
4119
+ if isinstance(stmt, c_ast.Label):
4120
+ self.codegen(stmt)
4121
+ continue
4122
+ self.codegen(stmt)
2832
4123
  if not self.builder.block.is_terminated:
2833
4124
  next_bb = after_bb
2834
4125
  if idx + 1 < len(labels):
@@ -2850,7 +4141,10 @@ class LLVMCodeGenerator(object):
2850
4141
  self.builder.cbranch(cmp, then_bb, else_bb)
2851
4142
 
2852
4143
  self.builder.position_at_end(then_bb)
2853
- true_val, _ = self.codegen(node.iftrue)
4144
+ if node.iftrue is None:
4145
+ true_val = cond_val
4146
+ else:
4147
+ true_val, _ = self.codegen(node.iftrue)
2854
4148
  true_bb_end = self.builder.block
2855
4149
 
2856
4150
  self.builder.position_at_end(else_bb)
@@ -2858,11 +4152,7 @@ class LLVMCodeGenerator(object):
2858
4152
  false_bb_end = self.builder.block
2859
4153
 
2860
4154
  def zero_value(target_type):
2861
- if isinstance(target_type, ir.PointerType):
2862
- return ir.Constant(target_type, None)
2863
- if self._is_floating_ir_type(target_type):
2864
- return ir.Constant(target_type, 0.0)
2865
- return ir.Constant(target_type, 0)
4155
+ return self._zero_initializer(target_type)
2866
4156
 
2867
4157
  def pick_target_type(lhs, rhs):
2868
4158
  if lhs is None and rhs is None:
@@ -2921,19 +4211,48 @@ class LLVMCodeGenerator(object):
2921
4211
  self.builder.position_at_end(merge_bb)
2922
4212
  if not incoming:
2923
4213
  return zero_value(target), None
4214
+ result_is_unsigned = isinstance(target, ir.IntType) and any(
4215
+ self._is_unsigned_val(value) for _pred, value in incoming
4216
+ )
4217
+ result_has_unsigned_pointee = isinstance(target, ir.PointerType) and any(
4218
+ self._is_unsigned_pointee(value) for _pred, value in incoming
4219
+ )
4220
+ result_has_unsigned_return = isinstance(target, ir.PointerType) and any(
4221
+ self._is_unsigned_return(value) for _pred, value in incoming
4222
+ )
2924
4223
  if len(incoming) == 1:
2925
- return incoming[0][1], None
4224
+ result = incoming[0][1]
4225
+ if result_is_unsigned:
4226
+ self._tag_unsigned(result)
4227
+ if result_has_unsigned_pointee:
4228
+ self._tag_unsigned_pointee(result)
4229
+ if result_has_unsigned_return:
4230
+ self._tag_unsigned_return(result)
4231
+ return result, None
2926
4232
 
2927
4233
  phi = self.builder.phi(target, "ternary")
2928
4234
  for pred, value in incoming:
2929
4235
  phi.add_incoming(value, pred)
4236
+ if result_is_unsigned:
4237
+ self._tag_unsigned(phi)
4238
+ if result_has_unsigned_pointee:
4239
+ self._tag_unsigned_pointee(phi)
4240
+ if result_has_unsigned_return:
4241
+ self._tag_unsigned_return(phi)
2930
4242
  return phi, None
2931
4243
 
2932
4244
  def codegen_Cast(self, node):
4245
+ dest_ir_type = self._resolve_ast_type(node.to_type.type)
4246
+ if isinstance(node.expr, c_ast.InitList) and (
4247
+ isinstance(dest_ir_type, ir.ArrayType)
4248
+ or _is_struct_ir_type(dest_ir_type)
4249
+ or getattr(dest_ir_type, "is_union", False)
4250
+ ):
4251
+ return self._materialize_compound_literal(node.to_type.type, node.expr)
2933
4252
 
2934
4253
  expr, ptr = self.codegen(node.expr)
2935
4254
 
2936
- dest_ir_type = self._resolve_ast_type(node.to_type.type)
4255
+ self._validate_explicit_cast(expr.type, dest_ir_type)
2937
4256
  # Check if casting to unsigned type
2938
4257
  is_unsigned = False
2939
4258
  if isinstance(node.to_type.type, c_ast.TypeDecl) and isinstance(
@@ -2982,6 +4301,9 @@ class LLVMCodeGenerator(object):
2982
4301
  self._tag_value_from_decl_type(result, node.to_type.type)
2983
4302
  return result, None
2984
4303
 
4304
+ def codegen_CompoundLiteral(self, node):
4305
+ return self._materialize_compound_literal(node.type.type, node.init)
4306
+
2985
4307
  def codegen_FuncCall(self, node):
2986
4308
 
2987
4309
  callee = None
@@ -2993,13 +4315,111 @@ class LLVMCodeGenerator(object):
2993
4315
  return self._codegen_builtin_va_end(node)
2994
4316
  if callee == "__builtin_va_copy":
2995
4317
  return self._codegen_builtin_va_copy(node)
4318
+ if callee == "__builtin_alloca":
4319
+ return self._codegen_builtin_alloca(node)
2996
4320
  if callee == "__builtin_va_arg":
2997
4321
  return ir.Constant(voidptr_t, None), None
4322
+ if callee == "__builtin_expect":
4323
+ return self._codegen_builtin_expect(node)
4324
+ if callee == "__builtin_assume":
4325
+ return ir.Constant(int64_t, 0), None
4326
+ if callee == "__builtin_prefetch":
4327
+ return ir.Constant(int64_t, 0), None
4328
+ if callee == "__builtin_unreachable":
4329
+ return self._codegen_builtin_unreachable(node)
4330
+ if callee == "__builtin_add_overflow":
4331
+ return self._codegen_builtin_overflow(node, "add")
4332
+ if callee == "__builtin_sub_overflow":
4333
+ return self._codegen_builtin_overflow(node, "sub")
4334
+ if callee == "__builtin_mul_overflow":
4335
+ return self._codegen_builtin_overflow(node, "mul")
4336
+ if callee == "__builtin_bswap16":
4337
+ return self._codegen_builtin_bswap(node, 16)
4338
+ if callee == "__builtin_bswap32":
4339
+ return self._codegen_builtin_bswap(node, 32)
4340
+ if callee == "__builtin_bswap64":
4341
+ return self._codegen_builtin_bswap(node, 64)
4342
+ if callee == "__builtin_rotateleft32":
4343
+ return self._codegen_builtin_rotate(node, 32, "left")
4344
+ if callee == "__builtin_rotateleft64":
4345
+ return self._codegen_builtin_rotate(node, 64, "left")
4346
+ if callee == "__builtin_rotateright32":
4347
+ return self._codegen_builtin_rotate(node, 32, "right")
4348
+ if callee == "__builtin_rotateright64":
4349
+ return self._codegen_builtin_rotate(node, 64, "right")
4350
+ if callee == "__builtin_clz":
4351
+ return self._codegen_builtin_bitcount(node, 32, "ctlz")
4352
+ if callee == "__builtin_clzll":
4353
+ return self._codegen_builtin_bitcount(node, 64, "ctlz")
4354
+ if callee == "__builtin_ctz":
4355
+ return self._codegen_builtin_bitcount(node, 32, "cttz")
4356
+ if callee == "__builtin_ctzll":
4357
+ return self._codegen_builtin_bitcount(node, 64, "cttz")
4358
+ if callee == "__builtin_ffs":
4359
+ return self._codegen_builtin_ffs(node, 32)
4360
+ if callee == "__builtin_ffsll":
4361
+ return self._codegen_builtin_ffs(node, 64)
4362
+ if callee == "__builtin_frame_address":
4363
+ return self._codegen_builtin_frame_address(node)
4364
+ if callee == "__builtin_memcmp":
4365
+ callee = "memcmp"
4366
+ if callee == "__builtin_memchr":
4367
+ callee = "memchr"
4368
+ if callee == "__builtin_inf":
4369
+ return ir.Constant(_double, float("inf")), None
4370
+ if callee == "__builtin_inff":
4371
+ return ir.Constant(_float, float("inf")), None
4372
+ if callee == "__builtin_infl":
4373
+ return ir.Constant(_double, float("inf")), None
4374
+ if callee == "__builtin_nan":
4375
+ return ir.Constant(_double, float("nan")), None
4376
+ if callee == "__builtin_nanf":
4377
+ return ir.Constant(_float, float("nan")), None
4378
+ if callee == "__builtin_nanl":
4379
+ return ir.Constant(_double, float("nan")), None
4380
+ if callee in ("__builtin_isnan", "__builtin_isnanf", "__builtin_isnanl"):
4381
+ return self._codegen_builtin_isnan(node)
4382
+ if callee in ("__builtin_isfinite", "__builtin_finite"):
4383
+ return self._codegen_builtin_isfinite(node)
4384
+ if callee in ("__builtin_isinf", "__builtin_isinff", "__builtin_isinfl"):
4385
+ return self._codegen_builtin_isinf(node)
4386
+ if callee == "__builtin_signbit":
4387
+ return self._codegen_builtin_signbit(node)
4388
+ if callee == "__builtin_isunordered":
4389
+ return self._codegen_builtin_isunordered(node)
4390
+ if callee == "__builtin_isless":
4391
+ return self._codegen_builtin_ordered_compare(node, "<", "isless")
4392
+ if callee == "__builtin_islessequal":
4393
+ return self._codegen_builtin_ordered_compare(
4394
+ node, "<=", "islessequal"
4395
+ )
4396
+ if callee == "__builtin_isgreater":
4397
+ return self._codegen_builtin_ordered_compare(node, ">", "isgreater")
4398
+ if callee == "__builtin_isgreaterequal":
4399
+ return self._codegen_builtin_ordered_compare(
4400
+ node, ">=", "isgreaterequal"
4401
+ )
4402
+ if callee == "__builtin_islessgreater":
4403
+ return self._codegen_builtin_islessgreater(node)
4404
+ if callee == "__builtin_copysign":
4405
+ return self._codegen_builtin_copysign(node, _double)
4406
+ if callee == "__builtin_copysignf":
4407
+ return self._codegen_builtin_copysign(node, _float)
4408
+ if callee == "__builtin_copysignl":
4409
+ return self._codegen_builtin_copysign(node, _double)
4410
+ if callee == "__sync_synchronize":
4411
+ return self._codegen_builtin_sync_synchronize(node)
4412
+ if callee == "__atomic_load_n":
4413
+ return self._codegen_builtin_atomic_load(node)
4414
+ if callee == "__atomic_store_n":
4415
+ return self._codegen_builtin_atomic_store(node)
2998
4416
  else:
2999
4417
  # Calling function pointer in struct: s.fn(args)
3000
4418
  call_args = []
4419
+ arg_nodes = []
3001
4420
  if node.args:
3002
- call_args = [self.codegen(arg)[0] for arg in node.args.exprs]
4421
+ arg_nodes = list(node.args.exprs)
4422
+ call_args = [self.codegen(arg)[0] for arg in arg_nodes]
3003
4423
  fp_val, _ = self.codegen(node.name)
3004
4424
  if isinstance(fp_val.type, ir.PointerType) and isinstance(
3005
4425
  fp_val.type.pointee, ir.FunctionType
@@ -3008,10 +4428,15 @@ class LLVMCodeGenerator(object):
3008
4428
  ftype = fp_val.type.pointee
3009
4429
  coerced = []
3010
4430
  for j, a in enumerate(call_args):
4431
+ arg_node = arg_nodes[j] if j < len(arg_nodes) else None
3011
4432
  if j < len(ftype.args):
3012
- coerced.append(self._coerce_arg(a, ftype.args[j]))
4433
+ coerced.append(
4434
+ self._coerce_arg(a, ftype.args[j], arg_node=arg_node)
4435
+ )
3013
4436
  else:
3014
- coerced.append(a)
4437
+ coerced.append(
4438
+ self._default_arg_promotion(a, arg_node=arg_node)
4439
+ )
3015
4440
  call_args = coerced
3016
4441
  ret_type = ftype.return_type
3017
4442
  if isinstance(ret_type, ir.VoidType):
@@ -3030,8 +4455,10 @@ class LLVMCodeGenerator(object):
3030
4455
  _, callee_func = self.lookup(callee)
3031
4456
 
3032
4457
  call_args = []
4458
+ arg_nodes = []
3033
4459
  if node.args:
3034
- call_args = [self.codegen(arg)[0] for arg in node.args.exprs]
4460
+ arg_nodes = list(node.args.exprs)
4461
+ call_args = [self.codegen(arg)[0] for arg in arg_nodes]
3035
4462
 
3036
4463
  # Function pointer: load the pointer and call through it
3037
4464
  if not isinstance(callee_func, ir.Function):
@@ -3049,7 +4476,16 @@ class LLVMCodeGenerator(object):
3049
4476
  ):
3050
4477
  ftype = func_val.type.pointee
3051
4478
  coerced = [
3052
- self._coerce_arg(a, ftype.args[j]) if j < len(ftype.args) else a
4479
+ self._coerce_arg(
4480
+ a,
4481
+ ftype.args[j],
4482
+ arg_node=arg_nodes[j] if j < len(arg_nodes) else None,
4483
+ )
4484
+ if j < len(ftype.args)
4485
+ else self._default_arg_promotion(
4486
+ a,
4487
+ arg_node=arg_nodes[j] if j < len(arg_nodes) else None,
4488
+ )
3053
4489
  for j, a in enumerate(call_args)
3054
4490
  ]
3055
4491
  ret_type = ftype.return_type
@@ -3070,7 +4506,9 @@ class LLVMCodeGenerator(object):
3070
4506
  return ir.Constant(int64_t, 0), None
3071
4507
 
3072
4508
  # Convert arguments to match function parameter types
3073
- converted = self._convert_call_args(call_args, callee_func)
4509
+ converted = self._convert_call_args(
4510
+ call_args, callee_func, arg_nodes=arg_nodes
4511
+ )
3074
4512
 
3075
4513
  # Call and handle return type
3076
4514
  try:
@@ -3100,11 +4538,18 @@ class LLVMCodeGenerator(object):
3100
4538
  return existing
3101
4539
  return ir.Function(self.module, ir.FunctionType(ret_type, arg_types), name=name)
3102
4540
 
4541
+ def _builtin_va_list_storage(self, expr):
4542
+ value, addr = self.codegen(expr)
4543
+ storage = addr if addr is not None else value
4544
+ if not isinstance(getattr(storage, "type", None), ir.PointerType):
4545
+ return None
4546
+ return storage
4547
+
3103
4548
  def _codegen_builtin_va_start(self, node):
3104
4549
  if not node.args or not node.args.exprs:
3105
4550
  return ir.Constant(int64_t, 0), None
3106
- ap_addr, _ = self.codegen(node.args.exprs[0])
3107
- if not isinstance(getattr(ap_addr, "type", None), ir.PointerType):
4551
+ ap_addr = self._builtin_va_list_storage(node.args.exprs[0])
4552
+ if ap_addr is None:
3108
4553
  return ir.Constant(int64_t, 0), None
3109
4554
  intrinsic = self._get_or_declare_intrinsic(
3110
4555
  "llvm.va_start", ir.VoidType(), [voidptr_t]
@@ -3118,8 +4563,8 @@ class LLVMCodeGenerator(object):
3118
4563
  def _codegen_builtin_va_end(self, node):
3119
4564
  if not node.args or not node.args.exprs:
3120
4565
  return ir.Constant(int64_t, 0), None
3121
- ap_addr, _ = self.codegen(node.args.exprs[0])
3122
- if not isinstance(getattr(ap_addr, "type", None), ir.PointerType):
4566
+ ap_addr = self._builtin_va_list_storage(node.args.exprs[0])
4567
+ if ap_addr is None:
3123
4568
  return ir.Constant(int64_t, 0), None
3124
4569
  intrinsic = self._get_or_declare_intrinsic(
3125
4570
  "llvm.va_end", ir.VoidType(), [voidptr_t]
@@ -3133,11 +4578,11 @@ class LLVMCodeGenerator(object):
3133
4578
  def _codegen_builtin_va_copy(self, node):
3134
4579
  if not node.args or len(node.args.exprs) < 2:
3135
4580
  return ir.Constant(int64_t, 0), None
3136
- dst_addr, _ = self.codegen(node.args.exprs[0])
3137
- src_addr, _ = self.codegen(node.args.exprs[1])
3138
- if not isinstance(getattr(dst_addr, "type", None), ir.PointerType):
4581
+ dst_addr = self._builtin_va_list_storage(node.args.exprs[0])
4582
+ src_addr = self._builtin_va_list_storage(node.args.exprs[1])
4583
+ if dst_addr is None:
3139
4584
  return ir.Constant(int64_t, 0), None
3140
- if not isinstance(getattr(src_addr, "type", None), ir.PointerType):
4585
+ if src_addr is None:
3141
4586
  return ir.Constant(int64_t, 0), None
3142
4587
  src_val = self._safe_load(src_addr)
3143
4588
  dst_pointee = dst_addr.type.pointee
@@ -3146,24 +4591,429 @@ class LLVMCodeGenerator(object):
3146
4591
  self._safe_store(src_val, dst_addr)
3147
4592
  return ir.Constant(int64_t, 0), None
3148
4593
 
3149
- def _convert_call_args(self, call_args, callee_func):
4594
+ def _codegen_builtin_expect(self, node):
4595
+ if not node.args or not node.args.exprs:
4596
+ return ir.Constant(int64_t, 0), None
4597
+ value, _ = self.codegen(node.args.exprs[0])
4598
+ return value, None
4599
+
4600
+ def _codegen_builtin_unreachable(self, node):
4601
+ if self.builder is not None and not self.builder.block.is_terminated:
4602
+ self.builder.unreachable()
4603
+ dead_bb = self.function.append_basic_block(name="after_unreachable")
4604
+ self.builder.position_at_end(dead_bb)
4605
+ return ir.Constant(int64_t, 0), None
4606
+
4607
+ def _codegen_builtin_alloca(self, node):
4608
+ if not node.args or not node.args.exprs:
4609
+ return ir.Constant(voidptr_t, None), None
4610
+
4611
+ size_val, _ = self.codegen(node.args.exprs[0])
4612
+ if not isinstance(getattr(size_val, "type", None), ir.IntType):
4613
+ size_val = self._implicit_convert(size_val, int64_t)
4614
+
4615
+ return self.builder.alloca(int8_t, size=size_val, name="builtinalloca"), None
4616
+
4617
+ def _codegen_builtin_frame_address(self, node):
4618
+ if not node.args or not node.args.exprs:
4619
+ return ir.Constant(voidptr_t, None), None
4620
+ try:
4621
+ level = int(self._eval_const_expr(node.args.exprs[0]))
4622
+ except Exception:
4623
+ level = 0
4624
+ if level != 0 or self.builder is None:
4625
+ return ir.Constant(voidptr_t, None), None
4626
+ if self._frame_address_marker is None:
4627
+ self._frame_address_marker = self._alloca_in_entry(
4628
+ int8_t, "__builtin_frame_address"
4629
+ )
4630
+ if self._frame_address_marker.type == voidptr_t:
4631
+ return self._frame_address_marker, None
4632
+ return self.builder.bitcast(
4633
+ self._frame_address_marker, voidptr_t, name="frameaddrcast"
4634
+ ), None
4635
+
4636
+ def _codegen_builtin_ffs(self, node, width):
4637
+ if not node.args or not node.args.exprs:
4638
+ return ir.Constant(int32_t, 0), None
4639
+
4640
+ arg, _ = self.codegen(node.args.exprs[0])
4641
+ arg_type = ir.IntType(width)
4642
+ if not isinstance(getattr(arg, "type", None), ir.IntType) or arg.type != arg_type:
4643
+ arg = self._implicit_convert(arg, arg_type)
4644
+
4645
+ zero = ir.Constant(arg_type, 0)
4646
+ is_zero = self.builder.icmp_unsigned("==", arg, zero, name="ffsiszero")
4647
+ intrinsic = self._get_or_declare_intrinsic(
4648
+ f"llvm.cttz.i{width}",
4649
+ arg_type,
4650
+ [arg_type, ir.IntType(1)],
4651
+ )
4652
+ cttz = self.builder.call(
4653
+ intrinsic,
4654
+ [arg, ir.Constant(ir.IntType(1), 0)],
4655
+ name="ffstmp",
4656
+ )
4657
+ if cttz.type != int32_t:
4658
+ cttz = self.builder.trunc(cttz, int32_t, name="ffsi32")
4659
+ plus_one = self.builder.add(cttz, ir.Constant(int32_t, 1), name="ffsplusone")
4660
+ result = self.builder.select(is_zero, ir.Constant(int32_t, 0), plus_one)
4661
+ self._clear_unsigned(result)
4662
+ return result, None
4663
+
4664
+ def _coerce_builtin_float_arg(self, expr, target_type=None):
4665
+ value, _ = self.codegen(expr)
4666
+ if target_type is None:
4667
+ if isinstance(getattr(value, "type", None), ir.FloatType):
4668
+ target_type = _float
4669
+ else:
4670
+ target_type = _double
4671
+ if not self._is_floating_ir_type(getattr(value, "type", None)):
4672
+ value = self._implicit_convert(value, target_type)
4673
+ elif value.type != target_type:
4674
+ value = self._implicit_convert(value, target_type)
4675
+ return value
4676
+
4677
+ def _materialize_compound_literal(self, ast_type, init_node):
4678
+ dest_ir_type = self._resolve_ast_type(ast_type)
4679
+ if self.builder is None:
4680
+ return self._build_const_init(init_node, dest_ir_type), None
4681
+ tmp_ptr = self._alloca_in_entry(dest_ir_type, "compoundlit")
4682
+ self._safe_store(self._zero_initializer(dest_ir_type), tmp_ptr)
4683
+ self._init_runtime_value(tmp_ptr, dest_ir_type, init_node)
4684
+ value = self._safe_load(tmp_ptr, name="compoundlitval")
4685
+ self._tag_value_from_decl_type(value, ast_type)
4686
+ return value, tmp_ptr
4687
+
4688
+ def _builtin_bool_to_i32(self, value, name):
4689
+ return self.builder.zext(value, int32_t, name=name), None
4690
+
4691
+ def _codegen_builtin_isnan(self, node):
4692
+ if not node.args or not node.args.exprs:
4693
+ return ir.Constant(int32_t, 0), None
4694
+ value = self._coerce_builtin_float_arg(node.args.exprs[0])
4695
+ result = self.builder.fcmp_unordered("!=", value, value, name="isnan")
4696
+ return self._builtin_bool_to_i32(result, "isnani32")
4697
+
4698
+ def _codegen_builtin_isinf(self, node):
4699
+ if not node.args or not node.args.exprs:
4700
+ return ir.Constant(int32_t, 0), None
4701
+ value = self._coerce_builtin_float_arg(node.args.exprs[0])
4702
+ pos_inf = ir.Constant(value.type, float("inf"))
4703
+ neg_inf = ir.Constant(value.type, float("-inf"))
4704
+ is_pos = self.builder.fcmp_ordered("==", value, pos_inf, name="isinfpos")
4705
+ is_neg = self.builder.fcmp_ordered("==", value, neg_inf, name="isinfneg")
4706
+ return self._builtin_bool_to_i32(
4707
+ self.builder.or_(is_pos, is_neg, name="isinf"),
4708
+ "isinfi32",
4709
+ )
4710
+
4711
+ def _codegen_builtin_isfinite(self, node):
4712
+ if not node.args or not node.args.exprs:
4713
+ return ir.Constant(int32_t, 0), None
4714
+ value = self._coerce_builtin_float_arg(node.args.exprs[0])
4715
+ not_nan = self.builder.fcmp_ordered("==", value, value, name="isfinitenotnan")
4716
+ pos_inf = self.builder.fcmp_ordered(
4717
+ "==", value, ir.Constant(value.type, float("inf")), name="isfiniteposinf"
4718
+ )
4719
+ neg_inf = self.builder.fcmp_ordered(
4720
+ "==", value, ir.Constant(value.type, float("-inf")), name="isfiniteneginf"
4721
+ )
4722
+ is_inf = self.builder.or_(pos_inf, neg_inf, name="isfiniteisinf")
4723
+ result = self.builder.and_(
4724
+ not_nan,
4725
+ self.builder.not_(is_inf, name="isfinite_not_inf"),
4726
+ name="isfinite",
4727
+ )
4728
+ return self._builtin_bool_to_i32(result, "isfinitei32")
4729
+
4730
+ def _codegen_builtin_signbit(self, node):
4731
+ if not node.args or not node.args.exprs:
4732
+ return ir.Constant(int32_t, 0), None
4733
+ value = self._coerce_builtin_float_arg(node.args.exprs[0])
4734
+ if isinstance(value.type, ir.FloatType):
4735
+ int_type = ir.IntType(32)
4736
+ sign_mask = ir.Constant(int_type, 0x80000000)
4737
+ else:
4738
+ int_type = ir.IntType(64)
4739
+ sign_mask = ir.Constant(int_type, 0x8000000000000000)
4740
+ bits = self.builder.bitcast(value, int_type, name="signbitbits")
4741
+ masked = self.builder.and_(bits, sign_mask, name="signbitmask")
4742
+ result = self.builder.icmp_unsigned(
4743
+ "!=", masked, ir.Constant(int_type, 0), name="signbit"
4744
+ )
4745
+ return self._builtin_bool_to_i32(result, "signbiti32")
4746
+
4747
+ def _codegen_builtin_isunordered(self, node):
4748
+ if not node.args or len(node.args.exprs) < 2:
4749
+ return ir.Constant(int32_t, 0), None
4750
+ lhs = self._coerce_builtin_float_arg(node.args.exprs[0])
4751
+ rhs = self._coerce_builtin_float_arg(node.args.exprs[1], lhs.type)
4752
+ lhs_nan = self.builder.fcmp_unordered("!=", lhs, lhs, name="lhsnan")
4753
+ rhs_nan = self.builder.fcmp_unordered("!=", rhs, rhs, name="rhsnan")
4754
+ result = self.builder.or_(lhs_nan, rhs_nan, name="isunord")
4755
+ return self._builtin_bool_to_i32(result, "isunordi32")
4756
+
4757
+ def _codegen_builtin_ordered_compare(self, node, op, name):
4758
+ if not node.args or len(node.args.exprs) < 2:
4759
+ return ir.Constant(int32_t, 0), None
4760
+ lhs = self._coerce_builtin_float_arg(node.args.exprs[0])
4761
+ rhs = self._coerce_builtin_float_arg(node.args.exprs[1], lhs.type)
4762
+ result = self.builder.fcmp_ordered(op, lhs, rhs, name=name)
4763
+ return self._builtin_bool_to_i32(result, f"{name}i32")
4764
+
4765
+ def _codegen_builtin_islessgreater(self, node):
4766
+ if not node.args or len(node.args.exprs) < 2:
4767
+ return ir.Constant(int32_t, 0), None
4768
+ lhs = self._coerce_builtin_float_arg(node.args.exprs[0])
4769
+ rhs = self._coerce_builtin_float_arg(node.args.exprs[1], lhs.type)
4770
+ result = self.builder.fcmp_ordered("!=", lhs, rhs, name="islessgreater")
4771
+ return self._builtin_bool_to_i32(result, "islessgreateri32")
4772
+
4773
+ def _codegen_builtin_copysign(self, node, target_type):
4774
+ if not node.args or len(node.args.exprs) < 2:
4775
+ return ir.Constant(target_type, 0.0), None
4776
+ magnitude = self._coerce_builtin_float_arg(node.args.exprs[0], target_type)
4777
+ sign = self._coerce_builtin_float_arg(node.args.exprs[1], target_type)
4778
+
4779
+ if isinstance(target_type, ir.FloatType):
4780
+ int_type = ir.IntType(32)
4781
+ sign_mask = ir.Constant(int_type, 0x80000000)
4782
+ value_mask = ir.Constant(int_type, 0x7FFFFFFF)
4783
+ else:
4784
+ int_type = ir.IntType(64)
4785
+ sign_mask = ir.Constant(int_type, 0x8000000000000000)
4786
+ value_mask = ir.Constant(int_type, 0x7FFFFFFFFFFFFFFF)
4787
+
4788
+ magnitude_bits = self.builder.bitcast(
4789
+ magnitude, int_type, name="copysignmagbits"
4790
+ )
4791
+ sign_bits = self.builder.bitcast(sign, int_type, name="copysignsignbits")
4792
+ magnitude_bits = self.builder.and_(
4793
+ magnitude_bits, value_mask, name="copysignmag"
4794
+ )
4795
+ sign_bits = self.builder.and_(sign_bits, sign_mask, name="copysignsign")
4796
+ result_bits = self.builder.or_(
4797
+ magnitude_bits, sign_bits, name="copysignbits"
4798
+ )
4799
+ return self.builder.bitcast(result_bits, target_type, name="copysigntmp"), None
4800
+
4801
+ def _codegen_builtin_overflow(self, node, operation):
4802
+ if not node.args or len(node.args.exprs) < 3:
4803
+ return ir.Constant(int32_t, 0), None
4804
+
4805
+ lhs, _ = self.codegen(node.args.exprs[0])
4806
+ rhs, _ = self.codegen(node.args.exprs[1])
4807
+ out_ptr, _ = self.codegen(node.args.exprs[2])
4808
+
4809
+ if not isinstance(getattr(out_ptr, "type", None), ir.PointerType):
4810
+ return ir.Constant(int32_t, 0), None
4811
+
4812
+ result_type = out_ptr.type.pointee
4813
+ if not isinstance(result_type, ir.IntType):
4814
+ return ir.Constant(int32_t, 0), None
4815
+
4816
+ lhs = self._implicit_convert(lhs, result_type)
4817
+ rhs = self._implicit_convert(rhs, result_type)
4818
+
4819
+ is_unsigned = self._is_unsigned_val(lhs) or self._is_unsigned_val(rhs)
4820
+ if is_unsigned:
4821
+ self._tag_unsigned(lhs)
4822
+ self._tag_unsigned(rhs)
4823
+
4824
+ intrinsic_prefix = {
4825
+ ("add", False): "sadd",
4826
+ ("add", True): "uadd",
4827
+ ("sub", False): "ssub",
4828
+ ("sub", True): "usub",
4829
+ ("mul", False): "smul",
4830
+ ("mul", True): "umul",
4831
+ }[(operation, is_unsigned)]
4832
+ pair_type = ir.LiteralStructType([result_type, ir.IntType(1)])
4833
+ intrinsic = self._get_or_declare_intrinsic(
4834
+ f"llvm.{intrinsic_prefix}.with.overflow.i{result_type.width}",
4835
+ pair_type,
4836
+ [result_type, result_type],
4837
+ )
4838
+ pair = self.builder.call(intrinsic, [lhs, rhs], name=f"{operation}ovtmp")
4839
+ result = self.builder.extract_value(pair, 0, name=f"{operation}ovval")
4840
+ overflow = self.builder.extract_value(pair, 1, name=f"{operation}ovflag")
4841
+ if is_unsigned:
4842
+ self._tag_unsigned(result)
4843
+ self._safe_store(result, out_ptr)
4844
+
4845
+ overflow_i32 = self.builder.zext(overflow, int32_t, name=f"{operation}ovi32")
4846
+ self._clear_unsigned(overflow_i32)
4847
+ return overflow_i32, None
4848
+
4849
+ def _codegen_builtin_sync_synchronize(self, node):
4850
+ self.builder.fence("seq_cst")
4851
+ return ir.Constant(int64_t, 0), None
4852
+
4853
+ def _codegen_builtin_bswap(self, node, width):
4854
+ if not node.args or not node.args.exprs:
4855
+ return ir.Constant(ir.IntType(width), 0), None
4856
+
4857
+ arg, _ = self.codegen(node.args.exprs[0])
4858
+ arg_type = ir.IntType(width)
4859
+ returns_unsigned = self._is_unsigned_val(arg)
4860
+
4861
+ if not isinstance(getattr(arg, "type", None), ir.IntType) or arg.type != arg_type:
4862
+ arg = self._implicit_convert(arg, arg_type)
4863
+
4864
+ mask = ir.Constant(arg_type, 0xFF)
4865
+ result = ir.Constant(arg_type, 0)
4866
+ byte_count = width // 8
4867
+
4868
+ for index in range(byte_count):
4869
+ piece = arg
4870
+ if index:
4871
+ piece = self.builder.lshr(
4872
+ piece,
4873
+ ir.Constant(arg_type, index * 8),
4874
+ name=f"bswapshr{index}",
4875
+ )
4876
+ piece = self.builder.and_(piece, mask, name=f"bswapmask{index}")
4877
+ shift = (byte_count - 1 - index) * 8
4878
+ if shift:
4879
+ piece = self.builder.shl(
4880
+ piece,
4881
+ ir.Constant(arg_type, shift),
4882
+ name=f"bswapshl{index}",
4883
+ )
4884
+ result = self.builder.or_(result, piece, name=f"bswapor{index}")
4885
+
4886
+ return self._extend_call_result(result, returns_unsigned=returns_unsigned), None
4887
+
4888
+ def _codegen_builtin_rotate(self, node, width, direction):
4889
+ if not node.args or len(node.args.exprs) < 2:
4890
+ return ir.Constant(ir.IntType(width), 0), None
4891
+
4892
+ value, _ = self.codegen(node.args.exprs[0])
4893
+ amount, _ = self.codegen(node.args.exprs[1])
4894
+ value_type = ir.IntType(width)
4895
+ returns_unsigned = self._is_unsigned_val(value)
4896
+
4897
+ if (
4898
+ not isinstance(getattr(value, "type", None), ir.IntType)
4899
+ or value.type != value_type
4900
+ ):
4901
+ value = self._implicit_convert(value, value_type)
4902
+ if (
4903
+ not isinstance(getattr(amount, "type", None), ir.IntType)
4904
+ or amount.type != value_type
4905
+ ):
4906
+ amount = self._implicit_convert(amount, value_type)
4907
+
4908
+ mask = ir.Constant(value_type, width - 1)
4909
+ amount = self.builder.and_(amount, mask, name=f"rot{direction}amt")
4910
+ inverse = self.builder.sub(
4911
+ ir.Constant(value_type, 0), amount, name=f"rot{direction}invtmp"
4912
+ )
4913
+ inverse = self.builder.and_(inverse, mask, name=f"rot{direction}inv")
4914
+
4915
+ if direction == "left":
4916
+ lhs = self.builder.shl(value, amount, name=f"rot{direction}lhs")
4917
+ rhs = self.builder.lshr(value, inverse, name=f"rot{direction}rhs")
4918
+ else:
4919
+ lhs = self.builder.lshr(value, amount, name=f"rot{direction}lhs")
4920
+ rhs = self.builder.shl(value, inverse, name=f"rot{direction}rhs")
4921
+
4922
+ result = self.builder.or_(lhs, rhs, name=f"rot{direction}")
4923
+ return self._extend_call_result(result, returns_unsigned=returns_unsigned), None
4924
+
4925
+ def _codegen_builtin_bitcount(self, node, width, intrinsic_base):
4926
+ if not node.args or not node.args.exprs:
4927
+ return ir.Constant(int32_t, 0), None
4928
+
4929
+ arg, _ = self.codegen(node.args.exprs[0])
4930
+ arg_type = ir.IntType(width)
4931
+ if not isinstance(getattr(arg, "type", None), ir.IntType) or arg.type != arg_type:
4932
+ arg = self._implicit_convert(arg, arg_type)
4933
+
4934
+ intrinsic = self._get_or_declare_intrinsic(
4935
+ f"llvm.{intrinsic_base}.i{width}",
4936
+ arg_type,
4937
+ [arg_type, ir.IntType(1)],
4938
+ )
4939
+ result = self.builder.call(
4940
+ intrinsic,
4941
+ [arg, ir.Constant(ir.IntType(1), 0)],
4942
+ name=f"{intrinsic_base}tmp",
4943
+ )
4944
+ if result.type != int32_t:
4945
+ result = self.builder.trunc(result, int32_t, name=f"{intrinsic_base}i32")
4946
+ self._clear_unsigned(result)
4947
+ return result, None
4948
+
4949
+ def _atomic_ordering(self, node, is_store):
4950
+ try:
4951
+ value = self._eval_const_expr(node)
4952
+ except Exception:
4953
+ return "monotonic"
4954
+ if is_store:
4955
+ return {
4956
+ 0: "monotonic", # __ATOMIC_RELAXED
4957
+ 3: "release", # __ATOMIC_RELEASE
4958
+ 4: "release", # __ATOMIC_ACQ_REL
4959
+ 5: "seq_cst", # __ATOMIC_SEQ_CST
4960
+ }.get(value, "monotonic")
4961
+ return {
4962
+ 0: "monotonic", # __ATOMIC_RELAXED
4963
+ 1: "acquire", # __ATOMIC_CONSUME
4964
+ 2: "acquire", # __ATOMIC_ACQUIRE
4965
+ 5: "seq_cst", # __ATOMIC_SEQ_CST
4966
+ }.get(value, "monotonic")
4967
+
4968
+ def _codegen_builtin_atomic_load(self, node):
4969
+ if not node.args or len(node.args.exprs) < 2:
4970
+ return ir.Constant(int64_t, 0), None
4971
+ ptr, _ = self.codegen(node.args.exprs[0])
4972
+ if not isinstance(getattr(ptr, "type", None), ir.PointerType):
4973
+ return ir.Constant(int64_t, 0), None
4974
+ pointee_type = ptr.type.pointee
4975
+ align = max(1, self._ir_type_align(pointee_type))
4976
+ ordering = self._atomic_ordering(node.args.exprs[1], is_store=False)
4977
+ result = self.builder.load_atomic(ptr, ordering, align)
4978
+ if self._is_unsigned_pointee(ptr):
4979
+ self._tag_unsigned(result)
4980
+ return result, ptr
4981
+
4982
+ def _codegen_builtin_atomic_store(self, node):
4983
+ if not node.args or len(node.args.exprs) < 3:
4984
+ return ir.Constant(int64_t, 0), None
4985
+ ptr, _ = self.codegen(node.args.exprs[0])
4986
+ value, _ = self.codegen(node.args.exprs[1])
4987
+ if not isinstance(getattr(ptr, "type", None), ir.PointerType):
4988
+ return ir.Constant(int64_t, 0), None
4989
+ pointee_type = ptr.type.pointee
4990
+ if value.type != pointee_type:
4991
+ value = self._implicit_convert(value, pointee_type)
4992
+ align = max(1, self._ir_type_align(pointee_type))
4993
+ ordering = self._atomic_ordering(node.args.exprs[2], is_store=True)
4994
+ self.builder.store_atomic(value, ptr, ordering, align)
4995
+ return ir.Constant(int64_t, 0), None
4996
+
4997
+ def _convert_call_args(self, call_args, callee_func, arg_nodes=None):
3150
4998
  """Convert call arguments to match function parameter types."""
3151
4999
  converted = []
3152
5000
  param_types = [p.type for p in callee_func.args]
3153
5001
 
3154
5002
  for i, arg in enumerate(call_args):
5003
+ arg_node = arg_nodes[i] if arg_nodes and i < len(arg_nodes) else None
3155
5004
  if i < len(param_types):
3156
5005
  expected = param_types[i]
3157
- arg = self._coerce_arg(arg, expected)
5006
+ arg = self._coerce_arg(arg, expected, arg_node=arg_node)
3158
5007
  else:
3159
- arg = self._default_arg_promotion(arg)
5008
+ arg = self._default_arg_promotion(arg, arg_node=arg_node)
3160
5009
  converted.append(arg)
3161
5010
  return converted
3162
5011
 
3163
- def _default_arg_promotion(self, arg):
5012
+ def _default_arg_promotion(self, arg, arg_node=None):
3164
5013
  """Apply C default argument promotions for variadic calls."""
3165
5014
  if arg is None or isinstance(getattr(arg, "type", None), ir.VoidType):
3166
5015
  return ir.Constant(int64_t, 0)
5016
+ arg = self._decay_array_expr_to_pointer(arg_node, arg, "varargarraydecay")
3167
5017
  if isinstance(arg.type, ir.ArrayType):
3168
5018
  return self._implicit_convert(arg, ir.PointerType(arg.type.element))
3169
5019
  if isinstance(arg.type, ir.FloatType):
@@ -3172,7 +5022,7 @@ class LLVMCodeGenerator(object):
3172
5022
  return self._integer_promotion(arg)
3173
5023
  return arg
3174
5024
 
3175
- def _coerce_arg(self, arg, expected):
5025
+ def _coerce_arg(self, arg, expected, arg_node=None):
3176
5026
  """Coerce a single argument to the expected type."""
3177
5027
  if arg is None or isinstance(getattr(arg, "type", None), ir.VoidType):
3178
5028
  return (
@@ -3180,16 +5030,13 @@ class LLVMCodeGenerator(object):
3180
5030
  if isinstance(expected, ir.PointerType)
3181
5031
  else ir.Constant(int64_t, 0)
3182
5032
  )
5033
+ arg = self._decay_array_expr_to_pointer(arg_node, arg, "argarraydecay")
3183
5034
  if arg.type == expected:
3184
5035
  return arg
3185
- # String literal [N x i8] -> pointer
5036
+ # Array values decay to pointers at the call site; do not try to
5037
+ # synthesize globals from function-local SSA array values here.
3186
5038
  if isinstance(arg.type, ir.ArrayType) and isinstance(expected, ir.PointerType):
3187
- gv = ir.GlobalVariable(
3188
- self.module, arg.type, self.module.get_unique_name("str")
3189
- )
3190
- gv.initializer = arg
3191
- gv.global_constant = True
3192
- return self.builder.bitcast(gv, expected)
5039
+ return self._implicit_convert(arg, expected)
3193
5040
  # Pointer -> different pointer: bitcast
3194
5041
  if isinstance(arg.type, ir.PointerType) and isinstance(
3195
5042
  expected, ir.PointerType
@@ -3212,11 +5059,27 @@ class LLVMCodeGenerator(object):
3212
5059
  ):
3213
5060
  return None, None
3214
5061
 
3215
- # Static local variables: stored as globals with function-scoped names
5062
+ # Standalone tag definitions such as:
5063
+ # struct S { ... };
5064
+ # union U { ... };
5065
+ # enum E { ... };
5066
+ # do not declare objects. Register the aggregate/enum type and stop.
5067
+ if node.name is None and isinstance(node.type, c_ast.TypeDecl):
5068
+ inner = node.type.type
5069
+ if isinstance(inner, c_ast.Struct):
5070
+ self.codegen_Struct(inner)
5071
+ return None, None
5072
+ if isinstance(inner, c_ast.Union):
5073
+ self.codegen_Union(inner)
5074
+ return None, None
5075
+ if isinstance(inner, c_ast.Enum):
5076
+ self.codegen_Enum(inner)
5077
+ return None, None
5078
+
5079
+ # Static local objects: stored as internal globals with function-scoped names
3216
5080
  is_static = node.storage and "static" in node.storage
3217
- if is_static and not self.in_global and isinstance(node.type, c_ast.TypeDecl):
3218
- type_str = node.type.type.names
3219
- ir_type = self._get_ir_type(type_str)
5081
+ if is_static and not self.in_global and not isinstance(node.type, c_ast.FuncDecl):
5082
+ ir_type = self._static_local_ir_type(node.type, init_node=node.init)
3220
5083
  # Create unique global name
3221
5084
  global_name = self._static_local_symbol_name(node.name)
3222
5085
  gv = self._create_bound_global(node.name, ir_type, symbol_name=global_name)
@@ -3228,7 +5091,7 @@ class LLVMCodeGenerator(object):
3228
5091
  return None, None
3229
5092
 
3230
5093
  if self._is_global_extern_decl(node):
3231
- ir_type = self._extern_decl_ir_type(node.type)
5094
+ ir_type = self._extern_decl_ir_type(node.name, node.type)
3232
5095
  self._prepare_file_scope_object(
3233
5096
  node.name,
3234
5097
  ir_type,
@@ -3243,22 +5106,17 @@ class LLVMCodeGenerator(object):
3243
5106
  # Forward function declaration: int foo(int x);
3244
5107
  if isinstance(node.type, c_ast.FuncDecl):
3245
5108
  funcname = node.name
3246
- ir_type, _ = self.codegen(node.type)
3247
- arg_types = []
3248
- is_va = False
3249
- if node.type.args:
3250
- for arg in node.type.args.params:
3251
- if isinstance(arg, c_ast.EllipsisParam):
3252
- is_va = True
3253
- continue
3254
- t = self._resolve_param_type(arg)
3255
- if t is not None:
3256
- arg_types.append(t)
3257
- function_type = ir.FunctionType(ir_type, arg_types, var_arg=is_va)
5109
+ function_type, ir_type = self._build_function_ir_type(node.type)
5110
+ function_type = self._preferred_file_scope_function_ir_type(
5111
+ funcname,
5112
+ function_type,
5113
+ getattr(node.type, "args", None) is not None,
5114
+ )
3258
5115
  symbol_name = self._register_file_scope_function(
3259
5116
  funcname,
3260
5117
  function_type,
3261
5118
  storage=node.storage,
5119
+ funcspec=node.funcspec,
3262
5120
  is_definition=False,
3263
5121
  )
3264
5122
  # Skip if already exists (module globals, libc, or env)
@@ -3268,17 +5126,12 @@ class LLVMCodeGenerator(object):
3268
5126
  self._mark_unsigned_return(existing)
3269
5127
  self.define(funcname, (None, existing))
3270
5128
  return None, None
3271
- if funcname in LIBC_FUNCTIONS:
3272
- self._declare_libc(funcname)
3273
- return None, None
3274
5129
  try:
3275
5130
  func = ir.Function(
3276
5131
  self.module,
3277
5132
  function_type,
3278
5133
  name=symbol_name,
3279
5134
  )
3280
- if self._is_file_scope_static(node.storage):
3281
- func.linkage = "internal"
3282
5135
  if self._func_decl_returns_unsigned(node.type):
3283
5136
  self._mark_unsigned_return(func)
3284
5137
  self.define(funcname, (ir_type, func))
@@ -3305,8 +5158,29 @@ class LLVMCodeGenerator(object):
3305
5158
  if isinstance(node.type.type, c_ast.IdentifierType):
3306
5159
  # Check if the type resolves to a struct or pointer via typedef
3307
5160
  resolved = self._resolve_type_str(node.type.type.names)
3308
- if isinstance(
3309
- resolved, (ir.LiteralStructType, ir.PointerType, ir.ArrayType)
5161
+ if isinstance(resolved, ir.FunctionType):
5162
+ funcname = node.type.declname
5163
+ symbol_name = self._register_file_scope_function(
5164
+ funcname,
5165
+ resolved,
5166
+ storage=node.storage,
5167
+ funcspec=node.funcspec,
5168
+ is_definition=False,
5169
+ )
5170
+ existing = self.module.globals.get(symbol_name)
5171
+ if existing:
5172
+ self.define(funcname, (resolved.return_type, existing))
5173
+ return None, None
5174
+ try:
5175
+ func = ir.Function(self.module, resolved, name=symbol_name)
5176
+ self.define(funcname, (resolved.return_type, func))
5177
+ except Exception:
5178
+ existing = self.module.globals.get(symbol_name)
5179
+ if existing:
5180
+ self.define(funcname, (resolved.return_type, existing))
5181
+ return None, None
5182
+ if isinstance(resolved, (ir.PointerType, ir.ArrayType)) or _is_struct_ir_type(
5183
+ resolved
3310
5184
  ):
3311
5185
  name = node.type.declname
3312
5186
  ir_type = resolved
@@ -3329,11 +5203,20 @@ class LLVMCodeGenerator(object):
3329
5203
  node.init, ir_type
3330
5204
  )
3331
5205
  else:
3332
- init_val, _ = self.codegen(node.init)
3333
- if init_val is not None:
3334
- if init_val.type != ir_type:
3335
- init_val = self._implicit_convert(init_val, ir_type)
3336
- self._safe_store(init_val, ret)
5206
+ if isinstance(node.init, c_ast.InitList) and (
5207
+ getattr(ir_type, "is_union", False)
5208
+ or _is_struct_ir_type(ir_type)
5209
+ ):
5210
+ self._safe_store(self._zero_initializer(ir_type), ret)
5211
+ self._init_runtime_aggregate(ret, node.init, ir_type)
5212
+ else:
5213
+ init_val, _ = self.codegen(node.init)
5214
+ if init_val is not None:
5215
+ if init_val.type != ir_type:
5216
+ init_val = self._implicit_convert(
5217
+ init_val, ir_type
5218
+ )
5219
+ self._safe_store(init_val, ret)
3337
5220
  elif self.in_global and write_initializer:
3338
5221
  ret.initializer = self._zero_initializer(ir_type)
3339
5222
  return None, None
@@ -3345,8 +5228,15 @@ class LLVMCodeGenerator(object):
3345
5228
  if isinstance(node.type.type, c_ast.Union)
3346
5229
  else self.codegen_Struct
3347
5230
  )
3348
- if node.type.type.name is None:
5231
+ if node.type.type.name is None or getattr(node.type.type, "decls", None) is not None:
3349
5232
  struct_type = codegen_fn(node.type.type)
5233
+ if self.in_global and node.name and node.type.type.name is None:
5234
+ # Preserve the repository's legacy behavior for
5235
+ # file-scope anonymous aggregates declared as:
5236
+ # struct { ... } Name;
5237
+ # Existing tests rely on a later `struct Name`
5238
+ # resolving to the same aggregate type.
5239
+ self.define(self._tag_type_key(node.name), (struct_type, None))
3350
5240
  if not self.in_global:
3351
5241
  ret = self._alloca_in_entry(struct_type, name)
3352
5242
  self.define(name, (struct_type, ret))
@@ -3366,18 +5256,28 @@ class LLVMCodeGenerator(object):
3366
5256
  node.init, struct_type
3367
5257
  )
3368
5258
  else:
3369
- init_val, _ = self.codegen(node.init)
3370
- if init_val is not None:
3371
- if init_val.type != struct_type:
3372
- init_val = self._implicit_convert(
3373
- init_val, struct_type
3374
- )
3375
- self._safe_store(init_val, ret)
5259
+ if isinstance(node.init, c_ast.InitList):
5260
+ self._safe_store(
5261
+ self._zero_initializer(struct_type), ret
5262
+ )
5263
+ self._init_runtime_aggregate(
5264
+ ret, node.init, struct_type
5265
+ )
5266
+ else:
5267
+ init_val, _ = self.codegen(node.init)
5268
+ if init_val is not None:
5269
+ if init_val.type != struct_type:
5270
+ init_val = self._implicit_convert(
5271
+ init_val, struct_type
5272
+ )
5273
+ self._safe_store(init_val, ret)
3376
5274
  elif self.in_global and write_initializer:
3377
5275
  ret.initializer = self._zero_initializer(struct_type)
3378
5276
  return None, None
3379
5277
  else:
3380
- struct_type = self.env[node.type.type.name][0]
5278
+ struct_type = self.env[
5279
+ self._tag_type_key(node.type.type.name)
5280
+ ][0]
3381
5281
  if not self.in_global:
3382
5282
  ret = self._alloca_in_entry(struct_type, name)
3383
5283
  self.define(name, (struct_type, ret))
@@ -3397,23 +5297,38 @@ class LLVMCodeGenerator(object):
3397
5297
  node.init, struct_type
3398
5298
  )
3399
5299
  else:
3400
- init_val, _ = self.codegen(node.init)
3401
- if init_val is not None:
3402
- if init_val.type != struct_type:
3403
- init_val = self._implicit_convert(
3404
- init_val, struct_type
3405
- )
3406
- self._safe_store(init_val, ret)
5300
+ if isinstance(node.init, c_ast.InitList):
5301
+ self._safe_store(
5302
+ self._zero_initializer(struct_type), ret
5303
+ )
5304
+ self._init_runtime_aggregate(
5305
+ ret, node.init, struct_type
5306
+ )
5307
+ else:
5308
+ init_val, _ = self.codegen(node.init)
5309
+ if init_val is not None:
5310
+ if init_val.type != struct_type:
5311
+ init_val = self._implicit_convert(
5312
+ init_val, struct_type
5313
+ )
5314
+ self._safe_store(init_val, ret)
3407
5315
  elif self.in_global and write_initializer:
3408
5316
  ret.initializer = self._zero_initializer(struct_type)
3409
5317
  return None, None
3410
5318
  else:
3411
- type_str = node.type.type.names
3412
- is_unsigned = self._is_unsigned_type_names(type_str)
3413
- ir_type = self._get_ir_type(type_str)
3414
- type_str = self._resolve_type_str(type_str)
3415
- if isinstance(type_str, ir.Type):
3416
- type_str = "int" # fallback for alloca name
5319
+ if isinstance(node.type.type, c_ast.IdentifierType):
5320
+ type_str = node.type.type.names
5321
+ is_unsigned = self._is_unsigned_type_names(type_str)
5322
+ ir_type = self._get_ir_type(type_str)
5323
+ type_str = self._resolve_type_str(type_str)
5324
+ if isinstance(type_str, ir.Type):
5325
+ type_str = "int" # fallback for alloca name
5326
+ else:
5327
+ if isinstance(node.type.type, c_ast.Enum):
5328
+ self.codegen_Enum(node.type.type)
5329
+ type_str = "int"
5330
+ is_unsigned = False
5331
+ ir_type = self._resolve_ast_type(node.type)
3417
5332
  if self._is_floating_ir_type(ir_type):
3418
5333
  init = 0.0
3419
5334
  else:
@@ -3423,6 +5338,11 @@ class LLVMCodeGenerator(object):
3423
5338
  if self.in_global:
3424
5339
  init_val = self._build_const_init(node.init, ir_type)
3425
5340
  else:
5341
+ var_addr, var_ir_type = self.create_entry_block_alloca(
5342
+ node.name, type_str, 1, storage=node.storage
5343
+ )
5344
+ if is_unsigned:
5345
+ self._mark_unsigned(var_addr)
3426
5346
  init_val, _ = self.codegen(node.init)
3427
5347
  else:
3428
5348
  init_val = self._zero_initializer(ir_type)
@@ -3439,45 +5359,60 @@ class LLVMCodeGenerator(object):
3439
5359
  if write_initializer:
3440
5360
  var_addr.initializer = init_val
3441
5361
  else:
3442
- var_addr, var_ir_type = self.create_entry_block_alloca(
3443
- node.name, type_str, 1, storage=node.storage
3444
- )
3445
- if is_unsigned:
3446
- self._mark_unsigned(var_addr)
5362
+ if node.init is None:
5363
+ var_addr, var_ir_type = self.create_entry_block_alloca(
5364
+ node.name, type_str, 1, storage=node.storage
5365
+ )
5366
+ if is_unsigned:
5367
+ self._mark_unsigned(var_addr)
3447
5368
  init_val = self._implicit_convert(init_val, ir_type)
3448
5369
  self._safe_store(init_val, var_addr)
3449
5370
  if self.in_global and is_unsigned:
3450
5371
  self._mark_unsigned(var_addr)
3451
5372
 
3452
5373
  elif isinstance(node.type, c_ast.ArrayDecl):
3453
- global_symbol_name = self._file_scope_symbol_name(node.name, node.storage)
3454
5374
  array_list = []
3455
5375
  array_node = node.type
3456
5376
  var_addr = None
3457
5377
  var_ir_type = None
3458
5378
  elem_ir_type = None
3459
5379
  write_initializer = True
5380
+ inferred_top_dim = self._infer_array_count_from_initializer(node.init)
3460
5381
  while True:
3461
5382
  array_next_type = array_node.type
3462
5383
  if isinstance(array_next_type, c_ast.TypeDecl):
3463
5384
  dim_val = self._eval_dim(array_node.dim) if array_node.dim else 0
5385
+ if (
5386
+ dim_val == 0
5387
+ and array_node is node.type
5388
+ and inferred_top_dim is not None
5389
+ ):
5390
+ dim_val = inferred_top_dim
3464
5391
  array_list.append(dim_val)
3465
5392
  elem_ir_type = self._resolve_ast_type(array_next_type)
3466
5393
  break
3467
5394
 
3468
5395
  elif isinstance(array_next_type, c_ast.ArrayDecl):
3469
- array_list.append(self._eval_dim(array_node.dim))
5396
+ dim_val = self._eval_dim(array_node.dim)
5397
+ if (
5398
+ dim_val == 0
5399
+ and array_node is node.type
5400
+ and inferred_top_dim is not None
5401
+ ):
5402
+ dim_val = inferred_top_dim
5403
+ array_list.append(dim_val)
3470
5404
  array_node = array_next_type
3471
5405
  continue
3472
5406
  elif isinstance(array_next_type, c_ast.PtrDecl):
3473
5407
  # Array of pointers: int *arr[3]
3474
5408
  dim = self._eval_dim(array_node.dim)
3475
- inner = array_next_type.type
3476
- if isinstance(inner, c_ast.TypeDecl):
3477
- elem_type_str = inner.type.names
3478
- else:
3479
- elem_type_str = "int"
3480
- elem_ir = ir.PointerType(get_ir_type(elem_type_str))
5409
+ if (
5410
+ dim == 0
5411
+ and array_node is node.type
5412
+ and inferred_top_dim is not None
5413
+ ):
5414
+ dim = inferred_top_dim
5415
+ elem_ir = self._resolve_ast_type(array_next_type)
3481
5416
  elem_ir_type = elem_ir
3482
5417
  arr_ir = ir.ArrayType(elem_ir, dim)
3483
5418
  arr_ir.dim_array = [dim]
@@ -3519,42 +5454,6 @@ class LLVMCodeGenerator(object):
3519
5454
  if self._has_unsigned_scalar_pointee(node.type):
3520
5455
  self._mark_unsigned_pointee(var_addr)
3521
5456
 
3522
- # Infer the size of zero-length arrays from the initializer.
3523
- if (
3524
- isinstance(var_ir_type, ir.ArrayType)
3525
- and var_ir_type.count == 0
3526
- and node.init is not None
3527
- ):
3528
- actual_count = None
3529
- if isinstance(node.init, c_ast.InitList):
3530
- actual_count = len(node.init.exprs)
3531
- elif (
3532
- isinstance(node.init, c_ast.Constant)
3533
- and getattr(node.init, "type", None) == "string"
3534
- ):
3535
- raw = node.init.value[1:-1]
3536
- actual_count = len(self._process_escapes(raw)) + 1
3537
- if actual_count is not None and elem_ir_type is not None:
3538
- var_ir_type = ir.ArrayType(elem_ir_type, actual_count)
3539
- var_ir_type.dim_array = [actual_count]
3540
- if self.in_global:
3541
- new_name = self.module.get_unique_name(global_symbol_name)
3542
- var_addr = self._create_bound_global(
3543
- node.name,
3544
- var_ir_type,
3545
- symbol_name=new_name,
3546
- storage=node.storage,
3547
- )
3548
- self.define(node.name, (var_ir_type, var_addr))
3549
- if not hasattr(self, "_array_renames"):
3550
- self._array_renames = {}
3551
- self._array_renames[
3552
- f'@"{global_symbol_name}"'
3553
- ] = f'@"{new_name}"'
3554
- else:
3555
- var_addr = self._alloca_in_entry(var_ir_type, node.name)
3556
- self.define(node.name, (var_ir_type, var_addr))
3557
-
3558
5457
  if self._has_unsigned_scalar_pointee(node.type):
3559
5458
  self._mark_unsigned_pointee(var_addr)
3560
5459
 
@@ -3570,10 +5469,11 @@ class LLVMCodeGenerator(object):
3570
5469
  except Exception:
3571
5470
  var_addr.initializer = self._zero_initializer(var_ir_type)
3572
5471
  elif isinstance(node.init, c_ast.InitList):
5472
+ self._safe_store(self._zero_initializer(var_ir_type), var_addr)
3573
5473
  self._init_array(
3574
5474
  var_addr,
3575
5475
  node.init,
3576
- elem_ir_type,
5476
+ var_ir_type.element,
3577
5477
  [ir.Constant(ir.IntType(32), 0)],
3578
5478
  )
3579
5479
  elif (
@@ -3582,6 +5482,7 @@ class LLVMCodeGenerator(object):
3582
5482
  and isinstance(elem_ir_type, ir.IntType)
3583
5483
  and elem_ir_type.width == 8
3584
5484
  ):
5485
+ self._safe_store(self._zero_initializer(var_ir_type), var_addr)
3585
5486
  raw = self._process_escapes(node.init.value[1:-1]) + "\00"
3586
5487
  idx0 = ir.Constant(ir.IntType(32), 0)
3587
5488
  for i, ch in enumerate(raw[: var_ir_type.count]):
@@ -3611,12 +5512,16 @@ class LLVMCodeGenerator(object):
3611
5512
  elif isinstance(sub_next_type.type, c_ast.Union):
3612
5513
  resolved_pointee_type = self.codegen_Union(sub_next_type.type)
3613
5514
  type_str = "union"
5515
+ elif isinstance(sub_next_type.type, c_ast.Enum):
5516
+ self.codegen_Enum(sub_next_type.type)
5517
+ resolved_pointee_type = int32_t
5518
+ type_str = "int"
3614
5519
  else:
3615
5520
  type_str = sub_next_type.type.names
3616
5521
  resolved = self._get_ir_type(type_str)
3617
5522
  if isinstance(resolved, ir.Type):
3618
5523
  resolved_pointee_type = resolved
3619
- if isinstance(resolved, ir.LiteralStructType):
5524
+ if _is_struct_ir_type(resolved):
3620
5525
  type_str = "struct"
3621
5526
  break
3622
5527
  elif isinstance(sub_next_type, c_ast.PtrDecl):
@@ -3642,10 +5547,28 @@ class LLVMCodeGenerator(object):
3642
5547
  self._mark_unsigned_return(var_addr)
3643
5548
  if node.init is not None:
3644
5549
  init_val, _ = self.codegen(node.init)
3645
- # init_val is an ir.Function, bitcast to func ptr type
3646
- if init_val.type != func_ir_type:
3647
- init_val = self.builder.bitcast(init_val, func_ir_type)
3648
- self._safe_store(init_val, var_addr)
5550
+ # For global scope, set as initializer directly
5551
+ if self.in_global and isinstance(var_addr, ir.GlobalVariable):
5552
+ # NULL (i64 0) → null pointer of correct type
5553
+ if (isinstance(init_val.type, ir.IntType)
5554
+ and isinstance(init_val, ir.Constant)
5555
+ and init_val.constant == 0):
5556
+ init_val = ir.Constant(
5557
+ var_addr.value_type, None
5558
+ )
5559
+ var_addr.initializer = init_val
5560
+ else:
5561
+ if (
5562
+ isinstance(init_val, ir.Constant)
5563
+ and isinstance(init_val.type, ir.IntType)
5564
+ and init_val.constant == 0
5565
+ ):
5566
+ init_val = ir.Constant(func_ir_type, None)
5567
+ elif init_val.type != func_ir_type:
5568
+ init_val = self._implicit_convert(
5569
+ init_val, func_ir_type
5570
+ )
5571
+ self._safe_store(init_val, var_addr)
3649
5572
  return None, var_addr
3650
5573
  pass
3651
5574
 
@@ -3707,17 +5630,13 @@ class LLVMCodeGenerator(object):
3707
5630
  var_addr.initializer = ir.Constant(var_ir_type, None)
3708
5631
  else:
3709
5632
  init_val, _ = self.codegen(node.init)
5633
+ init_val = self._decay_array_expr_to_pointer(
5634
+ node.init, init_val, f"{node.name}.initdecay"
5635
+ )
3710
5636
  if isinstance(init_val.type, ir.ArrayType) and isinstance(
3711
5637
  var_ir_type, ir.PointerType
3712
5638
  ):
3713
- gv = ir.GlobalVariable(
3714
- self.module,
3715
- init_val.type,
3716
- self.module.get_unique_name("str"),
3717
- )
3718
- gv.initializer = init_val
3719
- gv.global_constant = True
3720
- init_val = self.builder.bitcast(gv, var_ir_type)
5639
+ init_val = self._implicit_convert(init_val, var_ir_type)
3721
5640
  elif init_val.type != var_ir_type:
3722
5641
  init_val = self._implicit_convert(init_val, var_ir_type)
3723
5642
  self._safe_store(init_val, var_addr)
@@ -3727,6 +5646,14 @@ class LLVMCodeGenerator(object):
3727
5646
  return None, var_addr
3728
5647
 
3729
5648
  def codegen_ID(self, node):
5649
+ if node.name in {"__func__", "__FUNCTION__", "__PRETTY_FUNCTION__"}:
5650
+ func_name = self._function_display_name or (
5651
+ self.function.name if self.function is not None else node.name
5652
+ )
5653
+ gv = self._make_global_string_constant(func_name, name_hint="funcname")
5654
+ ptr = self._const_pointer_to_first_elem(gv, cstring)
5655
+ node.ir_type = cstring
5656
+ return ptr, gv
3730
5657
 
3731
5658
  valtype, var = self.lookup(node.name)
3732
5659
  node.ir_type = valtype
@@ -3813,7 +5740,7 @@ class LLVMCodeGenerator(object):
3813
5740
  # Non-array type (opaque struct etc): treat as pointer subscript
3814
5741
  if not isinstance(name_type, ir.ArrayType):
3815
5742
  ptr = (
3816
- self.builder.bitcast(name_ir, ir.PointerType(int8_t))
5743
+ self._implicit_convert(name_ir, ir.PointerType(int8_t))
3817
5744
  if not isinstance(name_ir.type, ir.PointerType)
3818
5745
  else name_ir
3819
5746
  )
@@ -3896,22 +5823,30 @@ class LLVMCodeGenerator(object):
3896
5823
  retval, _ = self.codegen(node.expr)
3897
5824
  # Implicit convert to function return type
3898
5825
  func_ret_type = self.function.return_value.type
5826
+ if isinstance(func_ret_type, ir.VoidType):
5827
+ self.builder.ret_void()
5828
+ return None, None
3899
5829
  if retval.type != func_ret_type:
3900
5830
  retval = self._implicit_convert(retval, func_ret_type)
3901
5831
  self.builder.ret(retval)
3902
5832
  return None, None
3903
5833
 
3904
5834
  def codegen_Compound(self, node):
3905
-
3906
- if node.block_items:
3907
- for stmt in node.block_items:
3908
- if self.builder and self.builder.block.is_terminated:
3909
- # After a terminator (goto/break/continue/return),
3910
- # only process labels — skip unreachable code
3911
- if isinstance(stmt, c_ast.Label):
3912
- self.codegen(stmt)
3913
- continue
3914
- self.codegen(stmt)
5835
+ return self._codegen_compound_items(node, use_new_scope=True)
5836
+
5837
+ def _codegen_compound_items(self, node, use_new_scope):
5838
+ scope = self.new_scope() if use_new_scope else nullcontext()
5839
+
5840
+ with scope:
5841
+ if node.block_items:
5842
+ for stmt in node.block_items:
5843
+ if self.builder and self.builder.block.is_terminated:
5844
+ # After a terminator (goto/break/continue/return),
5845
+ # only process labels — skip unreachable code
5846
+ if isinstance(stmt, c_ast.Label):
5847
+ self.codegen(stmt)
5848
+ continue
5849
+ self.codegen(stmt)
3915
5850
  return None, None
3916
5851
 
3917
5852
  def codegen_FuncDecl(self, node):
@@ -3930,31 +5865,38 @@ class LLVMCodeGenerator(object):
3930
5865
  self.func_return_types = {}
3931
5866
  self.func_return_types[funcname] = ir_type
3932
5867
 
3933
- arg_types = []
3934
- is_var_arg = False
3935
- if node.decl.type.args:
3936
- for arg_type in node.decl.type.args.params:
3937
- if isinstance(arg_type, c_ast.EllipsisParam):
3938
- is_var_arg = True
3939
- continue
3940
- t = self._resolve_param_type(arg_type)
3941
- if t is not None:
3942
- arg_types.append(t)
5868
+ param_infos, is_var_arg = self._funcdef_param_infos(node)
5869
+ arg_types = [param_type for _name, param_type, _decl in param_infos]
3943
5870
 
3944
5871
  function_type = ir.FunctionType(ir_type, arg_types, var_arg=is_var_arg)
5872
+ prior_state = self._file_scope_function_states.get(funcname)
5873
+ if node.param_decls and prior_state is not None:
5874
+ prior_decl = self.module.globals.get(prior_state.symbol_name)
5875
+ if isinstance(prior_decl, ir.Function):
5876
+ prior_type = prior_decl.function_type
5877
+ if self._function_arg_types_match(prior_type.args, arg_types):
5878
+ function_type = prior_type
3945
5879
  symbol_name = self._register_file_scope_function(
3946
5880
  funcname,
3947
5881
  function_type,
3948
5882
  storage=node.decl.storage,
5883
+ funcspec=node.decl.funcspec,
3949
5884
  is_definition=True,
3950
5885
  )
5886
+ function_state = self._file_scope_function_states.get(funcname)
5887
+ needs_internal_linkage = (
5888
+ function_state is not None and function_state.linkage == "internal"
5889
+ )
3951
5890
 
3952
5891
  with self.new_function():
5892
+ self._function_display_name = funcname
3953
5893
 
3954
5894
  existing = self.module.globals.get(symbol_name)
3955
5895
  if existing and isinstance(existing, ir.Function):
3956
5896
  if existing.is_declaration:
3957
5897
  self.function = existing
5898
+ if needs_internal_linkage:
5899
+ self.function.linkage = "internal"
3958
5900
  else:
3959
5901
  raise SemanticError(f"redefinition of function '{funcname}'")
3960
5902
  else:
@@ -3964,7 +5906,7 @@ class LLVMCodeGenerator(object):
3964
5906
  function_type,
3965
5907
  name=symbol_name,
3966
5908
  )
3967
- if self._is_file_scope_static(node.decl.storage):
5909
+ if needs_internal_linkage:
3968
5910
  self.function.linkage = "internal"
3969
5911
  except Exception:
3970
5912
  raise SemanticError(f"failed to define function '{funcname}'")
@@ -3975,53 +5917,38 @@ class LLVMCodeGenerator(object):
3975
5917
  if len(self.env.maps) > 1:
3976
5918
  self.env.maps[1][funcname] = (ir_type, self.function)
3977
5919
  self.define(funcname, (ir_type, self.function))
3978
- if node.decl.type.args:
3979
- param_idx = 0
3980
- for p in node.decl.type.args.params:
3981
- if isinstance(p, c_ast.EllipsisParam):
3982
- continue
3983
- # Skip void params (f(void) means no params)
3984
- if isinstance(p, c_ast.Typename) and isinstance(
3985
- getattr(p, "type", None), c_ast.TypeDecl
3986
- ):
3987
- if isinstance(
3988
- p.type.type, c_ast.IdentifierType
3989
- ) and p.type.type.names == ["void"]:
3990
- continue
3991
- if param_idx >= len(arg_types):
3992
- break
3993
- arg_type = arg_types[param_idx]
3994
- pname = p.name if isinstance(p.name, str) else f"arg{param_idx}"
3995
- var = self._alloca_in_entry(arg_type, pname)
3996
- self.define(pname, (arg_type, var))
3997
- self._safe_store(self.function.args[param_idx], var)
3998
- # Track unsigned params
3999
- if isinstance(p, c_ast.Decl) and isinstance(
4000
- getattr(p, "type", None), c_ast.TypeDecl
5920
+ for param_idx, (pname, arg_type, p) in enumerate(param_infos):
5921
+ if param_idx >= len(arg_types):
5922
+ break
5923
+ var = self._alloca_in_entry(arg_type, pname)
5924
+ self.define(pname, (arg_type, var))
5925
+ self._safe_store(self.function.args[param_idx], var)
5926
+ # Track unsigned params
5927
+ if isinstance(p, c_ast.Decl) and isinstance(
5928
+ getattr(p, "type", None), c_ast.TypeDecl
5929
+ ):
5930
+ if isinstance(p.type.type, c_ast.IdentifierType):
5931
+ if self._is_unsigned_type_names(p.type.type.names):
5932
+ self._mark_unsigned(var)
5933
+ if isinstance(p, c_ast.Decl):
5934
+ if self._has_unsigned_scalar_pointee(p.type):
5935
+ self._mark_unsigned_pointee(var)
5936
+ if isinstance(p.type, c_ast.PtrDecl) and self._func_decl_returns_unsigned(
5937
+ p.type.type
4001
5938
  ):
4002
- if isinstance(p.type.type, c_ast.IdentifierType):
4003
- if self._is_unsigned_type_names(p.type.type.names):
4004
- self._mark_unsigned(var)
4005
- if isinstance(p, c_ast.Decl):
4006
- if self._has_unsigned_scalar_pointee(p.type):
4007
- self._mark_unsigned_pointee(var)
4008
- if isinstance(
4009
- p.type, c_ast.PtrDecl
4010
- ) and self._func_decl_returns_unsigned(p.type.type):
4011
- self._mark_unsigned_return(var)
4012
- param_idx += 1
4013
-
4014
- self.codegen(node.body)
5939
+ self._mark_unsigned_return(var)
5940
+
5941
+ for _pname, _arg_type, p in param_infos:
5942
+ if isinstance(p, c_ast.Decl):
5943
+ self._emit_vla_param_bound_side_effects(p.type)
5944
+
5945
+ self._codegen_compound_items(node.body, use_new_scope=False)
4015
5946
 
4016
5947
  if not self.builder.block.is_terminated:
4017
5948
  if isinstance(ir_type, ir.VoidType):
4018
5949
  self.builder.ret_void()
4019
- elif isinstance(ir_type, ir.PointerType):
4020
- self.builder.ret(ir.Constant(ir_type, None))
4021
- elif self._is_floating_ir_type(ir_type):
4022
- self.builder.ret(ir.Constant(ir_type, 0.0))
4023
5950
  else:
4024
- self.builder.ret(ir.Constant(ir_type, 0))
5951
+ self.builder.ret(self._zero_initializer(ir_type))
4025
5952
 
4026
5953
  return None, None
4027
5954
 
@@ -4029,63 +5956,58 @@ class LLVMCodeGenerator(object):
4029
5956
  # Generate LLVM types for struct members
4030
5957
 
4031
5958
  # If this is a reference to a named struct without decls, look it up
4032
- if node.name and (node.decls is None or len(node.decls) == 0):
4033
- if node.name in self.env:
4034
- return self.env[node.name][0]
4035
- # Opaque/forward-declared struct: treat as i8 (byte) for pointer use
4036
- opaque = ir.IntType(8)
4037
- self.define(node.name, (opaque, None))
5959
+ if node.name and node.decls is None:
5960
+ tag_key = self._tag_type_key(node.name)
5961
+ if tag_key in self.env:
5962
+ return self.env[tag_key][0]
5963
+ opaque = self.module.context.get_identified_type(
5964
+ self._aggregate_type_name("struct", node.name)
5965
+ )
5966
+ self.define(tag_key, (opaque, None))
4038
5967
  return opaque
4039
5968
 
5969
+ if any(decl.bitsize is not None for decl in node.decls):
5970
+ return self._build_layout_backed_struct(node)
5971
+
4040
5972
  member_types = []
4041
5973
  member_names = []
4042
5974
  member_decl_types = []
4043
5975
  for decl in node.decls:
4044
- if isinstance(decl.type, c_ast.TypeDecl) and isinstance(
4045
- decl.type.type, c_ast.Struct
4046
- ):
4047
- nested_type = self.codegen_Struct(decl.type.type)
4048
- member_types.append(nested_type)
4049
- elif isinstance(decl.type, c_ast.TypeDecl) and isinstance(
4050
- decl.type.type, c_ast.Union
4051
- ):
4052
- nested_type = self.codegen_Union(decl.type.type)
4053
- member_types.append(nested_type)
4054
- elif isinstance(decl.type, c_ast.ArrayDecl):
4055
- # Handle multi-dimensional arrays: a[N][M] -> [N x [M x T]]
4056
- def _build_array_type(arr_node):
4057
- dim = self._eval_dim(arr_node.dim) if arr_node.dim else 0
4058
- if isinstance(arr_node.type, c_ast.ArrayDecl):
4059
- inner = _build_array_type(arr_node.type)
4060
- else:
4061
- inner = self._resolve_ast_type(arr_node.type)
4062
- return ir.ArrayType(inner, dim)
4063
-
4064
- member_types.append(_build_array_type(decl.type))
4065
- elif isinstance(decl.type, c_ast.PtrDecl):
4066
- member_types.append(self._resolve_ast_type(decl.type))
4067
- elif isinstance(decl.type, c_ast.TypeDecl):
4068
- type_str = decl.type.type.names
4069
- member_types.append(self._get_ir_type(type_str))
4070
- else:
4071
- member_types.append(int64_t) # fallback
5976
+ member_types.append(self._resolve_struct_member_ir_type(decl))
4072
5977
  member_names.append(decl.name)
4073
5978
  member_decl_types.append(decl.type)
4074
5979
  # Create the struct type
4075
- struct_type = ir.LiteralStructType(member_types)
5980
+ struct_type = self._identified_aggregate_type("struct", node.name, member_types)
4076
5981
  struct_type.members = member_names
4077
5982
  struct_type.member_decl_types = member_decl_types
4078
5983
 
4079
5984
  # Register named structs for later reuse
4080
5985
  if node.name:
4081
- self.define(node.name, (struct_type, None))
5986
+ self.define(self._tag_type_key(node.name), (struct_type, None))
4082
5987
 
4083
5988
  return struct_type
4084
5989
 
4085
5990
  def codegen_Union(self, node):
4086
5991
  """Model union as a struct with alignment-preserving storage."""
4087
- if node.name and (node.decls is None or len(node.decls) == 0):
4088
- return self.env[node.name][0]
5992
+ if node.name and node.decls is None:
5993
+ tag_key = self._tag_type_key(node.name)
5994
+ if tag_key in self.env:
5995
+ return self.env[tag_key][0]
5996
+ opaque = self.module.context.get_identified_type(
5997
+ self._aggregate_type_name("union", node.name)
5998
+ )
5999
+ self.define(tag_key, (opaque, None))
6000
+ return opaque
6001
+
6002
+ if node.decls is not None and len(node.decls) == 0:
6003
+ union_type = self._identified_aggregate_type("union", node.name, [])
6004
+ union_type.members = []
6005
+ union_type.member_types = {}
6006
+ union_type.member_decl_types = {}
6007
+ union_type.is_union = True
6008
+ if node.name:
6009
+ self.define(self._tag_type_key(node.name), (union_type, None))
6010
+ return union_type
4089
6011
 
4090
6012
  member_types = {}
4091
6013
  member_decl_types = {}
@@ -4112,18 +6034,17 @@ class LLVMCodeGenerator(object):
4112
6034
  align_size = max_align
4113
6035
  pad_size = max_size - align_size
4114
6036
  if pad_size > 0:
4115
- union_type = ir.LiteralStructType(
4116
- [align_type, ir.ArrayType(int8_t, pad_size)]
4117
- )
6037
+ union_body = [align_type, ir.ArrayType(int8_t, pad_size)]
4118
6038
  else:
4119
- union_type = ir.LiteralStructType([align_type])
6039
+ union_body = [align_type]
6040
+ union_type = self._identified_aggregate_type("union", node.name, union_body)
4120
6041
  union_type.members = list(member_types.keys())
4121
6042
  union_type.member_types = member_types
4122
6043
  union_type.member_decl_types = member_decl_types
4123
6044
  union_type.is_union = True
4124
6045
 
4125
6046
  if node.name:
4126
- self.define(node.name, (union_type, None))
6047
+ self.define(self._tag_type_key(node.name), (union_type, None))
4127
6048
 
4128
6049
  return union_type
4129
6050
 
@@ -4167,7 +6088,7 @@ class LLVMCodeGenerator(object):
4167
6088
  )
4168
6089
  struct_addr = inner_addr
4169
6090
  elif isinstance(node.name, c_ast.ID):
4170
- struct_instance_addr = self.env[node.name.name][1]
6091
+ _, struct_instance_addr = self.lookup(node.name.name)
4171
6092
  if not isinstance(struct_instance_addr.type, ir.PointerType):
4172
6093
  raise Exception("Invalid struct reference")
4173
6094
 
@@ -4227,6 +6148,66 @@ class LLVMCodeGenerator(object):
4227
6148
  if semantic_base_type is not None
4228
6149
  else (val.type if hasattr(val.type, "members") else int8_t)
4229
6150
  )
6151
+ if (
6152
+ addr is None
6153
+ and not isinstance(getattr(struct_addr, "type", None), ir.PointerType)
6154
+ and (
6155
+ getattr(struct_type, "has_custom_layout", False)
6156
+ or _is_struct_ir_type(struct_type)
6157
+ )
6158
+ ):
6159
+ materialized = self._alloca_in_entry(val.type, "structrval")
6160
+ self._safe_store(val, materialized)
6161
+ struct_addr = materialized
6162
+
6163
+ if getattr(struct_type, "has_custom_layout", False):
6164
+ layout = struct_type.field_layouts.get(node.field.name)
6165
+ if layout is None:
6166
+ raise RuntimeError(f"Field '{node.field.name}' not found in struct")
6167
+
6168
+ if layout.is_bitfield:
6169
+ container_ptr = self._byte_offset_ptr(
6170
+ struct_addr,
6171
+ layout.storage_byte_offset,
6172
+ ir.PointerType(layout.storage_ir_type),
6173
+ name="bitfieldptr",
6174
+ )
6175
+ ref = BitFieldRef(
6176
+ container_ptr=container_ptr,
6177
+ storage_ir_type=layout.storage_ir_type,
6178
+ bit_offset=layout.bit_offset,
6179
+ bit_width=layout.bit_width,
6180
+ semantic_ir_type=layout.semantic_ir_type,
6181
+ is_unsigned=layout.is_unsigned,
6182
+ )
6183
+ val = self._load_bitfield(ref)
6184
+ if layout.decl_type is not None:
6185
+ self._tag_value_from_decl_type(val, layout.decl_type)
6186
+ self._set_expr_ir_type(node, layout.semantic_ir_type)
6187
+ return val, ref
6188
+
6189
+ typed_field_addr = self._byte_offset_ptr(
6190
+ struct_addr,
6191
+ layout.byte_offset,
6192
+ ir.PointerType(layout.semantic_ir_type),
6193
+ name="fieldptr",
6194
+ )
6195
+ if isinstance(layout.semantic_ir_type, ir.ArrayType):
6196
+ elem_ptr = self.builder.gep(
6197
+ typed_field_addr,
6198
+ [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)],
6199
+ name="arraydecay",
6200
+ )
6201
+ if layout.decl_type is not None:
6202
+ self._tag_value_from_decl_type(elem_ptr, layout.decl_type)
6203
+ self._set_expr_ir_type(node, layout.semantic_ir_type)
6204
+ return elem_ptr, typed_field_addr
6205
+
6206
+ field_value = self._safe_load(typed_field_addr)
6207
+ if layout.decl_type is not None:
6208
+ self._tag_value_from_decl_type(field_value, layout.decl_type)
6209
+ self._set_expr_ir_type(node, layout.semantic_ir_type)
6210
+ return field_value, typed_field_addr
4230
6211
 
4231
6212
  # Union access: all fields share offset 0, use bitcast
4232
6213
  if getattr(struct_type, "is_union", False):
@@ -4242,8 +6223,8 @@ class LLVMCodeGenerator(object):
4242
6223
  resolved, ir.PointerType
4243
6224
  ):
4244
6225
  pass
4245
- elif isinstance(
4246
- resolved, (ir.ArrayType, ir.LiteralStructType, ir.PointerType)
6226
+ elif isinstance(resolved, (ir.ArrayType, ir.PointerType)) or _is_struct_ir_type(
6227
+ resolved
4247
6228
  ):
4248
6229
  semantic_field_type = resolved
4249
6230
  except Exception:
@@ -4267,10 +6248,9 @@ class LLVMCodeGenerator(object):
4267
6248
 
4268
6249
  # Opaque struct (no members) — treat as byte-offset access
4269
6250
  if not hasattr(struct_type, "members"):
4270
- ptr = self.builder.bitcast(struct_addr, voidptr_t)
4271
- val = self._safe_load(self.builder.bitcast(ptr, ir.PointerType(int64_t)))
4272
- self._set_expr_ir_type(node, int64_t)
4273
- return val, ptr
6251
+ raise SemanticError(
6252
+ f"field '{node.field.name}' accessed on incomplete struct"
6253
+ )
4274
6254
 
4275
6255
  field_index = None
4276
6256
  for i, field in enumerate(struct_type.members):
@@ -4301,7 +6281,9 @@ class LLVMCodeGenerator(object):
4301
6281
  resolved, ir.PointerType
4302
6282
  ):
4303
6283
  pass # keep original array type
4304
- elif isinstance(resolved, (ir.LiteralStructType, ir.PointerType)):
6284
+ elif isinstance(resolved, ir.PointerType) or _is_struct_ir_type(
6285
+ resolved
6286
+ ):
4305
6287
  semantic_field_type = resolved
4306
6288
  except Exception:
4307
6289
  pass
@@ -4385,16 +6367,35 @@ class LLVMCodeGenerator(object):
4385
6367
  if enumerator.value:
4386
6368
  current_val = self._eval_const_expr(enumerator.value)
4387
6369
  self.define(
4388
- enumerator.name, (int64_t, ir.Constant(int64_t, current_val))
6370
+ enumerator.name, (int32_t, ir.Constant(int32_t, current_val))
4389
6371
  )
4390
6372
  current_val += 1
4391
6373
  return None, None
4392
6374
 
4393
6375
  def _eval_const_expr(self, node):
4394
6376
  """Evaluate a constant expression at compile time (for enum values)."""
6377
+ def is_float_value(value):
6378
+ return isinstance(value, float)
6379
+
6380
+ def c_int_div(lhs, rhs):
6381
+ return int(lhs / rhs)
6382
+
6383
+ def c_int_mod(lhs, rhs):
6384
+ return lhs - c_int_div(lhs, rhs) * rhs
6385
+
6386
+ def c_float_div(lhs, rhs):
6387
+ if rhs == 0.0:
6388
+ if lhs == 0.0:
6389
+ return float("nan")
6390
+ sign = math.copysign(1.0, lhs) * math.copysign(1.0, rhs)
6391
+ return math.copysign(float("inf"), sign)
6392
+ return lhs / rhs
6393
+
4395
6394
  if isinstance(node, c_ast.Constant):
4396
6395
  if node.type == "string":
4397
6396
  return 0 # string constants can't be int-evaluated
6397
+ if node.type in ("float", "double"):
6398
+ return self._parse_float_constant(node.value)
4398
6399
  v = node.value.rstrip("uUlL")
4399
6400
  if v.startswith("'"):
4400
6401
  return self._char_constant_value(v)
@@ -4415,8 +6416,8 @@ class LLVMCodeGenerator(object):
4415
6416
  raw = node.expr.value[1:-1]
4416
6417
  processed = self._process_escapes(raw)
4417
6418
  return len(self._string_bytes(processed + "\00"))
4418
- val = self._eval_const_expr(node.expr)
4419
- return 8 # default sizeof for expressions
6419
+ ir_t = self._infer_sizeof_operand_ir_type(node.expr)
6420
+ return self._ir_type_size(ir_t)
4420
6421
  if node.op == "&" and isinstance(node.expr, c_ast.StructRef):
4421
6422
  offset, _ = self._eval_offsetof_structref(node.expr)
4422
6423
  return offset
@@ -4432,12 +6433,13 @@ class LLVMCodeGenerator(object):
4432
6433
  elif isinstance(node, c_ast.BinaryOp):
4433
6434
  l = self._eval_const_expr(node.left)
4434
6435
  r = self._eval_const_expr(node.right)
6436
+ use_float = is_float_value(l) or is_float_value(r)
4435
6437
  ops = {
4436
6438
  "+": lambda a, b: a + b,
4437
6439
  "-": lambda a, b: a - b,
4438
6440
  "*": lambda a, b: a * b,
4439
- "/": lambda a, b: a // b,
4440
- "%": lambda a, b: a % b,
6441
+ "/": lambda a, b: c_float_div(a, b) if use_float else c_int_div(a, b),
6442
+ "%": lambda a, b: a % b if use_float else c_int_mod(a, b),
4441
6443
  "<<": lambda a, b: a << b,
4442
6444
  ">>": lambda a, b: a >> b,
4443
6445
  "&": lambda a, b: a & b,
@@ -4469,6 +6471,14 @@ class LLVMCodeGenerator(object):
4469
6471
  return 0 # unknown identifier defaults to 0
4470
6472
  elif isinstance(node, c_ast.Cast):
4471
6473
  return self._eval_const_expr(node.expr)
6474
+ elif isinstance(node, c_ast.FuncCall):
6475
+ if isinstance(node.name, c_ast.ID):
6476
+ callee = node.name.name
6477
+ if callee in ("__builtin_inf", "__builtin_inff", "__builtin_infl"):
6478
+ return float("inf")
6479
+ if callee in ("__builtin_nan", "__builtin_nanf", "__builtin_nanl"):
6480
+ return float("nan")
6481
+ raise CodegenError(f"Not a constant expression: {type(node).__name__}")
4472
6482
  elif isinstance(node, c_ast.Typename):
4473
6483
  return 0
4474
6484
  raise CodegenError(f"Not a constant expression: {type(node).__name__}")
@@ -4512,7 +6522,7 @@ class LLVMCodeGenerator(object):
4512
6522
  elif isinstance(node.type.type, c_ast.Enum):
4513
6523
  # typedef enum { A, B, C } MyEnum;
4514
6524
  self.codegen_Enum(node.type.type)
4515
- self.define(f"__typedef_{node.name}", int64_t)
6525
+ self.define(f"__typedef_{node.name}", int32_t)
4516
6526
  elif isinstance(node.type, c_ast.ArrayDecl):
4517
6527
  self.define(f"__typedef_{node.name}", self._build_array_ir_type(node.type))
4518
6528
  elif isinstance(node.type, c_ast.PtrDecl):
@@ -4536,4 +6546,7 @@ class LLVMCodeGenerator(object):
4536
6546
  else:
4537
6547
  ptr_type = ir.PointerType(base_ir)
4538
6548
  self.define(f"__typedef_{node.name}", ptr_type)
6549
+ elif isinstance(node.type, c_ast.FuncDecl):
6550
+ func_type, _ = self._build_function_ir_type(node.type)
6551
+ self.define(f"__typedef_{node.name}", func_type)
4539
6552
  return None, None