TCC currently recognises the restrict keyword but
discards it. Give the qualifier its own type bit and
diagnose misuse that violates constraints in ISO C.
There is (obviously) no new aliasing analysis.
Modify post_type() such that the volatile qualifier is
removed from function return types, as required by ISO C.
Previously, only the const qualifier was removed.
Added tests for all three standard qualifiers, although
the new test for restrict only passes because TCC ignores
that keyword.
To ensure that qualifiers within an array-style
parameter declaration (e.g. int [const 2] are handled
correctly, post_type() is modified to collect qualifiers
and OR them into the t member of CType.
The new test for restrict also passes, but only
because the restrict keyword is ignored.
Previously, TCC wrongly discarded the const and
volatile qualifiers from parameter types when
adjusting them.
- convert_parameter_type() will no longer remove
the const and volatile qualifiers from a CType.
- a new function, convert_expression_type(), will
remove the const and volatile qualifiers before
tail-calling convert_parameter_type().
- unary() and gexpr() will call
convert_expression_type() instead of
convert_parameter_type() to ensure that
expression semantics are unchanged.
The new tests for restrict also pass, but only
because the restrict keyword is ignored.
Clang doesn't combine qualifiers through array types
so put a workaround into tcctest.c to avoid spurious
failures of the recently-added tests for that
operation, when using Clang instead of GCC to
generate the reference output.
Conditional expressions combine the qualifiers
of their pointer target types. TCC only examines
the outer array node, although array qualifiers
are stored on the element type. This makes the
result depend on operand order.
Collect and apply qualifiers through array layers.
__builtin_types_compatible_p ignores qualifiers
exposed through array type derivations in GCC
and Clang, whereas it does not in TCC.
This fix uses the existing unqualified
comparison recursively for array types, while
continuing to preserve qualifiers on referenced
types.
- tcc_add_linker_symbols():
don't add for dlls
- bind_exe_dynsym():
update _end symbol when done
allow data definition in the .so override a bss symbol
in the exe. For example
exe: int x;
.so: int x = 123;
- set_elf_sym():
cannot compare section numbers with symbols from dlls
tcc_load_obj():
return -1 from symbol conflict errors
tests/tests2/148_linker_symbols:
- use only one file
- make it print useful output rather than just "2 errors."
In general, the tests2 setup with .expect files is meant
to compare text output. For example:
printf("val = %d\n", val);
.expect file can be generated or refreshed using
make tests2.nnn+
or in order to force generation with tcc itself
make tests2.nnn+ GEN-CC='$(GEN-TCC)'
tccgen.c:
- __attribute__((section(...))) : set section type/flags
according to first usage
- fix bcheck bounds for -fcommon
The host-sized symbol addend parameter was introduced by commit b14ef0e2 and truncates 64-bit addends on LLP64 hosts.
Use addr_t and cover the negative-addend case.
Also:
- arm64-gen.c: cleanup load/store
fixes a problem with bound-check test1b with
./configure --cc=tcc
Also, do not use 'long' or 'ul' suffix in tcc because
for example on win32-arm64
(uint64_t) ~0xffful == 0x00000000fffff000
but on linux-arm64
(uint64_t) ~0xffful == 0xfffffffffffff000
Also:
- tccpe.c: cleanup "quoted dll nanes in def" parse
- tests2: enable some threaded tests on windows
An input shared object can be the only place where a conventional boundary name is referenced or defined. In that case the regular symbol table has no entry, but native ELF linkers still synthesize the executable definition so that it participates in dynamic symbol resolution.
Consult the input dynamic symbol table before treating a plain boundary name as unreferenced. Exercise the case with a DSO-defined end symbol whose address must be interposed by the executable's final boundary.
The traditional boundary symbols were recorded against the canonical .text, .data and .bss sections before dynamic binding and final layout. Separately named allocatable sections and copy-relocated data could therefore fall outside the advertised ranges.
Track definitions synthesized by the linker and finalize them from the ordered loadable sections. Refresh both regular and dynamic symbol tables, and give the plain etext, edata and end names PROVIDE semantics on every ELF target while preserving user definitions.
Cover archive references, overrides, copy relocations and independently named text/data/bss sections.
Reported-by: Richard Wheeler <18647491+PythonWillRule@users.noreply.github.com>
Parse quoted LIBRARY names as a single token and add the default .dll
extension when needed. Allocate the stored name dynamically.
Add a Windows regression test using a quoted name with spaces.
Preserve symbolic offsets when materializing i386 TLS addresses, generate ARM64 TLS addresses from the thread pointer, and carry RISC-V addends on both halves of the TPREL relocation pair.
Keep undefined ELF TLS symbols typed as STT_TLS so objects produced by TCC can be linked by the system linker. Extend the TLS regression coverage for narrow signed and unsigned accesses, address-taking, runtime indexing, thread isolation, large RISC-V offsets, unsupported runtimes, and external TLS definitions.
tccgen.c:
remove VT_TLS in gen_cast
libtcc.c:
remove warning for gcc 16
i386-gen.c:
fix gen_modrm for VT_LDOUBLE
riscv64-gen.c:
add fc to TLS address
x86_64-gen.c:
add TLS prefix in gen_opf
tests2/144_tls.c/tests2/144_tls.expect:
updated test case
tests/tests2/Makefile
skip TLS test for BSD
tccrun.c:
- simplify usage of VirtualAlloc on win32/64
from 6728a64f1b
tccpe.c:
- revert changes to pe_create_pdb()
(doesn't work and/or too much complication)
from 37b7247796
- reduce #ifdef amount
tccgen.c:
- fix fix for cast to void
from fad812360b
tccelf.c:
- put create_riscv_attribute_section() into right place
from c77339ab41
tcc.h/libtcc.c:
- simplify link-option parser for pe dll_characteristics
from 1a54e47dda
i386-gen.c:
- fix tls
from 8502540b4a
workflow/build.yml:
- add i386-linux
- disable riscv64-linux-native (seems not supported currently)
Use the existing array-aware qualifier helper when propagating qualifiers from a const or volatile aggregate to a selected field. This keeps address-of array members from spuriously warning while preserving warnings for discarded qualifiers and read-only writes.
Add regression coverage for pointer-to-array returns, array decay, and writes through const array members.
Move the TinyCC/Windows ARM64 Interlocked workaround from the semlock call site into the bundled WinAPI header. This keeps tcc.h using the normal Interlocked API while avoiding unresolved Interlocked imports when tcc -run self-compiles on Windows ARM64.
Update lib/bt-exe.c as well so its ARM64 runtime object does not define a second InterlockedExchange fallback now provided by winnt.h.
- tccmacho.c: add STT_TLS to convert_symbol() for Mach-O output
- tests/tests2/Makefile: skip 144_tls on Win32 (no pthread.h) and
remove arm from TLS arch list (QEMU user-mode cannot emulate
MRC p15 TLS register access)
Replace raw-register AMO asm with proper inline-asm constraints
for all three operands (rd, rs2, rs1). Use long long for .d-word
AMO variants with pre-set values (xd=0xCAFEBABECAFEBABE,
val=0xDEADBEEFDEADBEEF) to avoid uninitialized-data issues.
Restore csr_pseudo_main printf output and re-enable amo_main()
in the test (was commented out as 'crash on qemu').
Use GEN = $(GEN-TCC) since GCC and TCC produce different fcsr
initial values on riscv64.
Revert 199369bb17
- generating test.ref with tcc makes little sense.
- combine riscv_asm & conversion tests into only two files.
(too many files are bad ...)
tccgen.c:
- init_putv(): improve long double cross constants
now in separate function including some basic conversions
arm-gen.c:
- switch to using TCC_USING_DOUBLE_FOR_LDOUBLE
now it actually never will see VT_LDOUBLE
lib/lib-arm64.c:
- simplify by using unions
tccpp.c:
- reduce amounts of #ifdef TCC_USING_DOUBLE_FOR_LDOUBLE
originally, this was meant to avoid 'void' values leak
to the gen-xxx.c generators which would otherwise try
to load void from/to registers. Also catch invalid types
such as 'unsigned bool' eariler.
tccgen.c:
- expr_cond()/gexpr(): ignore VT_VOID
- gen_cast(): set float reg with static initializers to make
backends happier with invalid input
- init_putv():
improve static compound literal initializers
Also:
- ignore "missing prototype" and "might return no value"
for old style K&R functions
tcc.c, libtcc.c:
- tcc_set_output_type(): return errors from loading crt1.o
tcc.h/tccpp.c:
- fix horrible longstanding mistake with sizeof SValue.tab.
Must have place for sizeof (long double) rather than the
target's LDOUBLE_SIZE.
tccdefs.h:
- add a fake __[u]int128_t for all platforms
arm-gen.c:
- gen_cvt_ftof() must load to reg always
arm64-gen.c:
- simplify arm64_pcs() a bit
i386/x86_64-gen.c:
- do not assume char is signed (for cross compilation arm->x86)
tccelf/macho linker message:
- instead of
"undefined symbol 'X'"
now say
"unresolved reference to 'X'"
which makes it more clear that it is a _linker_error_.
tccasm.c:
- .align/p2align: use 'nop's in exec sections
Add handlers for csrr, csrw, csrwi, csrsi, csrci as pseudo-instructions:
csrr rd, csr -> csrrs rd, csr, x0
csrw csr, rs -> csrrw x0, csr, rs
csrwi csr, uimm -> csrrwi x0, csr, uimm
csrsi csr, uimm -> csrrsi x0, csr, uimm
csrci csr, uimm -> csrrci x0, csr, uimm
Tokens were already defined in riscv64-tok.h. Tested on
Spacemit X100 using fcsr (0x003) which is accessible in user mode.
cycle/instret CSRs are privileged and not accessible from Linux
user mode on this hardware.
The promote char/short funcret test in tcctest.c invokes
undefined behavior (narrow return without extension). GCC
produces one set of values, TCC with PROMOTE_RET produces
correct ABI-compliant values. Generate the reference with
TCC so self-tests are consistent.
Adds the 8 most essential floating-point arithmetic instructions:
fadd.s/d, fsub.s/d, fmul.s/d, fdiv.s/d, for both single and double
precision. These complement the existing fsgnj/fmax/fmin/fsqrt set.
Comparison (feq/flt/fle) and conversion (fcvt) instructions remain
as future work.
sext.w: addiw rd, rs, 0 (sign-extend 32-bit word)
fmv.s/fmv.d: fsgnj.s/d rd, rs, rs (float register move)
fneg.s/fneg.d: fsgnjn.s/d rd, rs, rs (float register negate)
These were defined in riscv64-tok.h but had no handler code.
CSR pseudo-instructions (csrr, csrw, csrci, csrsi, csrwi) skipped
for now — they require CSR operand parsing in binary mode.
On RV64, long long (64-bit) fits in a single general-purpose register.
The existing load/store calls already handle 64-bit values correctly.
The 'not implemented' errors were vestigial from 32-bit architectures
where long long requires a register pair.
Removing these errors allows inline asm to accept 64-bit integer operands
on RV64, which is important since this is the native register width.
Replaces the empty stub that relied on 'RV64 registers are always
sign-extended' assumption. Now emits addiw rd, rs, 0 for proper
32-to-64 bit sign extension, matching arm64's sxtw behavior.
Verified on Spacemit X100: tests2 pass (125_atomic_misc has a
pre-existing intermittent segfault, not caused by this change).
Add sign/zero extension for char/short to int casts using
slli+srai (signed) or slli+srli/andi (unsigned) sequences.
Also adds RISCV64 to the gen_cvt_csti fast-path conditional
in tccgen.c:3466, matching arm64/i386/x86_64.
Verified on Spacemit X100: all tests2 pass, no regressions.
Other mature backends (i386, x86_64, arm64) all define PROMOTE_RET,
which forces explicit sign/zero extension of narrow return types at the
caller side. Without it, riscv64 relied on the assumption that RV64
registers are always sign-extended, which may not hold when interfacing
with non-TCC compilers.
Verified on Spacemit X100 (riscv64): self-compilation is self-consistent
across 3 layers. test3 reference mismatch is expected — the test
intentionally invokes UB via type-punned function pointers (csf macro).
tccpe.c:
- fix arm64 unwind codes (to make native set/longjmp() work)
sizeof(RUNTIME_FUNCTION) is 8 on arm64 in the first place
no need to note stack slots if we don't save any registers anyway
arm64-gen.c:
- fix long double reg-move
- fix arm64_hfa() for structs with float arrays
- gfunc_prolog(): setup stackframe eariler (simplifies unwind codes)
- new function gv_addr(RC);
win32/include/setjmp.h:
- provide correct definition for setjmo() (frameoffset = 224)
tccasm.c:
- support ".quad" with symbol & relocation
- support ".size"
- fix ". - symbol" arithmetic
win32/lib/crt1.c and win32/include/stdlib.h:
- do not write to __argc/__argv which reside in msvcrt.dll
(msvcrt.dll on arm64 does not like that, crashes on unload)
tcc.c,libtcc.c:
- new functions tcc_fopen/fclose to avoid different stdio unstances
in tcc.exe & libtcc.dll
tests & github workflow:
- add test-win32.bat to run tests with a tcc compiled by build-tcc.bat
- add msvcrt_start.c for gcc/clang to use the same runtime as tcc
the problem is that newer gcc as well as clang and cl are
linking to newer runtimes (such as UCRT) that have partially
different printf format behavior which makes tcctest fail.
the solution here is to force these compilers to link with
msvcrt.dll just like tcc.
Also, there is no gcc on arm64-win32 currently at all.
Anyway, this approach to running the github CI tests
does not require msys2. But It does rely on gnumake
as well as on some 'sh' shell though which seems to be
installed somewhere (maybe it is the one from git).
from: https://github.com/bold84/tinycc
Author: Benjamin Oldenburg <benjamin.oldenburg@ordis.co.th> 2026-04-04 16:29:28
Committer: Benjamin Oldenburg <benjamin.oldenburg@ordis.co.th> 2026-04-04 16:29:28
Branch: win_arm64_clean
Commit: 8b5ab1bb01
Also here: https://repo.or.cz/tinycc.git/shortlog/refs/mob/mob_bold84/win_arm64_clean
This and the followup commits on mob is selected parts
of that original branch. So it is not everything.
It is not, for example:
- unrelated whitespace changes in many files
- a "pin msvcrt.dll" feature in tccpe.c (why that)
- a native getenv() replacement in tcc.c (why that)
- larger changes to the win32/lib runtime and tccrun.c (not needed)
- a very gcc specific detail for struct alignent in tccgen.c
- a custom set/longjmp implementation/replacement (not needed)
- lots of rather basic test files in the win32 folder
- a 'tests/asm' folder with some files (one file renamed to 140_test...c)
- a .docs folder with one file
Revert "Add support to debug libtcc code"
- not fully developed experimental feature
This reverts commit 1fe3e3bff5.
This reverts commit 4768b11737.
Revert "tests: add test for x86_64 xor REX prefix bug in load()"
- AI generated nonsense test
This reverts commit d5ecb52a71.
Revert "tccpp.c: Improve integer constant overflow warning"
- Too long and confusing messages and comments for feature
with questionable benefit.
This reverts commit 085bdf8997.
riscv64-link:
- cleanup "pair pcrel lo relocations by hi address"
From fada98b1ce
tccgen.c:
- Simplify "Cast signed pointer offset to ptrdiff_t before performing arithmetic"
From 5ad52cc1ed
libtcc.c:
- Revert "tcc options: document behavior and clashes (no-op)"
a bit more information than one would like to have I think.
(why try to understand that comments plus the extra
script if one can as well just read the code itself ;)
From 234e2dd2bf
tccdefs.h:
- Revert "Move lib/va_list.c into include/tccdefs.h"
Lets not fill tccdefs.h with too much inline code
Also, -nostdlib -run is no longer supported
From fa6a6bfbbd
arm64-gen.c:
- cleanup "Implement TOK_NEG for floats natively"
Also, make it "no lvalue" in tccgen.c/x86-64-gen.c
From c39eaf10cf
lib/lib-arm64.c:
- cleanup "Remove libc dependency from lib-arm64"
using unions is much faster than some made up memcpy()
From 8c61b91de8
This reverts the crap from an unknown committer "OpenCode[AI]", that added an extension to TinyCC,
which is incompatible to the C standard and not available in any other mainstream C compiler:
* Revert "No More ; needed"
This reverts commit 6547ea47d6
* Revert "Add AGENTS.md"
This reverts commit 898f496dc9.
This also reverts a regression fix by "Zuhaitz.dev", which fixed a small part of the OpenCode-AI chaos:
* Revert "tccgen: Fix optional semicolon regression"
This reverts commit 169628a6ab
--
Regards ... Detlef
When load() in x86_64-gen.c generates a zero constant for a 64-bit
register, it uses:
o(0xc031 + REG_VALUE(r) * 0x900);
REG_VALUE(r) masks to (r & 7), losing bit 3, and no orex() call
emits the REX prefix needed for registers r8-r15. For TREG_R11
(used by gcall_or_jmp for indirect calls), this emits
"xor %ebx,%ebx" (31 db) instead of "xor %r11d,%r11d" (45 31 db),
clobbering the wrong register.
The test compiles an indirect call through a null function pointer
((void(*)(void))0)() via libtcc, then inspects the generated machine
code for the incorrect encoding.
workflow:
- revert 'pinact for security' for readability
from 831c3fa184
tccpp.c:
- remove code that allows tcc to parse numbers incorrectly (*)
from 829c848520
tccgen.c:
- Revert "Relaxed the 'incompatible pointer type' warning a bit" (*)
from d9ec17d334.
tccrun.c:
- remove support for -nostdlib -run
for simplicity, we require "main" with tcc -run always
tccpp.c:
- Revert "Free all preprocessor memmory in case of error."
from c96f0cad61
Remove TinyAlloc->limit instead. Thus it can do also bigger
allocs. Big TokenStrings (like 200kb+ when compiling tcc)
may come from inline functions or from large initializers.
Makefile/configure:
- use --config-pie for configuring tcc output only
- use -fPIC with clang-x86_64 to avoid 32-bit relocs
libtcc.c:
- fix "tcc file.c -run" i.e. -run as last argument
i386-gen.c:
- PIC refactor
(*) sorry, but code in tcc should have a minimum of generic relevance