python-cc 0.0.5__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 +2940 -565
  2. pcc/evaluater/c_evaluator.py +1192 -47
  3. pcc/lex/c_lexer.py +24 -5
  4. pcc/parse/c_parser.py +124 -51
  5. pcc/pcc.py +198 -5
  6. pcc/ply/yacc.py +1 -1
  7. pcc/project.py +934 -9
  8. python_cc-0.0.7.dist-info/METADATA +429 -0
  9. {python_cc-0.0.5.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.5.dist-info/METADATA +0 -185
  37. {python_cc-0.0.5.dist-info → python_cc-0.0.7.dist-info}/WHEEL +0 -0
  38. {python_cc-0.0.5.dist-info → python_cc-0.0.7.dist-info}/entry_points.txt +0 -0
  39. {python_cc-0.0.5.dist-info → python_cc-0.0.7.dist-info}/licenses/LICENSE +0 -0
pcc/codegen/c_codegen.py CHANGED
@@ -1,8 +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
7
+ from dataclasses import dataclass
8
+ from itertools import count
6
9
  from llvmlite.ir import IRBuilder
7
10
  from ..ast import c_ast as c_ast
8
11
 
@@ -10,6 +13,7 @@ bool_t = ir.IntType(1)
10
13
  int8_t = ir.IntType(8)
11
14
  int32_t = ir.IntType(32)
12
15
  int64_t = ir.IntType(64)
16
+ int128_t = ir.IntType(128)
13
17
  voidptr_t = int8_t.as_pointer()
14
18
  int64ptr_t = int64_t.as_pointer()
15
19
  true_bit = bool_t(1)
@@ -18,6 +22,52 @@ true_byte = int8_t(1)
18
22
  false_byte = int8_t(0)
19
23
  cstring = voidptr_t
20
24
  struct_types = {}
25
+ _aggregate_namespace_counter = count(1)
26
+
27
+
28
+ class SemanticError(ValueError):
29
+ pass
30
+
31
+
32
+ @dataclass
33
+ class FileScopeObjectState:
34
+ type_key: str
35
+ linkage: str
36
+ definition_kind: str
37
+ symbol_name: str
38
+ ir_type: object
39
+
40
+
41
+ @dataclass
42
+ class FileScopeFunctionState:
43
+ type_key: str
44
+ linkage: str
45
+ defined: bool
46
+ symbol_name: str
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
21
71
 
22
72
  # Libc function signature registry: name -> (return_type, [param_types], var_arg)
23
73
  # Covers: stdio.h, stdlib.h, string.h, ctype.h, math.h, unistd.h, time.h
@@ -92,6 +142,15 @@ LIBC_FUNCTIONS = {
92
142
  "qsort": (_VOID, [voidptr_t, _size_t, _size_t, voidptr_t], False),
93
143
  "bsearch": (voidptr_t, [voidptr_t, voidptr_t, _size_t, _size_t, voidptr_t], False),
94
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),
95
154
  "system": (int32_t, [cstring], False),
96
155
  # === string.h ===
97
156
  "strlen": (_size_t, [cstring], False),
@@ -101,6 +160,8 @@ LIBC_FUNCTIONS = {
101
160
  "strncpy": (cstring, [cstring, cstring, _size_t], False),
102
161
  "strcat": (cstring, [cstring, cstring], False),
103
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),
104
165
  "strchr": (cstring, [cstring, int32_t], False),
105
166
  "strrchr": (cstring, [cstring, int32_t], False),
106
167
  "strstr": (cstring, [cstring, cstring], False),
@@ -128,6 +189,34 @@ LIBC_FUNCTIONS = {
128
189
  "isgraph": (int32_t, [int32_t], False),
129
190
  "toupper": (int32_t, [int32_t], False),
130
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),
131
220
  # === math.h ===
132
221
  "sin": (_double, [_double], False),
133
222
  "cos": (_double, [_double], False),
@@ -153,7 +242,9 @@ LIBC_FUNCTIONS = {
153
242
  "round": (_double, [_double], False),
154
243
  "trunc": (_double, [_double], False),
155
244
  "fmod": (_double, [_double, _double], False),
245
+ "fabsf": (_float, [_float], False),
156
246
  "fabs": (_double, [_double], False),
247
+ "fabsl": (_double, [_double], False),
157
248
  "ldexp": (_double, [_double, int32_t], False),
158
249
  # === time.h ===
159
250
  "time": (_time_t, [voidptr_t], False),
@@ -161,36 +252,75 @@ LIBC_FUNCTIONS = {
161
252
  "difftime": (_double, [_time_t, _time_t], False),
162
253
  "gmtime_r": (voidptr_t, [voidptr_t, voidptr_t], False),
163
254
  "localtime_r": (voidptr_t, [voidptr_t, voidptr_t], False),
255
+ "nanosleep": (int32_t, [voidptr_t, voidptr_t], False),
164
256
  # === unistd.h (POSIX) ===
165
257
  "sleep": (int32_t, [int32_t], False),
258
+ "alarm": (int32_t, [int32_t], False),
166
259
  "usleep": (int32_t, [int32_t], False),
167
260
  "read": (int64_t, [int32_t, voidptr_t, _size_t], False),
168
261
  "write": (int64_t, [int32_t, voidptr_t, _size_t], False),
262
+ "getuid": (int32_t, [], False),
263
+ "geteuid": (int32_t, [], False),
169
264
  "open": (int32_t, [cstring, int32_t], True),
170
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),
171
274
  "getpid": (int32_t, [], False),
172
275
  "getppid": (int32_t, [], False),
276
+ "sysconf": (int64_t, [int32_t], False),
173
277
  "isatty": (int32_t, [int32_t], False),
174
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),
175
285
  # === setjmp.h ===
176
286
  "setjmp": (int32_t, [voidptr_t], False),
177
287
  "longjmp": (_VOID, [voidptr_t, int32_t], False),
178
288
  "_setjmp": (int32_t, [voidptr_t], False),
179
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),
180
292
  # === signal.h ===
181
293
  "signal": (voidptr_t, [int32_t, voidptr_t], False),
182
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),
183
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),
184
302
  "raise": (int32_t, [int32_t], False),
185
303
  # === errno ===
186
304
  "__errno_location": (ir.IntType(32).as_pointer(), [], False),
187
305
  # === locale.h ===
188
306
  "setlocale": (cstring, [int32_t, cstring], False),
189
307
  "localeconv": (voidptr_t, [], False),
308
+ "nl_langinfo": (cstring, [int32_t], False),
190
309
  # === misc ===
191
310
  "tmpnam": (cstring, [cstring], False),
192
311
  "tmpfile": (voidptr_t, [], False),
193
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),
194
324
  "gmtime": (voidptr_t, [voidptr_t], False),
195
325
  "localtime": (voidptr_t, [voidptr_t], False),
196
326
  "mktime": (_time_t, [voidptr_t], False),
@@ -202,10 +332,28 @@ LIBC_FUNCTIONS = {
202
332
  "__builtin_va_start": (_VOID, [voidptr_t], False),
203
333
  "__builtin_va_end": (_VOID, [voidptr_t], False),
204
334
  "__builtin_va_copy": (_VOID, [voidptr_t, voidptr_t], False),
335
+ "__builtin_alloca": (voidptr_t, [int64_t], False),
205
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),
206
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),
207
346
  "__builtin_clz": (int32_t, [int32_t], False),
347
+ "__builtin_clzll": (int32_t, [int64_t], False),
208
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),
209
357
  "modf": (_double, [_double, ir.DoubleType().as_pointer()], False),
210
358
  "ldexp": (_double, [_double, int32_t], False),
211
359
  "__builtin_va_arg": (voidptr_t, [voidptr_t, int64_t], False),
@@ -228,6 +376,12 @@ class CodegenError(Exception):
228
376
  pass
229
377
 
230
378
 
379
+ class ExternGlobalRef:
380
+ def __init__(self, symbol_name, ir_type):
381
+ self.symbol_name = symbol_name
382
+ self.ir_type = ir_type
383
+
384
+
231
385
  int16_t = ir.IntType(16)
232
386
 
233
387
 
@@ -275,25 +429,10 @@ _PCC_VAARG_CALL_RE = re.compile(
275
429
  )
276
430
 
277
431
 
278
- _INVALID_VOID_INSTR_RE = re.compile(
279
- r"^(?:%\S+\s*=\s*)?(?:alloca|load|bitcast) void(?! \()([,\s]|$)|^store void(?! \()([,\s]|$)"
280
- )
281
-
282
- _LABEL_RE = re.compile(r"^[A-Za-z$._][A-Za-z0-9$._-]*:$")
283
-
284
-
285
432
  def postprocess_ir_text(text):
286
- """Apply textual IR rewrites that llvmlite cannot express directly."""
433
+ """Apply the minimal textual lowering that llvmlite cannot express directly."""
287
434
 
288
- # --- simple regex rewrites ---
289
435
  text = _PCC_VAARG_DECL_RE.sub("", text)
290
- text = re.sub(
291
- r"bitcast i64 (%\S+) to (i8\*|[^,\n]+\*)", r"inttoptr i64 \1 to \2", text
292
- )
293
- text = re.sub(
294
- r"bitcast i8 (%\S+) to (i8\*|[^,\n]+\*)", r"inttoptr i8 \1 to \2", text
295
- )
296
- text = re.sub(r"ptrtoint \[\d+ x i8\] [^\n]+ to i64", "add i64 0, 0", text)
297
436
 
298
437
  def repl(match):
299
438
  lhs = match.group("lhs")
@@ -303,74 +442,7 @@ def postprocess_ir_text(text):
303
442
  return f"{lhs} = va_arg {argtype} {argval}, {rettype}"
304
443
 
305
444
  text = _PCC_VAARG_CALL_RE.sub(repl, text)
306
-
307
- # --- line-level fixups ---
308
- lines = []
309
- for line in text.splitlines():
310
- # Fix Python repr leak in array initializers → zeroinitializer
311
- if "<ir.Constant" in line:
312
- m = re.match(r'(@"[^"]*"\s*=\s*(?:global|constant)\s*\[[^\]]*\]).*', line)
313
- if m:
314
- line = m.group(1) + " zeroinitializer"
315
- else:
316
- continue
317
- s = line.strip()
318
- # Drop invalid void instructions (alloca void, load void, store void)
319
- if _INVALID_VOID_INSTR_RE.match(s):
320
- continue
321
- lines.append(line)
322
-
323
- # Deduplicate switch case values
324
- deduped = []
325
- for line in lines:
326
- s = line.strip()
327
- if s.startswith("switch i64 ") and "[" in line and "]" in line:
328
- prefix, rest = line.split("[", 1)
329
- case_text, suffix = rest.rsplit("]", 1)
330
- cases = re.findall(r'(i64 -?\d+, label %"[^"]*")', case_text)
331
- if cases:
332
- seen = set()
333
- unique = []
334
- for case in cases:
335
- val = re.match(r"i64 (-?\d+)", case).group(1)
336
- if val not in seen:
337
- seen.add(val)
338
- unique.append(case)
339
- line = prefix + "[" + " ".join(unique) + "]" + suffix
340
- deduped.append(line)
341
-
342
- # Repair control flow: drop dead code after terminators, bridge empty labels
343
- def _is_label(s):
344
- return bool(_LABEL_RE.match(s))
345
-
346
- def _is_terminator(s):
347
- return s.startswith(("br ", "ret ", "switch ", "unreachable", "resume "))
348
-
349
- repaired = []
350
- skip_dead = False
351
- for raw in deduped:
352
- s = raw.strip()
353
- if skip_dead:
354
- if _is_label(s):
355
- skip_dead = False
356
- else:
357
- continue
358
- if (
359
- repaired
360
- and _is_terminator(repaired[-1].strip())
361
- and s
362
- and raw.startswith(" ")
363
- and not _is_label(s)
364
- ):
365
- skip_dead = True
366
- continue
367
- if repaired and _is_label(repaired[-1].strip()) and _is_label(s):
368
- repaired.append(f' br label %"{s[:-1]}"')
369
- if s == "}" and repaired and _is_label(repaired[-1].strip()):
370
- repaired.append(" unreachable")
371
- repaired.append(raw)
372
-
373
- return "\n".join(repaired)
445
+ return text
374
446
 
375
447
 
376
448
  def get_ir_type_from_names(names):
@@ -400,12 +472,14 @@ def get_ir_type_from_names(names):
400
472
  "void": ir.VoidType(),
401
473
  "double": _double,
402
474
  "float": _float,
475
+ "_Float16": _float,
403
476
  "short": int16_t,
404
477
  "long": int64_t,
405
478
  "int short": int16_t,
406
479
  "int long": int64_t,
407
480
  "long long": int64_t,
408
481
  "int long long": int64_t,
482
+ "__int128": int128_t,
409
483
  "char unsigned": int8_t,
410
484
  "int unsigned": int32_t,
411
485
  "unsigned": int32_t,
@@ -414,6 +488,7 @@ def get_ir_type_from_names(names):
414
488
  "int long unsigned": int64_t,
415
489
  "long unsigned": int64_t,
416
490
  "long long unsigned": int64_t,
491
+ "__int128 unsigned": int128_t,
417
492
  # size_t, etc.
418
493
  "size_t": int64_t,
419
494
  "ssize_t": int64_t,
@@ -435,6 +510,8 @@ def get_ir_type_from_names(names):
435
510
  return _double
436
511
  if "float" in names:
437
512
  return _float
513
+ if "_Float16" in names:
514
+ return _float
438
515
  # If it contains 'char', return i8
439
516
  if "char" in names:
440
517
  return int8_t
@@ -459,14 +536,20 @@ def _resolve_node_type(node_type):
459
536
  if isinstance(inner, c_ast.FuncDecl):
460
537
  ret_type = _resolve_node_type(inner.type)
461
538
  param_types = []
539
+ is_var_arg = False
462
540
  if inner.args:
463
541
  for p in inner.args.params:
464
542
  if isinstance(p, c_ast.EllipsisParam):
543
+ is_var_arg = True
465
544
  continue
466
545
  t = get_ir_type_from_node(p)
467
546
  if not isinstance(t, ir.VoidType):
468
547
  param_types.append(t)
469
- 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()
470
553
  pointee = _resolve_node_type(inner)
471
554
  if isinstance(pointee, ir.VoidType):
472
555
  return voidptr_t
@@ -476,7 +559,7 @@ def _resolve_node_type(node_type):
476
559
  return get_ir_type(node_type.type.names)
477
560
  elif isinstance(node_type.type, c_ast.Struct):
478
561
  snode = node_type.type
479
- if snode.decls:
562
+ if snode.decls is not None:
480
563
  # Inline struct with declarations — build real type
481
564
  member_types = []
482
565
  for decl in snode.decls:
@@ -488,8 +571,15 @@ def _resolve_node_type(node_type):
488
571
  return int8_t # opaque/forward-declared struct
489
572
  elif isinstance(node_type.type, c_ast.Union):
490
573
  unode = node_type.type
491
- if unode.decls:
574
+ if unode.decls is not None:
492
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
493
583
  max_size = 0
494
584
  max_align = 1
495
585
  member_types = {}
@@ -517,7 +607,7 @@ def _resolve_node_type(node_type):
517
607
  ir_t = _resolve_union_member_type(decl.type)
518
608
  member_types[decl.name] = ir_t
519
609
  sz = ir_t.width // 8 if isinstance(ir_t, ir.IntType) else 8
520
- if isinstance(ir_t, ir.LiteralStructType):
610
+ if _is_struct_ir_type(ir_t):
521
611
  sz = sum(
522
612
  e.width // 8 if isinstance(e, ir.IntType) else 8
523
613
  for e in ir_t.elements
@@ -545,15 +635,21 @@ def _resolve_node_type(node_type):
545
635
  ut.is_union = True
546
636
  return ut
547
637
  return int8_t # opaque/forward-declared union
638
+ elif isinstance(node_type.type, c_ast.Enum):
639
+ return int32_t
548
640
  return int64_t
549
641
  elif isinstance(node_type, c_ast.ArrayDecl):
550
642
  return voidptr_t # array params decay to pointer
551
643
  return int64_t
552
644
 
553
645
 
646
+ def _is_struct_ir_type(ir_type):
647
+ return isinstance(ir_type, (ir.LiteralStructType, ir.IdentifiedStructType))
648
+
649
+
554
650
  class LLVMCodeGenerator(object):
555
651
 
556
- def __init__(self):
652
+ def __init__(self, translation_unit_name=None):
557
653
  self.module = ir.Module()
558
654
  # Set proper data layout for struct padding/alignment
559
655
  import llvmlite.binding as _llvm
@@ -570,6 +666,8 @@ class LLVMCodeGenerator(object):
570
666
  self.env = ChainMap()
571
667
  self.nlabels = 0
572
668
  self.function = None
669
+ self._function_display_name = None
670
+ self._frame_address_marker = None
573
671
  self.in_global = True
574
672
  self._declared_libc = set()
575
673
  self._unsigned_bindings = set() # alloca/global ids with unsigned type
@@ -578,10 +676,379 @@ class LLVMCodeGenerator(object):
578
676
  self._expr_ir_types = {}
579
677
  self._labels = {}
580
678
  self._vaarg_counter = 0
679
+ self._anon_type_counter = 0
680
+ self._file_scope_object_states = {}
681
+ self._file_scope_function_states = {}
682
+ self._future_file_scope_object_ir_types = {}
683
+ self._future_file_scope_function_ir_types = {}
684
+ self.translation_unit_name = self._sanitize_translation_unit_name(
685
+ translation_unit_name
686
+ )
687
+ base_namespace = self.translation_unit_name or "pcc"
688
+ self._aggregate_namespace = (
689
+ f"{base_namespace}_{next(_aggregate_namespace_counter)}"
690
+ )
581
691
 
582
692
  def define(self, name, val):
583
693
  self.env[name] = val
584
694
 
695
+ @staticmethod
696
+ def _sanitize_translation_unit_name(name):
697
+ if not name:
698
+ return None
699
+ return re.sub(r"\W+", "_", name)
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
+
724
+ def _is_file_scope_static(self, storage=None):
725
+ return (
726
+ self.translation_unit_name
727
+ and self.in_global
728
+ and storage
729
+ and "static" in storage
730
+ )
731
+
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):
743
+ return f"__pcc_internal_{self.translation_unit_name}_{name}"
744
+ return name
745
+
746
+ def _static_local_symbol_name(self, name):
747
+ if self.translation_unit_name:
748
+ return f"__static_{self.translation_unit_name}_{self.function.name}_{name}"
749
+ return f"__static_{self.function.name}_{name}"
750
+
751
+ def _create_bound_global(
752
+ self, bind_name, ir_type, symbol_name=None, external=False, storage=None
753
+ ):
754
+ actual_name = symbol_name or bind_name
755
+ gv = self.module.globals.get(actual_name)
756
+ if gv is None:
757
+ gv = ir.GlobalVariable(self.module, ir_type, actual_name)
758
+ if self._is_file_scope_static(storage):
759
+ gv.linkage = "internal"
760
+ elif not external and getattr(gv, "initializer", None) is None:
761
+ gv.initializer = ir.Constant(ir_type, None)
762
+ self.define(bind_name, (ir_type, gv))
763
+ return gv
764
+
765
+ def _decl_linkage(self, storage=None, funcspec=None, existing_state=None):
766
+ if storage and "static" in storage:
767
+ return "internal"
768
+ if self._has_internal_inline_linkage(storage, funcspec):
769
+ return "internal"
770
+ if existing_state is not None:
771
+ return existing_state.linkage
772
+ return "external"
773
+
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
+ )
787
+ if existing_state is not None and existing_state.linkage == "internal":
788
+ return existing_state.symbol_name
789
+ return name
790
+
791
+ def _file_scope_object_definition_kind(self, storage=None, has_initializer=False):
792
+ if storage and "extern" in storage and not has_initializer:
793
+ return "extern"
794
+ if has_initializer:
795
+ return "definition"
796
+ return "tentative"
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
+
840
+ def _prepare_file_scope_object(self, name, ir_type, storage=None, has_initializer=False):
841
+ if not self.in_global or name is None:
842
+ return None, True
843
+ if name in self._file_scope_function_states:
844
+ raise SemanticError(f"'{name}' redeclared as object after function declaration")
845
+ ir_type = self._preferred_file_scope_object_ir_type(name, ir_type)
846
+
847
+ type_key = str(ir_type)
848
+ definition_kind = self._file_scope_object_definition_kind(
849
+ storage, has_initializer
850
+ )
851
+ state = self._file_scope_object_states.get(name)
852
+ linkage = self._decl_linkage(storage, existing_state=state)
853
+ symbol_name = self._effective_file_scope_symbol_name(
854
+ name, storage=storage, existing_state=state
855
+ )
856
+
857
+ if state is None:
858
+ state = FileScopeObjectState(
859
+ type_key=type_key,
860
+ linkage=linkage,
861
+ definition_kind=definition_kind,
862
+ symbol_name=symbol_name,
863
+ ir_type=ir_type,
864
+ )
865
+ self._file_scope_object_states[name] = state
866
+ if definition_kind == "extern":
867
+ self._record_extern_global(name, ir_type, storage=storage)
868
+ return None, False
869
+ gv = self._create_bound_global(
870
+ name, ir_type, symbol_name=symbol_name, storage=storage
871
+ )
872
+ return gv, True
873
+
874
+ if not self._are_compatible_object_ir_types(state.ir_type, ir_type):
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)
879
+ if state.linkage != linkage:
880
+ raise SemanticError(f"conflicting linkage for global '{name}'")
881
+ if state.symbol_name != symbol_name:
882
+ raise SemanticError(f"conflicting symbol binding for global '{name}'")
883
+
884
+ existing = self.module.globals.get(symbol_name)
885
+ if definition_kind == "extern":
886
+ if existing is not None:
887
+ self.define(name, (merged_ir_type, existing))
888
+ else:
889
+ self._record_extern_global(name, merged_ir_type, storage=storage)
890
+ return None, False
891
+
892
+ if existing is None:
893
+ gv = self._create_bound_global(
894
+ name, ir_type, symbol_name=symbol_name, storage=storage
895
+ )
896
+ else:
897
+ gv = existing
898
+ self.define(name, (ir_type, gv))
899
+
900
+ if state.definition_kind == "definition":
901
+ if definition_kind == "definition":
902
+ raise SemanticError(f"redefinition of global '{name}'")
903
+ return gv, False
904
+
905
+ if state.definition_kind == "tentative":
906
+ if definition_kind == "definition":
907
+ state.definition_kind = "definition"
908
+ return gv, True
909
+ return gv, False
910
+
911
+ state.definition_kind = definition_kind
912
+ return gv, True
913
+
914
+ def _register_file_scope_function(
915
+ self, name, function_type, storage=None, funcspec=None, is_definition=False
916
+ ):
917
+ if not self.in_global or name is None:
918
+ return
919
+ if name in self._file_scope_object_states:
920
+ raise SemanticError(f"'{name}' redeclared as function after object declaration")
921
+
922
+ type_key = str(function_type)
923
+ state = self._file_scope_function_states.get(name)
924
+ linkage = self._decl_linkage(storage, funcspec=funcspec, existing_state=state)
925
+ symbol_name = self._effective_file_scope_symbol_name(
926
+ name,
927
+ storage=storage,
928
+ funcspec=funcspec,
929
+ existing_state=state,
930
+ linkage=linkage,
931
+ )
932
+
933
+ if state is None:
934
+ self._file_scope_function_states[name] = FileScopeFunctionState(
935
+ type_key=type_key,
936
+ linkage=linkage,
937
+ defined=is_definition,
938
+ symbol_name=symbol_name,
939
+ )
940
+ return symbol_name
941
+
942
+ if state.type_key != type_key:
943
+ raise SemanticError(f"conflicting types for function '{name}'")
944
+ if state.linkage != linkage:
945
+ raise SemanticError(f"conflicting linkage for function '{name}'")
946
+ if state.symbol_name != symbol_name:
947
+ raise SemanticError(f"conflicting symbol binding for function '{name}'")
948
+ if is_definition:
949
+ if state.defined:
950
+ raise SemanticError(f"redefinition of function '{name}'")
951
+ state.defined = True
952
+ return state.symbol_name
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
+
960
+ def external_definitions(self):
961
+ defs = []
962
+ for name, state in self._file_scope_function_states.items():
963
+ if state.linkage == "external" and state.defined:
964
+ defs.append(("function", state.symbol_name, name))
965
+ for name, state in self._file_scope_object_states.items():
966
+ if (
967
+ state.linkage == "external"
968
+ and state.definition_kind in ("tentative", "definition")
969
+ ):
970
+ defs.append(("object", state.symbol_name, name))
971
+ return defs
972
+
973
+ def _is_global_extern_decl(self, node):
974
+ return (
975
+ self.in_global
976
+ and node.init is None
977
+ and node.storage
978
+ and "extern" in node.storage
979
+ and not isinstance(node.type, c_ast.FuncDecl)
980
+ and node.name is not None
981
+ )
982
+
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):
991
+ if isinstance(node_type, c_ast.ArrayDecl):
992
+ return self._build_array_ir_type(node_type, init_node=init_node)
993
+ return self._resolve_ast_type(node_type)
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
+
1040
+ def _record_extern_global(self, name, ir_type, storage=None):
1041
+ ir_type = self._preferred_file_scope_object_ir_type(name, ir_type)
1042
+ self.define(
1043
+ name,
1044
+ (
1045
+ ir_type,
1046
+ ExternGlobalRef(
1047
+ self._file_scope_symbol_name(name, storage), ir_type
1048
+ ),
1049
+ ),
1050
+ )
1051
+
585
1052
  def _mark_unsigned(self, binding):
586
1053
  """Mark a concrete IR binding as having unsigned type."""
587
1054
  if binding is not None:
@@ -698,6 +1165,34 @@ class LLVMCodeGenerator(object):
698
1165
  return self._convert_int_value(val, int32_t, result_unsigned=False)
699
1166
  return val
700
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
+
701
1196
  def _usual_arithmetic_conversion(self, lhs, rhs):
702
1197
  lhs = self._integer_promotion(lhs)
703
1198
  rhs = self._integer_promotion(rhs)
@@ -747,10 +1242,18 @@ class LLVMCodeGenerator(object):
747
1242
  return _float
748
1243
 
749
1244
  def _parse_float_constant(self, raw):
1245
+ is_float32 = raw.endswith(("f", "F"))
750
1246
  value = raw.rstrip("fFlL")
751
1247
  if value.lower().startswith("0x") and "p" in value.lower():
752
- return float.fromhex(value)
753
- 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
754
1257
 
755
1258
  def _float_literal_ir_type(self, raw):
756
1259
  if raw.endswith(("f", "F")):
@@ -769,18 +1272,14 @@ class LLVMCodeGenerator(object):
769
1272
  return existing
770
1273
  try:
771
1274
  gv = ir.GlobalVariable(self.module, ir_type, name)
772
- if external:
773
- gv.linkage = "external"
774
- else:
1275
+ if not external:
775
1276
  gv.initializer = ir.Constant(ir_type, None)
776
1277
  return gv
777
1278
  except Exception:
778
1279
  gv = self.module.globals.get(name) or ir.GlobalVariable(
779
1280
  self.module, ir_type, self.module.get_unique_name(name)
780
1281
  )
781
- if external:
782
- gv.linkage = "external"
783
- elif getattr(gv, "initializer", None) is None:
1282
+ if not external and getattr(gv, "initializer", None) is None:
784
1283
  gv.initializer = ir.Constant(ir_type, None)
785
1284
  return gv
786
1285
 
@@ -805,7 +1304,17 @@ class LLVMCodeGenerator(object):
805
1304
  gv_type = self._EXTERN_GLOBAL_VARS[name]
806
1305
  gv = self._safe_global_var(gv_type, name, external=True)
807
1306
  self.define(name, (gv_type, gv))
808
- return self.env[name]
1307
+ stored = self.env[name]
1308
+ if not (isinstance(stored, tuple) and len(stored) == 2):
1309
+ return stored
1310
+ valtype, binding = stored
1311
+ if isinstance(binding, ExternGlobalRef):
1312
+ gv = self._safe_global_var(
1313
+ binding.ir_type, binding.symbol_name, external=True
1314
+ )
1315
+ self.define(name, (binding.ir_type, gv))
1316
+ return self.env[name]
1317
+ return valtype, binding
809
1318
 
810
1319
  def _declare_libc(self, name):
811
1320
  """Lazily declare a libc function on first use."""
@@ -844,16 +1353,21 @@ class LLVMCodeGenerator(object):
844
1353
  @contextmanager
845
1354
  def new_function(self):
846
1355
  oldfunc = self.function
1356
+ old_display_name = self._function_display_name
1357
+ old_frame_address_marker = self._frame_address_marker
847
1358
  oldbuilder = self.builder
848
1359
  oldenv = self.env
849
1360
  oldlabels = self._labels
850
1361
  self.in_global = False
851
1362
  self.env = self.env.new_child()
852
1363
  self._labels = {}
1364
+ self._frame_address_marker = None
853
1365
  try:
854
1366
  yield
855
1367
  finally:
856
1368
  self.function = oldfunc
1369
+ self._function_display_name = old_display_name
1370
+ self._frame_address_marker = old_frame_address_marker
857
1371
  self.builder = oldbuilder
858
1372
  self.env = oldenv
859
1373
  self._labels = oldlabels
@@ -872,7 +1386,14 @@ class LLVMCodeGenerator(object):
872
1386
  return normal
873
1387
 
874
1388
  def create_entry_block_alloca(
875
- self, name, type_str, size, array_list=None, point_level=0
1389
+ self,
1390
+ name,
1391
+ type_str,
1392
+ size,
1393
+ array_list=None,
1394
+ point_level=0,
1395
+ storage=None,
1396
+ symbol_name=None,
876
1397
  ):
877
1398
 
878
1399
  ir_type = get_ir_type(type_str)
@@ -893,20 +1414,12 @@ class LLVMCodeGenerator(object):
893
1414
  ret = self._alloca_in_entry(ir_type, name)
894
1415
  self.define(name, (ir_type, ret))
895
1416
  else:
896
- existing = self.module.globals.get(name)
897
- if existing:
898
- ret = existing
899
- else:
900
- try:
901
- ret = ir.GlobalVariable(self.module, ir_type, name)
902
- ret.initializer = ir.Constant(ir_type, None)
903
- except Exception:
904
- ret = self.module.globals.get(name) or ir.GlobalVariable(
905
- self.module, ir_type, self.module.get_unique_name(name)
906
- )
907
- if hasattr(ret, "initializer") and ret.initializer is None:
908
- ret.initializer = ir.Constant(ir_type, None)
909
- self.define(name, (ir_type, ret))
1417
+ ret = self._create_bound_global(
1418
+ name,
1419
+ ir_type,
1420
+ symbol_name=symbol_name or self._file_scope_symbol_name(name, storage),
1421
+ storage=storage,
1422
+ )
910
1423
 
911
1424
  return ret, ir_type
912
1425
 
@@ -956,9 +1469,11 @@ class LLVMCodeGenerator(object):
956
1469
  for i, ext in enumerate(node.ext):
957
1470
  is_type_def = False
958
1471
  if isinstance(ext, c_ast.Decl):
959
- 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
+ ):
960
1475
  is_type_def = True
961
- elif isinstance(ext.type, c_ast.TypeDecl) and isinstance(
1476
+ elif ext.name is None and isinstance(ext.type, c_ast.TypeDecl) and isinstance(
962
1477
  ext.type.type, (c_ast.Struct, c_ast.Union)
963
1478
  ):
964
1479
  is_type_def = True
@@ -970,6 +1485,9 @@ class LLVMCodeGenerator(object):
970
1485
  except Exception:
971
1486
  pass
972
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)
973
1491
  for i, ext in enumerate(node.ext):
974
1492
  if i not in pass1:
975
1493
  try:
@@ -1053,15 +1571,37 @@ class LLVMCodeGenerator(object):
1053
1571
  if node.type == "int":
1054
1572
  # Support hex (0xFF), octal (077), and decimal literals
1055
1573
  raw = node.value
1056
- 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
1057
1577
  val_str = raw.rstrip("uUlL")
1058
1578
  if val_str.startswith("0x") or val_str.startswith("0X"):
1059
1579
  int_val = int(val_str, 16)
1580
+ is_non_decimal = True
1060
1581
  elif val_str.startswith("0") and len(val_str) > 1 and val_str[1:].isdigit():
1061
1582
  int_val = int(val_str, 8)
1583
+ is_non_decimal = True
1062
1584
  else:
1063
1585
  int_val = int(val_str)
1064
- 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)
1065
1605
  if is_unsigned:
1066
1606
  self._tag_unsigned(result)
1067
1607
  return result, None
@@ -1095,6 +1635,7 @@ class LLVMCodeGenerator(object):
1095
1635
  if lv is None or rv is None:
1096
1636
  return ir.Constant(int64_t, 0), None
1097
1637
  result = None
1638
+ is_bitfield = isinstance(lv_addr, BitFieldRef)
1098
1639
 
1099
1640
  dispatch_type_double = 1
1100
1641
  dispatch_type_int = 0
@@ -1150,13 +1691,19 @@ class LLVMCodeGenerator(object):
1150
1691
 
1151
1692
  if node.op == "=":
1152
1693
  # Type coercion: match rv to the target's pointee type
1153
- 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"):
1154
1697
  target_type = lv_addr.type.pointee
1155
1698
  else:
1156
1699
  target_type = lv.type
1157
1700
  if rv.type != target_type:
1158
1701
  rv = self._implicit_convert(rv, target_type)
1159
- 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)
1160
1707
  return rv, lv_addr # return value for chained assignment
1161
1708
  else:
1162
1709
  # Pointer compound assignment: p += n, p -= n
@@ -1172,6 +1719,11 @@ class LLVMCodeGenerator(object):
1172
1719
  addresult = handle(lv, rv, "addtmp")
1173
1720
  else:
1174
1721
  addresult = handle(lv, rv, "addtmp")
1722
+ if dispatch_type == dispatch_type_int and is_unsigned:
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
1175
1727
  self._safe_store(addresult, lv_addr)
1176
1728
  return addresult, lv_addr
1177
1729
 
@@ -1196,8 +1748,14 @@ class LLVMCodeGenerator(object):
1196
1748
  if is_inc
1197
1749
  else self.builder.sub(lv, one, "dec")
1198
1750
  )
1199
- self._safe_store(new_val, lv_addr)
1200
- result = lv if is_post else new_val
1751
+ if self._is_unsigned_val(lv):
1752
+ self._tag_unsigned(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
1201
1759
 
1202
1760
  elif node.op == "*":
1203
1761
  if (
@@ -1211,8 +1769,8 @@ class LLVMCodeGenerator(object):
1211
1769
  node.expr.expr.args.exprs if node.expr.expr.args is not None else []
1212
1770
  )
1213
1771
  if isinstance(target_ptr_type, ir.PointerType) and va_args:
1214
- ap_addr, _ = self.codegen(va_args[0])
1215
- 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:
1216
1774
  self._vaarg_counter += 1
1217
1775
  name = f"__pcc_va_arg_{self._vaarg_counter}"
1218
1776
  placeholder = self.module.globals.get(name)
@@ -1235,6 +1793,11 @@ class LLVMCodeGenerator(object):
1235
1793
  result_ptr = self._decay_array_value_to_pointer(name_ir, "derefarray")
1236
1794
  else:
1237
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
1238
1801
  result = self._safe_load(result_ptr)
1239
1802
  if self._is_unsigned_pointee(name_ir) or self._is_unsigned_pointee(
1240
1803
  result_ptr
@@ -1308,12 +1871,15 @@ class LLVMCodeGenerator(object):
1308
1871
  if isinstance(expr, c_ast.Typename):
1309
1872
  ir_t = self._resolve_ast_type(expr.type)
1310
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"))
1311
1878
  elif isinstance(expr, c_ast.ID):
1312
1879
  ir_type, _ = self.lookup(expr.name)
1313
1880
  size = self._ir_type_size(ir_type)
1314
1881
  else:
1315
- val, _ = self.codegen(expr)
1316
- semantic_type = self._get_expr_ir_type(expr, getattr(val, "type", None))
1882
+ semantic_type = self._infer_sizeof_operand_ir_type(expr)
1317
1883
  size = self._ir_type_size(semantic_type)
1318
1884
  result = ir.Constant(int64_t, size)
1319
1885
  return self._tag_unsigned(result)
@@ -1332,9 +1898,8 @@ class LLVMCodeGenerator(object):
1332
1898
  if isinstance(resolved, str):
1333
1899
  # Could be a __struct_ reference or a base type name
1334
1900
  if resolved.startswith("__struct_"):
1335
- struct_name = resolved[len("__struct_") :]
1336
- if struct_name in self.env:
1337
- return self.env[struct_name][0]
1901
+ if resolved in self.env:
1902
+ return self.env[resolved][0]
1338
1903
  return int8_t # opaque
1339
1904
  # Recursively resolve further typedefs
1340
1905
  return self._resolve_type_str(resolved, depth + 1)
@@ -1477,11 +2042,162 @@ class LLVMCodeGenerator(object):
1477
2042
  return None
1478
2043
  return self._scalar_init_node(init_node.exprs[0])
1479
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
+
1480
2194
  def _build_pointer_const(self, init_node, ir_type):
1481
2195
  if isinstance(init_node, c_ast.InitList):
1482
2196
  if init_node.exprs:
1483
2197
  return self._build_pointer_const(init_node.exprs[0], ir_type)
1484
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)
1485
2201
  if (
1486
2202
  isinstance(init_node, c_ast.Constant)
1487
2203
  and getattr(init_node, "type", None) == "string"
@@ -1502,22 +2218,23 @@ class LLVMCodeGenerator(object):
1502
2218
  return sym
1503
2219
  if isinstance(sym.type, ir.PointerType):
1504
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)
1505
2228
  if (
1506
2229
  isinstance(init_node, c_ast.UnaryOp)
1507
2230
  and init_node.op == "&"
1508
- and isinstance(init_node.expr, c_ast.ID)
1509
2231
  ):
1510
- try:
1511
- _, sym = self.lookup(init_node.expr.name)
1512
- except Exception:
1513
- sym = None
1514
- if isinstance(sym, ir.Function):
1515
- return sym if sym.type == ir_type else sym.bitcast(ir_type)
1516
- if isinstance(sym, ir.GlobalVariable):
1517
- if sym.type == ir_type:
1518
- return sym
1519
- if isinstance(sym.type, ir.PointerType):
1520
- 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)
1521
2238
  try:
1522
2239
  val = self._eval_const_expr(init_node)
1523
2240
  if val == 0:
@@ -1598,6 +2315,17 @@ class LLVMCodeGenerator(object):
1598
2315
  result -= 1 << bits
1599
2316
  return ir.Constant(int_type, result)
1600
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
+
1601
2329
  def _const_init_bytes(self, init_node, ir_type):
1602
2330
  size = self._ir_type_size(ir_type)
1603
2331
  if init_node is None:
@@ -1605,28 +2333,12 @@ class LLVMCodeGenerator(object):
1605
2333
 
1606
2334
  if getattr(ir_type, "is_union", False):
1607
2335
  raw = self._zero_bytes(size)
1608
- member_names = getattr(ir_type, "members", None) or list(
1609
- ir_type.member_types.keys()
2336
+ field_name, member_type, member_init = self._select_union_initializer(
2337
+ init_node, ir_type
1610
2338
  )
1611
- if not member_names:
2339
+ if field_name is None:
1612
2340
  return raw
1613
2341
 
1614
- first_name = member_names[0]
1615
- member_type = ir_type.member_types[first_name]
1616
- member_init = init_node
1617
- if isinstance(init_node, c_ast.InitList):
1618
- exprs = init_node.exprs or []
1619
- if not exprs:
1620
- member_init = None
1621
- elif isinstance(member_type, (ir.ArrayType, ir.LiteralStructType)):
1622
- member_init = (
1623
- exprs[0]
1624
- if len(exprs) == 1 and isinstance(exprs[0], c_ast.InitList)
1625
- else init_node
1626
- )
1627
- else:
1628
- member_init = exprs[0]
1629
-
1630
2342
  member_bytes = self._const_init_bytes(member_init, member_type)
1631
2343
  raw[: min(size, len(member_bytes))] = member_bytes[:size]
1632
2344
  return raw
@@ -1686,16 +2398,62 @@ class LLVMCodeGenerator(object):
1686
2398
 
1687
2399
  return self._zero_bytes(size)
1688
2400
 
1689
- 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
+
1690
2447
  raw = self._zero_bytes(size)
1691
2448
  if not isinstance(init_node, c_ast.InitList):
1692
2449
  return raw
1693
2450
 
2451
+ exprs = self._ordered_struct_init_exprs(init_node, ir_type)
1694
2452
  offset = 0
1695
2453
  for i, member_type in enumerate(ir_type.elements):
1696
2454
  align = self._ir_type_align(member_type)
1697
2455
  offset = (offset + align - 1) & ~(align - 1)
1698
- expr = init_node.exprs[i] if i < len(init_node.exprs) else None
2456
+ expr = exprs[i] if i < len(exprs) else None
1699
2457
  field_bytes = self._const_init_bytes(expr, member_type)
1700
2458
  field_size = self._ir_type_size(member_type)
1701
2459
  raw[offset : offset + field_size] = field_bytes[:field_size]
@@ -1767,11 +2525,36 @@ class LLVMCodeGenerator(object):
1767
2525
 
1768
2526
  return self._zero_initializer(ir_type)
1769
2527
 
1770
- 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)
1771
2553
  if isinstance(init_node, c_ast.InitList):
2554
+ exprs = self._ordered_struct_init_exprs(init_node, ir_type)
1772
2555
  values = []
1773
2556
  for i, member_type in enumerate(ir_type.elements):
1774
- expr = init_node.exprs[i] if i < len(init_node.exprs) else None
2557
+ expr = exprs[i] if i < len(exprs) else None
1775
2558
  values.append(self._build_const_init(expr, member_type))
1776
2559
  try:
1777
2560
  return ir.Constant(ir_type, values)
@@ -1796,49 +2579,249 @@ class LLVMCodeGenerator(object):
1796
2579
  """Recursively initialize array elements from an InitList."""
1797
2580
  for i, expr in enumerate(init_list.exprs):
1798
2581
  idx = prefix_idx + [ir.Constant(ir.IntType(32), i)]
1799
- if isinstance(expr, c_ast.InitList):
1800
- self._init_array(base_addr, expr, elem_ir_type, idx)
1801
- else:
1802
- val, _ = self.codegen(expr)
1803
- val = self._implicit_convert(val, elem_ir_type)
1804
- elem_ptr = self.builder.gep(base_addr, idx, inbounds=True)
1805
- self._safe_store(val, elem_ptr)
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)
1806
2587
 
1807
- def _build_array_ir_type(self, array_decl):
1808
- dims = []
1809
- node = array_decl
1810
- while isinstance(node, c_ast.ArrayDecl):
1811
- dims.append(self._eval_dim(node.dim) if node.dim else 0)
1812
- node = node.type
1813
- elem_ir_type = self._resolve_ast_type(node)
1814
- if isinstance(elem_ir_type, ir.VoidType):
1815
- elem_ir_type = int8_t
1816
- arr_ir_type = elem_ir_type
1817
- for dim in reversed(dims):
1818
- arr_ir_type = ir.ArrayType(arr_ir_type, dim)
1819
- arr_ir_type.dim_array = dims
1820
- return arr_ir_type
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
1821
2591
 
1822
- def _resolve_param_type(self, param):
1823
- """Resolve a function parameter type, handling typedefs and pointers."""
1824
- if isinstance(param.type, c_ast.ArrayDecl):
1825
- arr_type = self._build_array_ir_type(param.type)
1826
- return ir.PointerType(arr_type.element)
1827
- t = self._resolve_ast_type(param.type)
1828
- if isinstance(t, ir.ArrayType):
1829
- return ir.PointerType(t.element)
1830
- if isinstance(t, ir.VoidType):
1831
- return None # void params mean "no params" in C
1832
- return t
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
1833
2617
 
1834
- def _resolve_ast_type(self, node_type):
1835
- """Recursively resolve an AST type to IR type, with typedef support."""
1836
- if isinstance(node_type, c_ast.PtrDecl):
1837
- inner = node_type.type
1838
- if isinstance(inner, c_ast.FuncDecl):
1839
- return self._build_func_ptr_type(inner)
1840
- pointee = self._resolve_ast_type(inner)
1841
- if isinstance(pointee, ir.VoidType):
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
+ )
2765
+ else:
2766
+ member_init = first_expr
2767
+
2768
+ return field_name, field_type, member_init
2769
+
2770
+ def _build_array_ir_type(self, array_decl, init_node=None):
2771
+ dims = []
2772
+ node = array_decl
2773
+ inferred_top_dim = self._infer_array_count_from_initializer(init_node)
2774
+ is_top_level = True
2775
+ while isinstance(node, c_ast.ArrayDecl):
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)
2780
+ node = node.type
2781
+ is_top_level = False
2782
+ elem_ir_type = self._resolve_ast_type(node)
2783
+ if isinstance(elem_ir_type, ir.VoidType):
2784
+ elem_ir_type = int8_t
2785
+ arr_ir_type = elem_ir_type
2786
+ for dim in reversed(dims):
2787
+ arr_ir_type = ir.ArrayType(arr_ir_type, dim)
2788
+ arr_ir_type.dim_array = dims
2789
+ return arr_ir_type
2790
+
2791
+ def _resolve_param_type(self, param):
2792
+ """Resolve a function parameter type, handling typedefs and pointers."""
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)
2803
+ if isinstance(t, ir.ArrayType):
2804
+ return ir.PointerType(t.element)
2805
+ if isinstance(t, ir.VoidType):
2806
+ return None # void params mean "no params" in C
2807
+ return t
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
+
2817
+ def _resolve_ast_type(self, node_type):
2818
+ """Recursively resolve an AST type to IR type, with typedef support."""
2819
+ if isinstance(node_type, c_ast.PtrDecl):
2820
+ inner = node_type.type
2821
+ if isinstance(inner, c_ast.FuncDecl):
2822
+ return self._build_func_ptr_type(inner)
2823
+ pointee = self._resolve_ast_type(inner)
2824
+ if isinstance(pointee, ir.VoidType):
1842
2825
  return voidptr_t
1843
2826
  return ir.PointerType(pointee)
1844
2827
  elif isinstance(node_type, c_ast.TypeDecl):
@@ -1848,6 +2831,9 @@ class LLVMCodeGenerator(object):
1848
2831
  return self.codegen_Struct(node_type.type)
1849
2832
  elif isinstance(node_type.type, c_ast.Union):
1850
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
1851
2837
  return int64_t
1852
2838
  elif isinstance(node_type, c_ast.ArrayDecl):
1853
2839
  return voidptr_t
@@ -1862,26 +2848,74 @@ class LLVMCodeGenerator(object):
1862
2848
  return int(v, 0) # handles hex/octal/decimal
1863
2849
  return self._eval_const_expr(dim_node)
1864
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
+
1865
2864
  def _build_func_ptr_type(self, func_decl_node):
1866
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."""
1867
2871
  ret_ir, _ = self.codegen(func_decl_node)
1868
2872
  param_types = []
2873
+ is_var_arg = False
1869
2874
  if func_decl_node.args:
1870
2875
  for param in func_decl_node.args.params:
1871
2876
  if isinstance(param, c_ast.EllipsisParam):
2877
+ is_var_arg = True
1872
2878
  continue
1873
- if isinstance(param, c_ast.Typename):
1874
- t = self._resolve_ast_type(param.type)
1875
- if not isinstance(t, ir.VoidType):
1876
- param_types.append(t)
1877
- elif isinstance(param, c_ast.Decl):
2879
+ if isinstance(param, (c_ast.Typename, c_ast.Decl)):
1878
2880
  t = self._resolve_param_type(param)
1879
2881
  if t is not None:
1880
2882
  param_types.append(t)
1881
2883
  if isinstance(ret_ir, ir.VoidType):
1882
2884
  ret_ir = ir.VoidType()
1883
- func_type = ir.FunctionType(ret_ir, param_types)
1884
- return func_type.as_pointer()
2885
+ return ir.FunctionType(ret_ir, param_types, var_arg=is_var_arg), ret_ir
2886
+
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
1885
2919
 
1886
2920
  def _safe_load(self, ptr, name=""):
1887
2921
  """Load from ptr, guard against non-pointer types."""
@@ -1907,9 +2941,26 @@ class LLVMCodeGenerator(object):
1907
2941
  gv.global_constant = True
1908
2942
  gv.linkage = "internal"
1909
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)
1910
2947
  idx0 = ir.Constant(ir.IntType(32), 0)
1911
2948
  return self.builder.gep(base, [idx0, idx0], name=name)
1912
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
+
1913
2964
  def _safe_store(self, value, ptr):
1914
2965
  """Store value to ptr, auto-converting types if needed."""
1915
2966
  if value is None or ptr is None:
@@ -1929,11 +2980,9 @@ class LLVMCodeGenerator(object):
1929
2980
  """Convert val to target_type if needed (implicit C promotion/truncation)."""
1930
2981
  if val is None or isinstance(val.type, ir.VoidType):
1931
2982
  # Can't convert void — return a zero of target type
1932
- if isinstance(target_type, ir.PointerType):
1933
- return ir.Constant(target_type, None)
1934
- elif isinstance(target_type, ir.VoidType):
2983
+ if isinstance(target_type, ir.VoidType):
1935
2984
  return val
1936
- return ir.Constant(target_type, 0)
2985
+ return self._zero_initializer(target_type)
1937
2986
  if val.type == target_type:
1938
2987
  return val
1939
2988
  if isinstance(val.type, ir.IntType) and self._is_floating_ir_type(target_type):
@@ -1994,6 +3043,49 @@ class LLVMCodeGenerator(object):
1994
3043
  return self.builder.bitcast(ptr, target_type)
1995
3044
  return val
1996
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
+
1997
3089
  def _extend_call_result(self, result, returns_unsigned=False):
1998
3090
  if not isinstance(result.type, ir.IntType):
1999
3091
  return result
@@ -2019,6 +3111,9 @@ class LLVMCodeGenerator(object):
2019
3111
 
2020
3112
  def _ir_type_align(self, ir_type):
2021
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
2022
3117
  if isinstance(ir_type, ir.IntType):
2023
3118
  return min(ir_type.width // 8, 8)
2024
3119
  elif isinstance(ir_type, ir.FloatType):
@@ -2029,7 +3124,7 @@ class LLVMCodeGenerator(object):
2029
3124
  return 8
2030
3125
  elif isinstance(ir_type, ir.ArrayType):
2031
3126
  return self._ir_type_align(ir_type.element)
2032
- elif isinstance(ir_type, ir.LiteralStructType):
3127
+ elif _is_struct_ir_type(ir_type):
2033
3128
  if not ir_type.elements:
2034
3129
  return 1
2035
3130
  return max(self._ir_type_align(e) for e in ir_type.elements)
@@ -2037,6 +3132,9 @@ class LLVMCodeGenerator(object):
2037
3132
 
2038
3133
  def _ir_type_size(self, ir_type):
2039
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
2040
3138
  if isinstance(ir_type, ir.IntType):
2041
3139
  return ir_type.width // 8
2042
3140
  elif isinstance(ir_type, ir.FloatType):
@@ -2047,7 +3145,7 @@ class LLVMCodeGenerator(object):
2047
3145
  return 8
2048
3146
  elif isinstance(ir_type, ir.ArrayType):
2049
3147
  return int(ir_type.count) * self._ir_type_size(ir_type.element)
2050
- elif isinstance(ir_type, ir.LiteralStructType):
3148
+ elif _is_struct_ir_type(ir_type):
2051
3149
  offset = 0
2052
3150
  for elem in ir_type.elements:
2053
3151
  align = self._ir_type_align(elem)
@@ -2059,6 +3157,232 @@ class LLVMCodeGenerator(object):
2059
3157
  return offset
2060
3158
  return 8
2061
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
+
2062
3386
  def _refine_member_ir_type(self, aggregate_type, member_key, field_type):
2063
3387
  """Prefer semantic member types over storage types when available."""
2064
3388
  semantic_field_type = field_type
@@ -2083,8 +3407,8 @@ class LLVMCodeGenerator(object):
2083
3407
  resolved, ir.PointerType
2084
3408
  ):
2085
3409
  return semantic_field_type
2086
- if isinstance(
2087
- resolved, (ir.ArrayType, ir.LiteralStructType, ir.PointerType)
3410
+ if isinstance(resolved, (ir.ArrayType, ir.PointerType)) or _is_struct_ir_type(
3411
+ resolved
2088
3412
  ):
2089
3413
  return resolved
2090
3414
  except Exception:
@@ -2101,6 +3425,14 @@ class LLVMCodeGenerator(object):
2101
3425
  )
2102
3426
  return 0, semantic_field_type
2103
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
+
2104
3436
  if not hasattr(aggregate_type, "members"):
2105
3437
  raise CodegenError(f"Aggregate has no named fields: {aggregate_type}")
2106
3438
 
@@ -2145,6 +3477,153 @@ class LLVMCodeGenerator(object):
2145
3477
 
2146
3478
  raise CodegenError(f"Not an offsetof base: {type(node).__name__}")
2147
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
+
2148
3627
  def codegen_Typename(self, node):
2149
3628
  # Used inside sizeof(type) — not directly code-generated
2150
3629
  return None, None
@@ -2160,6 +3639,8 @@ class LLVMCodeGenerator(object):
2160
3639
  rhs, _ = self.codegen(node.right)
2161
3640
  if lhs is None or rhs is None:
2162
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")
2163
3644
 
2164
3645
  # Pointer arithmetic: ptr + int or ptr - int
2165
3646
  if (
@@ -2299,7 +3780,10 @@ class LLVMCodeGenerator(object):
2299
3780
  self._tag_unsigned(result)
2300
3781
  return result, None
2301
3782
  elif node.op == "^":
2302
- return self.builder.xor(lhs, rhs, "xortmp"), None
3783
+ result = self.builder.xor(lhs, rhs, "xortmp")
3784
+ if is_unsigned:
3785
+ self._tag_unsigned(result)
3786
+ return result, None
2303
3787
  elif node.op == "<<":
2304
3788
  result = self.builder.shl(lhs, rhs, "shltmp")
2305
3789
  if is_unsigned:
@@ -2307,7 +3791,9 @@ class LLVMCodeGenerator(object):
2307
3791
  return result, None
2308
3792
  elif node.op == ">>":
2309
3793
  if is_unsigned:
2310
- return self.builder.lshr(lhs, rhs, "shrtmp"), None
3794
+ result = self.builder.lshr(lhs, rhs, "shrtmp")
3795
+ self._tag_unsigned(result)
3796
+ return result, None
2311
3797
  return self.builder.ashr(lhs, rhs, "shrtmp"), None
2312
3798
  else:
2313
3799
  func = self.module.globals.get("binary{0}".format(node.op))
@@ -2407,8 +3893,7 @@ class LLVMCodeGenerator(object):
2407
3893
 
2408
3894
  # append by name nor just add it
2409
3895
  after_loop_label = self.new_label("afterloop")
2410
- after_bb = ir.Block(self.builder.function, after_loop_label)
2411
- # self.builder.function.append_basic_block('afterloop')
3896
+ after_bb = self.builder.function.append_basic_block(after_loop_label)
2412
3897
 
2413
3898
  self.builder.branch(test_bb)
2414
3899
  self.builder.position_at_end(test_bb)
@@ -2432,9 +3917,6 @@ class LLVMCodeGenerator(object):
2432
3917
  if node.next is not None:
2433
3918
  self.codegen(node.next)
2434
3919
  self.builder.branch(test_bb)
2435
- # this append_basic_blook change the label
2436
- # after_bb = self.builder.function.append_basic_block(after_loop_label)
2437
- self.builder.function.basic_blocks.append(after_bb)
2438
3920
  self.builder.position_at_end(after_bb)
2439
3921
 
2440
3922
  return ir.values.Constant(ir.DoubleType(), 0.0), None
@@ -2472,11 +3954,17 @@ class LLVMCodeGenerator(object):
2472
3954
  return ir.values.Constant(ir.DoubleType(), 0.0)
2473
3955
 
2474
3956
  def codegen_Break(self, node):
2475
- self.builder.branch(self.lookup("break"))
3957
+ target = self.lookup("break")
3958
+ if isinstance(target, tuple):
3959
+ target = target[1]
3960
+ self.builder.branch(target)
2476
3961
  return None, None
2477
3962
 
2478
3963
  def codegen_Continue(self, node):
2479
- self.builder.branch(self.lookup("continue"))
3964
+ target = self.lookup("continue")
3965
+ if isinstance(target, tuple):
3966
+ target = target[1]
3967
+ self.builder.branch(target)
2480
3968
  return None, None
2481
3969
 
2482
3970
  def codegen_DoWhile(self, node):
@@ -2514,8 +4002,8 @@ class LLVMCodeGenerator(object):
2514
4002
  cond_val = self.builder.ptrtoint(cond_val, int64_t)
2515
4003
  elif self._is_floating_ir_type(cond_val.type):
2516
4004
  cond_val = self.builder.fptosi(cond_val, int64_t)
2517
- elif isinstance(cond_val.type, ir.IntType) and cond_val.type.width != 64:
2518
- 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)
2519
4007
 
2520
4008
  after_bb = self.builder.function.append_basic_block("switch_end")
2521
4009
 
@@ -2528,11 +4016,57 @@ class LLVMCodeGenerator(object):
2528
4016
  switch_items = [node.stmt]
2529
4017
  else:
2530
4018
  switch_items = []
2531
- labels = [
2532
- item
2533
- for item in switch_items
2534
- if isinstance(item, (c_ast.Case, c_ast.Default))
2535
- ]
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)
2536
4070
 
2537
4071
  label_blocks = {}
2538
4072
  default_bb = after_bb
@@ -2545,11 +4079,21 @@ class LLVMCodeGenerator(object):
2545
4079
  if isinstance(item, c_ast.Default):
2546
4080
  default_bb = bb
2547
4081
 
2548
- switch_inst = self.builder.switch(cond_val, default_bb)
2549
-
2550
4082
  with self.new_scope():
2551
4083
  self.define("break", after_bb)
2552
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
+
2553
4097
  for item in labels:
2554
4098
  if not isinstance(item, c_ast.Case):
2555
4099
  continue
@@ -2570,10 +4114,12 @@ class LLVMCodeGenerator(object):
2570
4114
 
2571
4115
  for idx, item in enumerate(labels):
2572
4116
  self.builder.position_at_end(label_blocks[id(item)])
2573
- for stmt in item.stmts or []:
2574
- self.codegen(stmt)
4117
+ for stmt in label_bodies.get(id(item), []):
2575
4118
  if self.builder.block.is_terminated:
2576
- break
4119
+ if isinstance(stmt, c_ast.Label):
4120
+ self.codegen(stmt)
4121
+ continue
4122
+ self.codegen(stmt)
2577
4123
  if not self.builder.block.is_terminated:
2578
4124
  next_bb = after_bb
2579
4125
  if idx + 1 < len(labels):
@@ -2595,7 +4141,10 @@ class LLVMCodeGenerator(object):
2595
4141
  self.builder.cbranch(cmp, then_bb, else_bb)
2596
4142
 
2597
4143
  self.builder.position_at_end(then_bb)
2598
- 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)
2599
4148
  true_bb_end = self.builder.block
2600
4149
 
2601
4150
  self.builder.position_at_end(else_bb)
@@ -2603,11 +4152,7 @@ class LLVMCodeGenerator(object):
2603
4152
  false_bb_end = self.builder.block
2604
4153
 
2605
4154
  def zero_value(target_type):
2606
- if isinstance(target_type, ir.PointerType):
2607
- return ir.Constant(target_type, None)
2608
- if self._is_floating_ir_type(target_type):
2609
- return ir.Constant(target_type, 0.0)
2610
- return ir.Constant(target_type, 0)
4155
+ return self._zero_initializer(target_type)
2611
4156
 
2612
4157
  def pick_target_type(lhs, rhs):
2613
4158
  if lhs is None and rhs is None:
@@ -2666,19 +4211,48 @@ class LLVMCodeGenerator(object):
2666
4211
  self.builder.position_at_end(merge_bb)
2667
4212
  if not incoming:
2668
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
+ )
2669
4223
  if len(incoming) == 1:
2670
- 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
2671
4232
 
2672
4233
  phi = self.builder.phi(target, "ternary")
2673
4234
  for pred, value in incoming:
2674
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)
2675
4242
  return phi, None
2676
4243
 
2677
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)
2678
4252
 
2679
4253
  expr, ptr = self.codegen(node.expr)
2680
4254
 
2681
- dest_ir_type = self._resolve_ast_type(node.to_type.type)
4255
+ self._validate_explicit_cast(expr.type, dest_ir_type)
2682
4256
  # Check if casting to unsigned type
2683
4257
  is_unsigned = False
2684
4258
  if isinstance(node.to_type.type, c_ast.TypeDecl) and isinstance(
@@ -2727,6 +4301,9 @@ class LLVMCodeGenerator(object):
2727
4301
  self._tag_value_from_decl_type(result, node.to_type.type)
2728
4302
  return result, None
2729
4303
 
4304
+ def codegen_CompoundLiteral(self, node):
4305
+ return self._materialize_compound_literal(node.type.type, node.init)
4306
+
2730
4307
  def codegen_FuncCall(self, node):
2731
4308
 
2732
4309
  callee = None
@@ -2738,13 +4315,111 @@ class LLVMCodeGenerator(object):
2738
4315
  return self._codegen_builtin_va_end(node)
2739
4316
  if callee == "__builtin_va_copy":
2740
4317
  return self._codegen_builtin_va_copy(node)
4318
+ if callee == "__builtin_alloca":
4319
+ return self._codegen_builtin_alloca(node)
2741
4320
  if callee == "__builtin_va_arg":
2742
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)
2743
4416
  else:
2744
4417
  # Calling function pointer in struct: s.fn(args)
2745
4418
  call_args = []
4419
+ arg_nodes = []
2746
4420
  if node.args:
2747
- 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]
2748
4423
  fp_val, _ = self.codegen(node.name)
2749
4424
  if isinstance(fp_val.type, ir.PointerType) and isinstance(
2750
4425
  fp_val.type.pointee, ir.FunctionType
@@ -2753,10 +4428,15 @@ class LLVMCodeGenerator(object):
2753
4428
  ftype = fp_val.type.pointee
2754
4429
  coerced = []
2755
4430
  for j, a in enumerate(call_args):
4431
+ arg_node = arg_nodes[j] if j < len(arg_nodes) else None
2756
4432
  if j < len(ftype.args):
2757
- 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
+ )
2758
4436
  else:
2759
- coerced.append(a)
4437
+ coerced.append(
4438
+ self._default_arg_promotion(a, arg_node=arg_node)
4439
+ )
2760
4440
  call_args = coerced
2761
4441
  ret_type = ftype.return_type
2762
4442
  if isinstance(ret_type, ir.VoidType):
@@ -2775,8 +4455,10 @@ class LLVMCodeGenerator(object):
2775
4455
  _, callee_func = self.lookup(callee)
2776
4456
 
2777
4457
  call_args = []
4458
+ arg_nodes = []
2778
4459
  if node.args:
2779
- 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]
2780
4462
 
2781
4463
  # Function pointer: load the pointer and call through it
2782
4464
  if not isinstance(callee_func, ir.Function):
@@ -2794,7 +4476,16 @@ class LLVMCodeGenerator(object):
2794
4476
  ):
2795
4477
  ftype = func_val.type.pointee
2796
4478
  coerced = [
2797
- 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
+ )
2798
4489
  for j, a in enumerate(call_args)
2799
4490
  ]
2800
4491
  ret_type = ftype.return_type
@@ -2815,7 +4506,9 @@ class LLVMCodeGenerator(object):
2815
4506
  return ir.Constant(int64_t, 0), None
2816
4507
 
2817
4508
  # Convert arguments to match function parameter types
2818
- 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
+ )
2819
4512
 
2820
4513
  # Call and handle return type
2821
4514
  try:
@@ -2845,11 +4538,18 @@ class LLVMCodeGenerator(object):
2845
4538
  return existing
2846
4539
  return ir.Function(self.module, ir.FunctionType(ret_type, arg_types), name=name)
2847
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
+
2848
4548
  def _codegen_builtin_va_start(self, node):
2849
4549
  if not node.args or not node.args.exprs:
2850
4550
  return ir.Constant(int64_t, 0), None
2851
- ap_addr, _ = self.codegen(node.args.exprs[0])
2852
- 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:
2853
4553
  return ir.Constant(int64_t, 0), None
2854
4554
  intrinsic = self._get_or_declare_intrinsic(
2855
4555
  "llvm.va_start", ir.VoidType(), [voidptr_t]
@@ -2863,8 +4563,8 @@ class LLVMCodeGenerator(object):
2863
4563
  def _codegen_builtin_va_end(self, node):
2864
4564
  if not node.args or not node.args.exprs:
2865
4565
  return ir.Constant(int64_t, 0), None
2866
- ap_addr, _ = self.codegen(node.args.exprs[0])
2867
- 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:
2868
4568
  return ir.Constant(int64_t, 0), None
2869
4569
  intrinsic = self._get_or_declare_intrinsic(
2870
4570
  "llvm.va_end", ir.VoidType(), [voidptr_t]
@@ -2878,11 +4578,11 @@ class LLVMCodeGenerator(object):
2878
4578
  def _codegen_builtin_va_copy(self, node):
2879
4579
  if not node.args or len(node.args.exprs) < 2:
2880
4580
  return ir.Constant(int64_t, 0), None
2881
- dst_addr, _ = self.codegen(node.args.exprs[0])
2882
- src_addr, _ = self.codegen(node.args.exprs[1])
2883
- 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:
2884
4584
  return ir.Constant(int64_t, 0), None
2885
- if not isinstance(getattr(src_addr, "type", None), ir.PointerType):
4585
+ if src_addr is None:
2886
4586
  return ir.Constant(int64_t, 0), None
2887
4587
  src_val = self._safe_load(src_addr)
2888
4588
  dst_pointee = dst_addr.type.pointee
@@ -2891,24 +4591,429 @@ class LLVMCodeGenerator(object):
2891
4591
  self._safe_store(src_val, dst_addr)
2892
4592
  return ir.Constant(int64_t, 0), None
2893
4593
 
2894
- 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):
2895
4998
  """Convert call arguments to match function parameter types."""
2896
4999
  converted = []
2897
5000
  param_types = [p.type for p in callee_func.args]
2898
5001
 
2899
5002
  for i, arg in enumerate(call_args):
5003
+ arg_node = arg_nodes[i] if arg_nodes and i < len(arg_nodes) else None
2900
5004
  if i < len(param_types):
2901
5005
  expected = param_types[i]
2902
- arg = self._coerce_arg(arg, expected)
5006
+ arg = self._coerce_arg(arg, expected, arg_node=arg_node)
2903
5007
  else:
2904
- arg = self._default_arg_promotion(arg)
5008
+ arg = self._default_arg_promotion(arg, arg_node=arg_node)
2905
5009
  converted.append(arg)
2906
5010
  return converted
2907
5011
 
2908
- def _default_arg_promotion(self, arg):
5012
+ def _default_arg_promotion(self, arg, arg_node=None):
2909
5013
  """Apply C default argument promotions for variadic calls."""
2910
5014
  if arg is None or isinstance(getattr(arg, "type", None), ir.VoidType):
2911
5015
  return ir.Constant(int64_t, 0)
5016
+ arg = self._decay_array_expr_to_pointer(arg_node, arg, "varargarraydecay")
2912
5017
  if isinstance(arg.type, ir.ArrayType):
2913
5018
  return self._implicit_convert(arg, ir.PointerType(arg.type.element))
2914
5019
  if isinstance(arg.type, ir.FloatType):
@@ -2917,7 +5022,7 @@ class LLVMCodeGenerator(object):
2917
5022
  return self._integer_promotion(arg)
2918
5023
  return arg
2919
5024
 
2920
- def _coerce_arg(self, arg, expected):
5025
+ def _coerce_arg(self, arg, expected, arg_node=None):
2921
5026
  """Coerce a single argument to the expected type."""
2922
5027
  if arg is None or isinstance(getattr(arg, "type", None), ir.VoidType):
2923
5028
  return (
@@ -2925,16 +5030,13 @@ class LLVMCodeGenerator(object):
2925
5030
  if isinstance(expected, ir.PointerType)
2926
5031
  else ir.Constant(int64_t, 0)
2927
5032
  )
5033
+ arg = self._decay_array_expr_to_pointer(arg_node, arg, "argarraydecay")
2928
5034
  if arg.type == expected:
2929
5035
  return arg
2930
- # 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.
2931
5038
  if isinstance(arg.type, ir.ArrayType) and isinstance(expected, ir.PointerType):
2932
- gv = ir.GlobalVariable(
2933
- self.module, arg.type, self.module.get_unique_name("str")
2934
- )
2935
- gv.initializer = arg
2936
- gv.global_constant = True
2937
- return self.builder.bitcast(gv, expected)
5039
+ return self._implicit_convert(arg, expected)
2938
5040
  # Pointer -> different pointer: bitcast
2939
5041
  if isinstance(arg.type, ir.PointerType) and isinstance(
2940
5042
  expected, ir.PointerType
@@ -2957,19 +5059,45 @@ class LLVMCodeGenerator(object):
2957
5059
  ):
2958
5060
  return None, None
2959
5061
 
2960
- # 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
2961
5080
  is_static = node.storage and "static" in node.storage
2962
- if is_static and not self.in_global and isinstance(node.type, c_ast.TypeDecl):
2963
- type_str = node.type.type.names
2964
- 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)
2965
5083
  # Create unique global name
2966
- global_name = f"__static_{self.function.name}_{node.name}"
2967
- gv = ir.GlobalVariable(self.module, ir_type, global_name)
5084
+ global_name = self._static_local_symbol_name(node.name)
5085
+ gv = self._create_bound_global(node.name, ir_type, symbol_name=global_name)
5086
+ gv.linkage = "internal"
2968
5087
  if node.init:
2969
5088
  gv.initializer = self._build_const_init(node.init, ir_type)
2970
5089
  else:
2971
5090
  gv.initializer = self._zero_initializer(ir_type)
2972
- self.define(node.name, (ir_type, gv))
5091
+ return None, None
5092
+
5093
+ if self._is_global_extern_decl(node):
5094
+ ir_type = self._extern_decl_ir_type(node.name, node.type)
5095
+ self._prepare_file_scope_object(
5096
+ node.name,
5097
+ ir_type,
5098
+ storage=node.storage,
5099
+ has_initializer=False,
5100
+ )
2973
5101
  return None, None
2974
5102
 
2975
5103
  if isinstance(node.type, c_ast.Enum):
@@ -2978,39 +5106,38 @@ class LLVMCodeGenerator(object):
2978
5106
  # Forward function declaration: int foo(int x);
2979
5107
  if isinstance(node.type, c_ast.FuncDecl):
2980
5108
  funcname = node.name
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
+ )
5115
+ symbol_name = self._register_file_scope_function(
5116
+ funcname,
5117
+ function_type,
5118
+ storage=node.storage,
5119
+ funcspec=node.funcspec,
5120
+ is_definition=False,
5121
+ )
2981
5122
  # Skip if already exists (module globals, libc, or env)
2982
- existing = self.module.globals.get(funcname)
5123
+ existing = self.module.globals.get(symbol_name)
2983
5124
  if existing:
2984
5125
  if self._func_decl_returns_unsigned(node.type):
2985
5126
  self._mark_unsigned_return(existing)
2986
5127
  self.define(funcname, (None, existing))
2987
5128
  return None, None
2988
- if funcname in LIBC_FUNCTIONS:
2989
- self._declare_libc(funcname)
2990
- return None, None
2991
- ir_type, _ = self.codegen(node.type)
2992
- arg_types = []
2993
- is_va = False
2994
- if node.type.args:
2995
- for arg in node.type.args.params:
2996
- if isinstance(arg, c_ast.EllipsisParam):
2997
- is_va = True
2998
- continue
2999
- t = self._resolve_param_type(arg)
3000
- if t is not None:
3001
- arg_types.append(t)
3002
5129
  try:
3003
5130
  func = ir.Function(
3004
5131
  self.module,
3005
- ir.FunctionType(ir_type, arg_types, var_arg=is_va),
3006
- name=funcname,
5132
+ function_type,
5133
+ name=symbol_name,
3007
5134
  )
3008
5135
  if self._func_decl_returns_unsigned(node.type):
3009
5136
  self._mark_unsigned_return(func)
3010
5137
  self.define(funcname, (ir_type, func))
3011
5138
  except Exception:
3012
5139
  # Already exists (libc or previous decl)
3013
- existing = self.module.globals.get(funcname)
5140
+ existing = self.module.globals.get(symbol_name)
3014
5141
  if existing:
3015
5142
  if self._func_decl_returns_unsigned(node.type):
3016
5143
  self._mark_unsigned_return(existing)
@@ -3031,8 +5158,29 @@ class LLVMCodeGenerator(object):
3031
5158
  if isinstance(node.type.type, c_ast.IdentifierType):
3032
5159
  # Check if the type resolves to a struct or pointer via typedef
3033
5160
  resolved = self._resolve_type_str(node.type.type.names)
3034
- if isinstance(
3035
- 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
3036
5184
  ):
3037
5185
  name = node.type.declname
3038
5186
  ir_type = resolved
@@ -3040,18 +5188,36 @@ class LLVMCodeGenerator(object):
3040
5188
  ret = self._alloca_in_entry(ir_type, name)
3041
5189
  self.define(name, (ir_type, ret))
3042
5190
  else:
3043
- ret = ir.GlobalVariable(self.module, ir_type, name)
3044
- self.define(name, (ir_type, ret))
5191
+ ret, write_initializer = self._prepare_file_scope_object(
5192
+ name,
5193
+ ir_type,
5194
+ storage=node.storage,
5195
+ has_initializer=node.init is not None,
5196
+ )
5197
+ if ret is None:
5198
+ return None, None
3045
5199
  if node.init is not None:
3046
5200
  if self.in_global:
3047
- ret.initializer = self._build_const_init(node.init, ir_type)
5201
+ if write_initializer:
5202
+ ret.initializer = self._build_const_init(
5203
+ node.init, ir_type
5204
+ )
3048
5205
  else:
3049
- init_val, _ = self.codegen(node.init)
3050
- if init_val is not None:
3051
- if init_val.type != ir_type:
3052
- init_val = self._implicit_convert(init_val, ir_type)
3053
- self._safe_store(init_val, ret)
3054
- elif self.in_global:
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)
5220
+ elif self.in_global and write_initializer:
3055
5221
  ret.initializer = self._zero_initializer(ir_type)
3056
5222
  return None, None
3057
5223
 
@@ -3062,61 +5228,107 @@ class LLVMCodeGenerator(object):
3062
5228
  if isinstance(node.type.type, c_ast.Union)
3063
5229
  else self.codegen_Struct
3064
5230
  )
3065
- 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:
3066
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))
3067
5240
  if not self.in_global:
3068
5241
  ret = self._alloca_in_entry(struct_type, name)
3069
5242
  self.define(name, (struct_type, ret))
3070
5243
  else:
3071
- ret = ir.GlobalVariable(self.module, struct_type, name)
3072
- self.define(name, (struct_type, ret))
5244
+ ret, write_initializer = self._prepare_file_scope_object(
5245
+ name,
5246
+ struct_type,
5247
+ storage=node.storage,
5248
+ has_initializer=node.init is not None,
5249
+ )
5250
+ if ret is None:
5251
+ return None, None
3073
5252
  if node.init is not None:
3074
5253
  if self.in_global:
3075
- ret.initializer = self._build_const_init(
3076
- node.init, struct_type
3077
- )
5254
+ if write_initializer:
5255
+ ret.initializer = self._build_const_init(
5256
+ node.init, struct_type
5257
+ )
3078
5258
  else:
3079
- init_val, _ = self.codegen(node.init)
3080
- if init_val is not None:
3081
- if init_val.type != struct_type:
3082
- init_val = self._implicit_convert(
3083
- init_val, struct_type
3084
- )
3085
- self._safe_store(init_val, ret)
3086
- elif self.in_global:
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)
5274
+ elif self.in_global and write_initializer:
3087
5275
  ret.initializer = self._zero_initializer(struct_type)
3088
5276
  return None, None
3089
5277
  else:
3090
- 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]
3091
5281
  if not self.in_global:
3092
5282
  ret = self._alloca_in_entry(struct_type, name)
3093
5283
  self.define(name, (struct_type, ret))
3094
5284
  else:
3095
- ret = ir.GlobalVariable(self.module, struct_type, name)
3096
- self.define(name, (struct_type, ret))
5285
+ ret, write_initializer = self._prepare_file_scope_object(
5286
+ name,
5287
+ struct_type,
5288
+ storage=node.storage,
5289
+ has_initializer=node.init is not None,
5290
+ )
5291
+ if ret is None:
5292
+ return None, None
3097
5293
  if node.init is not None:
3098
5294
  if self.in_global:
3099
- ret.initializer = self._build_const_init(
3100
- node.init, struct_type
3101
- )
5295
+ if write_initializer:
5296
+ ret.initializer = self._build_const_init(
5297
+ node.init, struct_type
5298
+ )
3102
5299
  else:
3103
- init_val, _ = self.codegen(node.init)
3104
- if init_val is not None:
3105
- if init_val.type != struct_type:
3106
- init_val = self._implicit_convert(
3107
- init_val, struct_type
3108
- )
3109
- self._safe_store(init_val, ret)
3110
- elif self.in_global:
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)
5315
+ elif self.in_global and write_initializer:
3111
5316
  ret.initializer = self._zero_initializer(struct_type)
3112
5317
  return None, None
3113
5318
  else:
3114
- type_str = node.type.type.names
3115
- is_unsigned = self._is_unsigned_type_names(type_str)
3116
- ir_type = self._get_ir_type(type_str)
3117
- type_str = self._resolve_type_str(type_str)
3118
- if isinstance(type_str, ir.Type):
3119
- 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)
3120
5332
  if self._is_floating_ir_type(ir_type):
3121
5333
  init = 0.0
3122
5334
  else:
@@ -3126,21 +5338,37 @@ class LLVMCodeGenerator(object):
3126
5338
  if self.in_global:
3127
5339
  init_val = self._build_const_init(node.init, ir_type)
3128
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)
3129
5346
  init_val, _ = self.codegen(node.init)
3130
5347
  else:
3131
5348
  init_val = self._zero_initializer(ir_type)
3132
-
3133
- var_addr, var_ir_type = self.create_entry_block_alloca(
3134
- node.name, type_str, 1
3135
- )
3136
- if is_unsigned:
3137
- self._mark_unsigned(var_addr)
3138
-
3139
5349
  if self.in_global:
3140
- var_addr.initializer = init_val
5350
+ var_addr, write_initializer = self._prepare_file_scope_object(
5351
+ node.name,
5352
+ ir_type,
5353
+ storage=node.storage,
5354
+ has_initializer=node.init is not None,
5355
+ )
5356
+ if var_addr is None:
5357
+ return None, None
5358
+ var_ir_type = ir_type
5359
+ if write_initializer:
5360
+ var_addr.initializer = init_val
3141
5361
  else:
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)
3142
5368
  init_val = self._implicit_convert(init_val, ir_type)
3143
5369
  self._safe_store(init_val, var_addr)
5370
+ if self.in_global and is_unsigned:
5371
+ self._mark_unsigned(var_addr)
3144
5372
 
3145
5373
  elif isinstance(node.type, c_ast.ArrayDecl):
3146
5374
  array_list = []
@@ -3148,27 +5376,43 @@ class LLVMCodeGenerator(object):
3148
5376
  var_addr = None
3149
5377
  var_ir_type = None
3150
5378
  elem_ir_type = None
5379
+ write_initializer = True
5380
+ inferred_top_dim = self._infer_array_count_from_initializer(node.init)
3151
5381
  while True:
3152
5382
  array_next_type = array_node.type
3153
5383
  if isinstance(array_next_type, c_ast.TypeDecl):
3154
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
3155
5391
  array_list.append(dim_val)
3156
5392
  elem_ir_type = self._resolve_ast_type(array_next_type)
3157
5393
  break
3158
5394
 
3159
5395
  elif isinstance(array_next_type, c_ast.ArrayDecl):
3160
- 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)
3161
5404
  array_node = array_next_type
3162
5405
  continue
3163
5406
  elif isinstance(array_next_type, c_ast.PtrDecl):
3164
5407
  # Array of pointers: int *arr[3]
3165
5408
  dim = self._eval_dim(array_node.dim)
3166
- inner = array_next_type.type
3167
- if isinstance(inner, c_ast.TypeDecl):
3168
- elem_type_str = inner.type.names
3169
- else:
3170
- elem_type_str = "int"
3171
- 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)
3172
5416
  elem_ir_type = elem_ir
3173
5417
  arr_ir = ir.ArrayType(elem_ir, dim)
3174
5418
  arr_ir.dim_array = [dim]
@@ -3176,12 +5420,14 @@ class LLVMCodeGenerator(object):
3176
5420
  var_addr = self._alloca_in_entry(arr_ir, node.name)
3177
5421
  self.define(node.name, (arr_ir, var_addr))
3178
5422
  else:
3179
- existing = self.module.globals.get(node.name)
3180
- if existing:
3181
- var_addr = existing
3182
- else:
3183
- var_addr = ir.GlobalVariable(self.module, arr_ir, node.name)
3184
- self.define(node.name, (arr_ir, var_addr))
5423
+ var_addr, write_initializer = self._prepare_file_scope_object(
5424
+ node.name,
5425
+ arr_ir,
5426
+ storage=node.storage,
5427
+ has_initializer=node.init is not None,
5428
+ )
5429
+ if var_addr is None:
5430
+ return None, None
3185
5431
  var_ir_type = arr_ir
3186
5432
  break
3187
5433
  else:
@@ -3195,47 +5441,19 @@ class LLVMCodeGenerator(object):
3195
5441
  if not self.in_global:
3196
5442
  var_addr = self._alloca_in_entry(var_ir_type, node.name)
3197
5443
  else:
3198
- existing = self.module.globals.get(node.name)
3199
- if existing:
3200
- var_addr = existing
3201
- else:
3202
- var_addr = ir.GlobalVariable(
3203
- self.module, var_ir_type, node.name
3204
- )
5444
+ var_addr, write_initializer = self._prepare_file_scope_object(
5445
+ node.name,
5446
+ var_ir_type,
5447
+ storage=node.storage,
5448
+ has_initializer=node.init is not None,
5449
+ )
5450
+ if var_addr is None:
5451
+ return None, None
3205
5452
  self.define(node.name, (var_ir_type, var_addr))
3206
5453
 
3207
5454
  if self._has_unsigned_scalar_pointee(node.type):
3208
5455
  self._mark_unsigned_pointee(var_addr)
3209
5456
 
3210
- # Infer the size of zero-length arrays from the initializer.
3211
- if (
3212
- isinstance(var_ir_type, ir.ArrayType)
3213
- and var_ir_type.count == 0
3214
- and node.init is not None
3215
- ):
3216
- actual_count = None
3217
- if isinstance(node.init, c_ast.InitList):
3218
- actual_count = len(node.init.exprs)
3219
- elif (
3220
- isinstance(node.init, c_ast.Constant)
3221
- and getattr(node.init, "type", None) == "string"
3222
- ):
3223
- raw = node.init.value[1:-1]
3224
- actual_count = len(self._process_escapes(raw)) + 1
3225
- if actual_count is not None and elem_ir_type is not None:
3226
- var_ir_type = ir.ArrayType(elem_ir_type, actual_count)
3227
- var_ir_type.dim_array = [actual_count]
3228
- if self.in_global:
3229
- new_name = self.module.get_unique_name(node.name)
3230
- var_addr = ir.GlobalVariable(self.module, var_ir_type, new_name)
3231
- self.define(node.name, (var_ir_type, var_addr))
3232
- if not hasattr(self, "_array_renames"):
3233
- self._array_renames = {}
3234
- self._array_renames[f'@"{node.name}"'] = f'@"{new_name}"'
3235
- else:
3236
- var_addr = self._alloca_in_entry(var_ir_type, node.name)
3237
- self.define(node.name, (var_ir_type, var_addr))
3238
-
3239
5457
  if self._has_unsigned_scalar_pointee(node.type):
3240
5458
  self._mark_unsigned_pointee(var_addr)
3241
5459
 
@@ -3243,17 +5461,19 @@ class LLVMCodeGenerator(object):
3243
5461
  # char s[] = "hi"; or const char *names[] = {"a", helper};
3244
5462
  if node.init is not None:
3245
5463
  if self.in_global:
3246
- try:
3247
- const_init = self._build_const_init(node.init, var_ir_type)
3248
- str(const_init)
3249
- var_addr.initializer = const_init
3250
- except Exception:
3251
- var_addr.initializer = self._zero_initializer(var_ir_type)
5464
+ if write_initializer:
5465
+ try:
5466
+ const_init = self._build_const_init(node.init, var_ir_type)
5467
+ str(const_init)
5468
+ var_addr.initializer = const_init
5469
+ except Exception:
5470
+ var_addr.initializer = self._zero_initializer(var_ir_type)
3252
5471
  elif isinstance(node.init, c_ast.InitList):
5472
+ self._safe_store(self._zero_initializer(var_ir_type), var_addr)
3253
5473
  self._init_array(
3254
5474
  var_addr,
3255
5475
  node.init,
3256
- elem_ir_type,
5476
+ var_ir_type.element,
3257
5477
  [ir.Constant(ir.IntType(32), 0)],
3258
5478
  )
3259
5479
  elif (
@@ -3262,6 +5482,7 @@ class LLVMCodeGenerator(object):
3262
5482
  and isinstance(elem_ir_type, ir.IntType)
3263
5483
  and elem_ir_type.width == 8
3264
5484
  ):
5485
+ self._safe_store(self._zero_initializer(var_ir_type), var_addr)
3265
5486
  raw = self._process_escapes(node.init.value[1:-1]) + "\00"
3266
5487
  idx0 = ir.Constant(ir.IntType(32), 0)
3267
5488
  for i, ch in enumerate(raw[: var_ir_type.count]):
@@ -3271,7 +5492,7 @@ class LLVMCodeGenerator(object):
3271
5492
  inbounds=True,
3272
5493
  )
3273
5494
  self.builder.store(int8_t(ord(ch)), elem_ptr)
3274
- elif self.in_global:
5495
+ elif self.in_global and write_initializer:
3275
5496
  var_addr.initializer = self._zero_initializer(var_ir_type)
3276
5497
 
3277
5498
  elif isinstance(node.type, c_ast.PtrDecl):
@@ -3279,6 +5500,7 @@ class LLVMCodeGenerator(object):
3279
5500
  point_level = 1
3280
5501
  sub_node = node.type
3281
5502
  resolved_pointee_type = None
5503
+ write_initializer = True
3282
5504
 
3283
5505
  while True:
3284
5506
  sub_next_type = sub_node.type
@@ -3290,12 +5512,16 @@ class LLVMCodeGenerator(object):
3290
5512
  elif isinstance(sub_next_type.type, c_ast.Union):
3291
5513
  resolved_pointee_type = self.codegen_Union(sub_next_type.type)
3292
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"
3293
5519
  else:
3294
5520
  type_str = sub_next_type.type.names
3295
5521
  resolved = self._get_ir_type(type_str)
3296
5522
  if isinstance(resolved, ir.Type):
3297
5523
  resolved_pointee_type = resolved
3298
- if isinstance(resolved, ir.LiteralStructType):
5524
+ if _is_struct_ir_type(resolved):
3299
5525
  type_str = "struct"
3300
5526
  break
3301
5527
  elif isinstance(sub_next_type, c_ast.PtrDecl):
@@ -3309,19 +5535,40 @@ class LLVMCodeGenerator(object):
3309
5535
  var_addr = self._alloca_in_entry(func_ir_type, node.name)
3310
5536
  self.define(node.name, (func_ir_type, var_addr))
3311
5537
  else:
3312
- var_addr = ir.GlobalVariable(
3313
- self.module, func_ir_type, node.name
5538
+ var_addr, write_initializer = self._prepare_file_scope_object(
5539
+ node.name,
5540
+ func_ir_type,
5541
+ storage=node.storage,
5542
+ has_initializer=node.init is not None,
3314
5543
  )
3315
- var_addr.initializer = ir.Constant(func_ir_type, None)
3316
- self.define(node.name, (func_ir_type, var_addr))
5544
+ if var_addr is None:
5545
+ return None, None
3317
5546
  if self._func_decl_returns_unsigned(sub_next_type):
3318
5547
  self._mark_unsigned_return(var_addr)
3319
5548
  if node.init is not None:
3320
5549
  init_val, _ = self.codegen(node.init)
3321
- # init_val is an ir.Function, bitcast to func ptr type
3322
- if init_val.type != func_ir_type:
3323
- init_val = self.builder.bitcast(init_val, func_ir_type)
3324
- 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)
3325
5572
  return None, var_addr
3326
5573
  pass
3327
5574
 
@@ -3335,38 +5582,61 @@ class LLVMCodeGenerator(object):
3335
5582
  var_addr = self._alloca_in_entry(ir_type, node.name)
3336
5583
  self.define(node.name, (ir_type, var_addr))
3337
5584
  else:
3338
- var_addr = ir.GlobalVariable(self.module, ir_type, node.name)
3339
- self.define(node.name, (ir_type, var_addr))
5585
+ var_addr, write_initializer = self._prepare_file_scope_object(
5586
+ node.name,
5587
+ ir_type,
5588
+ storage=node.storage,
5589
+ has_initializer=node.init is not None,
5590
+ )
5591
+ if var_addr is None:
5592
+ return None, None
3340
5593
  var_ir_type = ir_type
3341
5594
  else:
3342
- var_addr, var_ir_type = self.create_entry_block_alloca(
3343
- node.name, type_str, 1, point_level=point_level
3344
- )
5595
+ if self.in_global:
5596
+ pointee_ir_type = get_ir_type(type_str)
5597
+ if isinstance(pointee_ir_type, ir.VoidType):
5598
+ pointee_ir_type = int8_t
5599
+ for _ in range(point_level):
5600
+ pointee_ir_type = ir.PointerType(pointee_ir_type)
5601
+ var_ir_type = pointee_ir_type
5602
+ var_addr, write_initializer = self._prepare_file_scope_object(
5603
+ node.name,
5604
+ var_ir_type,
5605
+ storage=node.storage,
5606
+ has_initializer=node.init is not None,
5607
+ )
5608
+ if var_addr is None:
5609
+ return None, None
5610
+ else:
5611
+ var_addr, var_ir_type = self.create_entry_block_alloca(
5612
+ node.name,
5613
+ type_str,
5614
+ 1,
5615
+ point_level=point_level,
5616
+ storage=node.storage,
5617
+ )
3345
5618
 
3346
5619
  if self._has_unsigned_scalar_pointee(node.type):
3347
5620
  self._mark_unsigned_pointee(var_addr)
3348
5621
 
3349
5622
  if node.init is not None:
3350
5623
  if self.in_global:
3351
- try:
3352
- const_init = self._build_const_init(node.init, var_ir_type)
3353
- str(const_init)
3354
- var_addr.initializer = const_init
3355
- except Exception:
3356
- var_addr.initializer = ir.Constant(var_ir_type, None)
5624
+ if write_initializer:
5625
+ try:
5626
+ const_init = self._build_const_init(node.init, var_ir_type)
5627
+ str(const_init)
5628
+ var_addr.initializer = const_init
5629
+ except Exception:
5630
+ var_addr.initializer = ir.Constant(var_ir_type, None)
3357
5631
  else:
3358
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
+ )
3359
5636
  if isinstance(init_val.type, ir.ArrayType) and isinstance(
3360
5637
  var_ir_type, ir.PointerType
3361
5638
  ):
3362
- gv = ir.GlobalVariable(
3363
- self.module,
3364
- init_val.type,
3365
- self.module.get_unique_name("str"),
3366
- )
3367
- gv.initializer = init_val
3368
- gv.global_constant = True
3369
- init_val = self.builder.bitcast(gv, var_ir_type)
5639
+ init_val = self._implicit_convert(init_val, var_ir_type)
3370
5640
  elif init_val.type != var_ir_type:
3371
5641
  init_val = self._implicit_convert(init_val, var_ir_type)
3372
5642
  self._safe_store(init_val, var_addr)
@@ -3376,6 +5646,14 @@ class LLVMCodeGenerator(object):
3376
5646
  return None, var_addr
3377
5647
 
3378
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
3379
5657
 
3380
5658
  valtype, var = self.lookup(node.name)
3381
5659
  node.ir_type = valtype
@@ -3462,7 +5740,7 @@ class LLVMCodeGenerator(object):
3462
5740
  # Non-array type (opaque struct etc): treat as pointer subscript
3463
5741
  if not isinstance(name_type, ir.ArrayType):
3464
5742
  ptr = (
3465
- self.builder.bitcast(name_ir, ir.PointerType(int8_t))
5743
+ self._implicit_convert(name_ir, ir.PointerType(int8_t))
3466
5744
  if not isinstance(name_ir.type, ir.PointerType)
3467
5745
  else name_ir
3468
5746
  )
@@ -3545,22 +5823,30 @@ class LLVMCodeGenerator(object):
3545
5823
  retval, _ = self.codegen(node.expr)
3546
5824
  # Implicit convert to function return type
3547
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
3548
5829
  if retval.type != func_ret_type:
3549
5830
  retval = self._implicit_convert(retval, func_ret_type)
3550
5831
  self.builder.ret(retval)
3551
5832
  return None, None
3552
5833
 
3553
5834
  def codegen_Compound(self, node):
3554
-
3555
- if node.block_items:
3556
- for stmt in node.block_items:
3557
- if self.builder and self.builder.block.is_terminated:
3558
- # After a terminator (goto/break/continue/return),
3559
- # only process labels — skip unreachable code
3560
- if isinstance(stmt, c_ast.Label):
3561
- self.codegen(stmt)
3562
- continue
3563
- 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)
3564
5850
  return None, None
3565
5851
 
3566
5852
  def codegen_FuncDecl(self, node):
@@ -3574,38 +5860,56 @@ class LLVMCodeGenerator(object):
3574
5860
  ir_type, _ = self.codegen(node.decl.type)
3575
5861
  funcname = node.decl.name
3576
5862
 
3577
- if funcname == "main":
3578
- self.return_type = ir_type # for call in C
3579
-
3580
- arg_types = []
3581
- is_var_arg = False
3582
- if node.decl.type.args:
3583
- for arg_type in node.decl.type.args.params:
3584
- if isinstance(arg_type, c_ast.EllipsisParam):
3585
- is_var_arg = True
3586
- continue
3587
- t = self._resolve_param_type(arg_type)
3588
- if t is not None:
3589
- arg_types.append(t)
5863
+ self.return_type = ir_type # for call in C
5864
+ if not hasattr(self, "func_return_types"):
5865
+ self.func_return_types = {}
5866
+ self.func_return_types[funcname] = ir_type
5867
+
5868
+ param_infos, is_var_arg = self._funcdef_param_infos(node)
5869
+ arg_types = [param_type for _name, param_type, _decl in param_infos]
5870
+
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
5879
+ symbol_name = self._register_file_scope_function(
5880
+ funcname,
5881
+ function_type,
5882
+ storage=node.decl.storage,
5883
+ funcspec=node.decl.funcspec,
5884
+ is_definition=True,
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
+ )
3590
5890
 
3591
5891
  with self.new_function():
5892
+ self._function_display_name = funcname
3592
5893
 
3593
- existing = self.module.globals.get(funcname)
5894
+ existing = self.module.globals.get(symbol_name)
3594
5895
  if existing and isinstance(existing, ir.Function):
3595
5896
  if existing.is_declaration:
3596
5897
  self.function = existing
5898
+ if needs_internal_linkage:
5899
+ self.function.linkage = "internal"
3597
5900
  else:
3598
- # Already defined skip this duplicate definition
3599
- return None, None
5901
+ raise SemanticError(f"redefinition of function '{funcname}'")
3600
5902
  else:
3601
5903
  try:
3602
5904
  self.function = ir.Function(
3603
5905
  self.module,
3604
- ir.FunctionType(ir_type, arg_types, var_arg=is_var_arg),
3605
- name=funcname,
5906
+ function_type,
5907
+ name=symbol_name,
3606
5908
  )
5909
+ if needs_internal_linkage:
5910
+ self.function.linkage = "internal"
3607
5911
  except Exception:
3608
- return None, None
5912
+ raise SemanticError(f"failed to define function '{funcname}'")
3609
5913
  if self._func_decl_returns_unsigned(node.decl.type):
3610
5914
  self._mark_unsigned_return(self.function)
3611
5915
  self.block = self.function.append_basic_block()
@@ -3613,53 +5917,38 @@ class LLVMCodeGenerator(object):
3613
5917
  if len(self.env.maps) > 1:
3614
5918
  self.env.maps[1][funcname] = (ir_type, self.function)
3615
5919
  self.define(funcname, (ir_type, self.function))
3616
- if node.decl.type.args:
3617
- param_idx = 0
3618
- for p in node.decl.type.args.params:
3619
- if isinstance(p, c_ast.EllipsisParam):
3620
- continue
3621
- # Skip void params (f(void) means no params)
3622
- if isinstance(p, c_ast.Typename) and isinstance(
3623
- getattr(p, "type", None), c_ast.TypeDecl
3624
- ):
3625
- if isinstance(
3626
- p.type.type, c_ast.IdentifierType
3627
- ) and p.type.type.names == ["void"]:
3628
- continue
3629
- if param_idx >= len(arg_types):
3630
- break
3631
- arg_type = arg_types[param_idx]
3632
- pname = p.name if isinstance(p.name, str) else f"arg{param_idx}"
3633
- var = self._alloca_in_entry(arg_type, pname)
3634
- self.define(pname, (arg_type, var))
3635
- self._safe_store(self.function.args[param_idx], var)
3636
- # Track unsigned params
3637
- if isinstance(p, c_ast.Decl) and isinstance(
3638
- 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
3639
5938
  ):
3640
- if isinstance(p.type.type, c_ast.IdentifierType):
3641
- if self._is_unsigned_type_names(p.type.type.names):
3642
- self._mark_unsigned(var)
3643
- if isinstance(p, c_ast.Decl):
3644
- if self._has_unsigned_scalar_pointee(p.type):
3645
- self._mark_unsigned_pointee(var)
3646
- if isinstance(
3647
- p.type, c_ast.PtrDecl
3648
- ) and self._func_decl_returns_unsigned(p.type.type):
3649
- self._mark_unsigned_return(var)
3650
- param_idx += 1
3651
-
3652
- 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)
3653
5946
 
3654
5947
  if not self.builder.block.is_terminated:
3655
5948
  if isinstance(ir_type, ir.VoidType):
3656
5949
  self.builder.ret_void()
3657
- elif isinstance(ir_type, ir.PointerType):
3658
- self.builder.ret(ir.Constant(ir_type, None))
3659
- elif self._is_floating_ir_type(ir_type):
3660
- self.builder.ret(ir.Constant(ir_type, 0.0))
3661
5950
  else:
3662
- self.builder.ret(ir.Constant(ir_type, 0))
5951
+ self.builder.ret(self._zero_initializer(ir_type))
3663
5952
 
3664
5953
  return None, None
3665
5954
 
@@ -3667,63 +5956,58 @@ class LLVMCodeGenerator(object):
3667
5956
  # Generate LLVM types for struct members
3668
5957
 
3669
5958
  # If this is a reference to a named struct without decls, look it up
3670
- if node.name and (node.decls is None or len(node.decls) == 0):
3671
- if node.name in self.env:
3672
- return self.env[node.name][0]
3673
- # Opaque/forward-declared struct: treat as i8 (byte) for pointer use
3674
- opaque = ir.IntType(8)
3675
- 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))
3676
5967
  return opaque
3677
5968
 
5969
+ if any(decl.bitsize is not None for decl in node.decls):
5970
+ return self._build_layout_backed_struct(node)
5971
+
3678
5972
  member_types = []
3679
5973
  member_names = []
3680
5974
  member_decl_types = []
3681
5975
  for decl in node.decls:
3682
- if isinstance(decl.type, c_ast.TypeDecl) and isinstance(
3683
- decl.type.type, c_ast.Struct
3684
- ):
3685
- nested_type = self.codegen_Struct(decl.type.type)
3686
- member_types.append(nested_type)
3687
- elif isinstance(decl.type, c_ast.TypeDecl) and isinstance(
3688
- decl.type.type, c_ast.Union
3689
- ):
3690
- nested_type = self.codegen_Union(decl.type.type)
3691
- member_types.append(nested_type)
3692
- elif isinstance(decl.type, c_ast.ArrayDecl):
3693
- # Handle multi-dimensional arrays: a[N][M] -> [N x [M x T]]
3694
- def _build_array_type(arr_node):
3695
- dim = self._eval_dim(arr_node.dim) if arr_node.dim else 0
3696
- if isinstance(arr_node.type, c_ast.ArrayDecl):
3697
- inner = _build_array_type(arr_node.type)
3698
- else:
3699
- inner = self._resolve_ast_type(arr_node.type)
3700
- return ir.ArrayType(inner, dim)
3701
-
3702
- member_types.append(_build_array_type(decl.type))
3703
- elif isinstance(decl.type, c_ast.PtrDecl):
3704
- member_types.append(self._resolve_ast_type(decl.type))
3705
- elif isinstance(decl.type, c_ast.TypeDecl):
3706
- type_str = decl.type.type.names
3707
- member_types.append(self._get_ir_type(type_str))
3708
- else:
3709
- member_types.append(int64_t) # fallback
5976
+ member_types.append(self._resolve_struct_member_ir_type(decl))
3710
5977
  member_names.append(decl.name)
3711
5978
  member_decl_types.append(decl.type)
3712
5979
  # Create the struct type
3713
- struct_type = ir.LiteralStructType(member_types)
5980
+ struct_type = self._identified_aggregate_type("struct", node.name, member_types)
3714
5981
  struct_type.members = member_names
3715
5982
  struct_type.member_decl_types = member_decl_types
3716
5983
 
3717
5984
  # Register named structs for later reuse
3718
5985
  if node.name:
3719
- self.define(node.name, (struct_type, None))
5986
+ self.define(self._tag_type_key(node.name), (struct_type, None))
3720
5987
 
3721
5988
  return struct_type
3722
5989
 
3723
5990
  def codegen_Union(self, node):
3724
5991
  """Model union as a struct with alignment-preserving storage."""
3725
- if node.name and (node.decls is None or len(node.decls) == 0):
3726
- 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
3727
6011
 
3728
6012
  member_types = {}
3729
6013
  member_decl_types = {}
@@ -3750,18 +6034,17 @@ class LLVMCodeGenerator(object):
3750
6034
  align_size = max_align
3751
6035
  pad_size = max_size - align_size
3752
6036
  if pad_size > 0:
3753
- union_type = ir.LiteralStructType(
3754
- [align_type, ir.ArrayType(int8_t, pad_size)]
3755
- )
6037
+ union_body = [align_type, ir.ArrayType(int8_t, pad_size)]
3756
6038
  else:
3757
- union_type = ir.LiteralStructType([align_type])
6039
+ union_body = [align_type]
6040
+ union_type = self._identified_aggregate_type("union", node.name, union_body)
3758
6041
  union_type.members = list(member_types.keys())
3759
6042
  union_type.member_types = member_types
3760
6043
  union_type.member_decl_types = member_decl_types
3761
6044
  union_type.is_union = True
3762
6045
 
3763
6046
  if node.name:
3764
- self.define(node.name, (union_type, None))
6047
+ self.define(self._tag_type_key(node.name), (union_type, None))
3765
6048
 
3766
6049
  return union_type
3767
6050
 
@@ -3805,7 +6088,7 @@ class LLVMCodeGenerator(object):
3805
6088
  )
3806
6089
  struct_addr = inner_addr
3807
6090
  elif isinstance(node.name, c_ast.ID):
3808
- struct_instance_addr = self.env[node.name.name][1]
6091
+ _, struct_instance_addr = self.lookup(node.name.name)
3809
6092
  if not isinstance(struct_instance_addr.type, ir.PointerType):
3810
6093
  raise Exception("Invalid struct reference")
3811
6094
 
@@ -3865,6 +6148,66 @@ class LLVMCodeGenerator(object):
3865
6148
  if semantic_base_type is not None
3866
6149
  else (val.type if hasattr(val.type, "members") else int8_t)
3867
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
3868
6211
 
3869
6212
  # Union access: all fields share offset 0, use bitcast
3870
6213
  if getattr(struct_type, "is_union", False):
@@ -3880,8 +6223,8 @@ class LLVMCodeGenerator(object):
3880
6223
  resolved, ir.PointerType
3881
6224
  ):
3882
6225
  pass
3883
- elif isinstance(
3884
- resolved, (ir.ArrayType, ir.LiteralStructType, ir.PointerType)
6226
+ elif isinstance(resolved, (ir.ArrayType, ir.PointerType)) or _is_struct_ir_type(
6227
+ resolved
3885
6228
  ):
3886
6229
  semantic_field_type = resolved
3887
6230
  except Exception:
@@ -3905,10 +6248,9 @@ class LLVMCodeGenerator(object):
3905
6248
 
3906
6249
  # Opaque struct (no members) — treat as byte-offset access
3907
6250
  if not hasattr(struct_type, "members"):
3908
- ptr = self.builder.bitcast(struct_addr, voidptr_t)
3909
- val = self._safe_load(self.builder.bitcast(ptr, ir.PointerType(int64_t)))
3910
- self._set_expr_ir_type(node, int64_t)
3911
- return val, ptr
6251
+ raise SemanticError(
6252
+ f"field '{node.field.name}' accessed on incomplete struct"
6253
+ )
3912
6254
 
3913
6255
  field_index = None
3914
6256
  for i, field in enumerate(struct_type.members):
@@ -3939,7 +6281,9 @@ class LLVMCodeGenerator(object):
3939
6281
  resolved, ir.PointerType
3940
6282
  ):
3941
6283
  pass # keep original array type
3942
- elif isinstance(resolved, (ir.LiteralStructType, ir.PointerType)):
6284
+ elif isinstance(resolved, ir.PointerType) or _is_struct_ir_type(
6285
+ resolved
6286
+ ):
3943
6287
  semantic_field_type = resolved
3944
6288
  except Exception:
3945
6289
  pass
@@ -4023,16 +6367,35 @@ class LLVMCodeGenerator(object):
4023
6367
  if enumerator.value:
4024
6368
  current_val = self._eval_const_expr(enumerator.value)
4025
6369
  self.define(
4026
- enumerator.name, (int64_t, ir.Constant(int64_t, current_val))
6370
+ enumerator.name, (int32_t, ir.Constant(int32_t, current_val))
4027
6371
  )
4028
6372
  current_val += 1
4029
6373
  return None, None
4030
6374
 
4031
6375
  def _eval_const_expr(self, node):
4032
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
+
4033
6394
  if isinstance(node, c_ast.Constant):
4034
6395
  if node.type == "string":
4035
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)
4036
6399
  v = node.value.rstrip("uUlL")
4037
6400
  if v.startswith("'"):
4038
6401
  return self._char_constant_value(v)
@@ -4053,8 +6416,8 @@ class LLVMCodeGenerator(object):
4053
6416
  raw = node.expr.value[1:-1]
4054
6417
  processed = self._process_escapes(raw)
4055
6418
  return len(self._string_bytes(processed + "\00"))
4056
- val = self._eval_const_expr(node.expr)
4057
- 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)
4058
6421
  if node.op == "&" and isinstance(node.expr, c_ast.StructRef):
4059
6422
  offset, _ = self._eval_offsetof_structref(node.expr)
4060
6423
  return offset
@@ -4070,12 +6433,13 @@ class LLVMCodeGenerator(object):
4070
6433
  elif isinstance(node, c_ast.BinaryOp):
4071
6434
  l = self._eval_const_expr(node.left)
4072
6435
  r = self._eval_const_expr(node.right)
6436
+ use_float = is_float_value(l) or is_float_value(r)
4073
6437
  ops = {
4074
6438
  "+": lambda a, b: a + b,
4075
6439
  "-": lambda a, b: a - b,
4076
6440
  "*": lambda a, b: a * b,
4077
- "/": lambda a, b: a // b,
4078
- "%": 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),
4079
6443
  "<<": lambda a, b: a << b,
4080
6444
  ">>": lambda a, b: a >> b,
4081
6445
  "&": lambda a, b: a & b,
@@ -4107,6 +6471,14 @@ class LLVMCodeGenerator(object):
4107
6471
  return 0 # unknown identifier defaults to 0
4108
6472
  elif isinstance(node, c_ast.Cast):
4109
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__}")
4110
6482
  elif isinstance(node, c_ast.Typename):
4111
6483
  return 0
4112
6484
  raise CodegenError(f"Not a constant expression: {type(node).__name__}")
@@ -4150,7 +6522,7 @@ class LLVMCodeGenerator(object):
4150
6522
  elif isinstance(node.type.type, c_ast.Enum):
4151
6523
  # typedef enum { A, B, C } MyEnum;
4152
6524
  self.codegen_Enum(node.type.type)
4153
- self.define(f"__typedef_{node.name}", int64_t)
6525
+ self.define(f"__typedef_{node.name}", int32_t)
4154
6526
  elif isinstance(node.type, c_ast.ArrayDecl):
4155
6527
  self.define(f"__typedef_{node.name}", self._build_array_ir_type(node.type))
4156
6528
  elif isinstance(node.type, c_ast.PtrDecl):
@@ -4174,4 +6546,7 @@ class LLVMCodeGenerator(object):
4174
6546
  else:
4175
6547
  ptr_type = ir.PointerType(base_ir)
4176
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)
4177
6552
  return None, None