Compare commits

...

59 Commits

Author SHA1 Message Date
Mounir IDRASSI
a338258d30 Fix qualifiers on const array struct members
Some checks are pending
build and test / test-x86_64-linux (push) Waiting to run
build and test / test-x86_64-osx (push) Waiting to run
build and test / test-aarch64-osx (push) Waiting to run
build and test / test-x86_64-win32 (push) Waiting to run
build and test / test-i386-win32 (push) Waiting to run
build and test / test-arm64-win32 (push) Waiting to run
build and test / test-armv7-linux (push) Waiting to run
build and test / test-aarch64-linux (push) Waiting to run
build and test / test-riscv64-linux (push) Waiting to run
build and test / test-riscv64-linux-native (push) Waiting to run
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.
2026-06-13 22:26:02 +09:00
Meng Zhuo
fb4077f2da CI: fix win32 MSVC setup and arm64 test
Replace hardcoded VsDevCmd.bat Enterprise path with vswhere.exe +
vcvarsall.bat for dynamic VS discovery across editions and runner
image versions.  Architecture args: amd64, x86, amd64_arm64.

Split x86_64/i386 win32 jobs into separate build and test steps.

For arm64: keep build+test in one step (needs vcvarsall for lib.exe).
Prepend standalone LLVM bin to PATH so clang targets ARM64 natively
instead of VS's clang-cl inheriting the x64-hosted cross-compile env.
2026-06-12 10:26:51 +08:00
Meng Zhuo
724b24eeb4 riscv64: implement float/double constant loading 2026-06-12 10:26:51 +08:00
Meng Zhuo
d8aee9b26e riscv64: consolidate riscv intrinsics into lib-riscv.c
Replace the single-purpose lib/riscvflush.c with lib/lib-riscv.c
following the lib-arm64.c naming convention.
2026-06-12 10:26:50 +08:00
Meng Zhuo
b8f680a3b4 CI: use RISE runner to test riscv64 2026-06-02 16:47:08 +08:00
Meng Zhuo
1444843fd1 riscv64-link: fix C90 mixed declaration warning
Fix -Wdeclaration-after-statement: move tp_offset declaration
before the for-loop in R_RISCV_TPREL_HI20/LO12_I relocation
handler.
2026-05-31 22:36:41 +08:00
Meng Zhuo
6b53465347 riscv64: enable gen_clear_cache with I-cache flushing
Wire gen_clear_cache to __riscv64_clear_cache builtin, emitting
fence.i for instruction cache synchronization.
Add lib/riscvflush.c to provide __clear_cache for JIT bootstrap.
Extend tccrun.c I-cache flush guard to include RISC-V targets.
Add gen_clear_cache declaration to tcc.h riscv64 section.
2026-05-31 22:36:41 +08:00
Mounir IDRASSI
3b1fe97a59 win32: define arm64 Interlocked helpers in winnt.h
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.
2026-05-23 22:39:17 +09:00
Mounir IDRASSI
904e95cbdf win32: avoid Interlocked imports for tcc arm64
Windows/arm64 exposes Interlocked* operations as compiler intrinsics rather than kernel32 exports. When tcc -run self-compiles tcc.c, calls to InterlockedCompareExchange or InterlockedExchange can therefore remain as unresolved imports in the in-memory linker.

Route the TinyCC arm64 path through the existing __atomic helpers while keeping Interlocked* for other Windows compilers. The compile-lock initialization state machine remains unchanged.
2026-05-21 08:41:08 +09:00
Mounir IDRASSI
601a088214 win32: improve tccrun protection diagnostics
Report VirtualProtect failures with a Windows-specific -run diagnostic.
On Win64, also treat RtlAddFunctionTable failure as an error so unwind
registration problems are visible.
2026-05-15 21:41:46 +09:00
Mounir IDRASSI
1a54e47dda pe: add dll characteristic linker options
Add PE linker switches to set and clear DYNAMIC_BASE, NX_COMPAT,
HIGH_ENTROPY_VA and TERMINAL_SERVER_AWARE without changing x86/x64
defaults. HIGH_ENTROPY_VA is limited to 64-bit PE targets and implies
DYNAMIC_BASE; clearing DYNAMIC_BASE also clears HIGH_ENTROPY_VA.

When DYNAMIC_BASE is requested for an executable, emit base relocations
and clear RELOCS_STRIPPED so Windows ASLR can relocate the image. Keep
the existing ARM64 default hardening flags.
2026-05-15 21:40:44 +09:00
Mounir IDRASSI
37b7247796 win32: run cv2pdb without shell quoting issues
Invoke cv2pdb with _spawnvp on Windows so output paths are passed
as argv entries instead of through cmd.exe.

For non-Windows PE cross builds, keep the system() fallback but
shell-quote the executable name and size the PDB path dynamically.
2026-05-15 21:40:43 +09:00
Mounir IDRASSI
6a7c3df4d5 win32: make compile lock initialization thread-safe
Use interlocked state transitions around the lazily initialized Windows
CRITICAL_SECTION so concurrent first-time callers cannot race into a
double initialization.
2026-05-15 21:40:43 +09:00
Meng Zhuo
757507eb02 fix: skip TLS test on osx
tests/tests2/Makefile: skip 144_tls on OSX
2026-05-13 15:31:17 +08:00
Mounir IDRASSI
6728a64f1b win32: use VirtualAlloc for run memory 2026-05-13 14:25:22 +09:00
Meng Zhuo
256b4ef63c fix: TLS test failures on osx/win32/armv7
- 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)
2026-05-13 12:28:58 +08:00
Meng Zhuo
8502540b4a feat(i386): add TLS Local Exec code generation and linker relocations 2026-05-13 10:30:10 +08:00
Meng Zhuo
841ce0da03 feat(arm): add TLS Local Exec code generation and linker relocations 2026-05-13 10:29:44 +08:00
Meng Zhuo
8abaf10ab5 feat(arm64): add TLS Local Exec code generation and linker relocations 2026-05-13 09:37:59 +08:00
Meng Zhuo
11f5c6e1f9 feat(riscv64): add TLS Local Exec code generation and linker relocations 2026-05-13 09:37:42 +08:00
Meng Zhuo
ea26b85ac0 feat(x86_64): add TLS Local Exec support with tests 2026-05-13 09:07:31 +08:00
Meng Zhuo
8443e25bf5 feat: implement Thread Local Storage frontend support 2026-05-12 20:59:01 +08:00
inostensibl
fad812360b tccgen: fix void expression side effect regression
From 7e01b20362

reduced from an app that builds/runs with gcc or clang.
2026-05-09 22:11:58 -04:00
Meng Zhuo
2888e49f39 riscv64-gen: implement gen_clear_cache
Emit fence + fence.i (Zifencei extension) for I/D cache
synchronization, needed for tcc -run mode on RISC-V.
Zifencei is required by the RISC-V Linux ABI and present
on all Linux-capable cores, though technically not part
of the RV64G base ISA.
2026-05-07 16:07:53 +08:00
Meng Zhuo
c77339ab41 riscv64: emit .riscv.attributes section in ELF output 2026-05-07 11:44:46 +08:00
Meng Zhuo
7f764f340f riscv64-asm: complete AMO aq/rl suffixes for all ops
Add 48 tokens + handlers + dispatch cases for all AMO aq/rl
variants (amoswap, amoand, amoor, amoxor, amomax, amomaxu,
amomin, amominu; each x {.w,.d} x {.aq,.rl,.aqrl}).

GNU-compatible 2-dot naming via DEF_ASM_WITH_SUFFIXES.
Correct funct5 values: amoswap=0x01, amoand=0x0C, amoor=0x08,
amoxor=0x04, amomax=0x14, amomaxu=0x1C, amomin=0x10, amominu=0x18.

Extend 141_riscv_asm amo_main() with 10 representative aq/rl
variants covering all ops x ordering suffix combinations.

Verified against riscv64-linux-gnu-as 2.44.
2026-05-07 11:44:46 +08:00
Meng Zhuo
a672babc6f tests: fix 141_riscv_asm AMO crash, restore full test coverage
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.
2026-05-07 11:44:18 +08:00
grischka
44977b0de8 tcctest: restore & combine
Revert 199369bb17
- generating test.ref with tcc makes little sense.
- combine riscv_asm & conversion tests into only two files.
  (too many files are bad ...)
2026-05-06 18:52:01 +02:00
grischka
6daf1617ef configure & tcc.h: include & lib searchpath cleanup
configure:
- remove CONFIG_USE_LIBGCC, --with-libgcc
- remove CONFIG_LDDIR
- remove CONFIG_USR_INCLUDE
- add -q switch for quiet (=no) output

tcc.h:
- remove /usr/local/include&lib search paths

arm-gen.c:
- remove default_elfinterp() function

tccelf.c:
- setup the ELF-interp in tccelf_new()
2026-05-06 13:46:42 +02:00
grischka
923fba83f1 general: long double issues
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
2026-05-06 13:46:38 +02:00
grischka
7e01b20362 tccgen: void values, etc...
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
2026-05-06 13:46:27 +02:00
Meng Zhuo
016087c954 riscv64-asm: implement AMO, fcvt rounding, and fcvt encoding fixes
AMO (A-extension):
  18 base instructions: amoadd/swap/and/or/xor/max/maxu/min/minu .w/.d
  6 aq/rl suffixes: amoadd.w.aq/.rl/.aqrl, amoadd.d.aq/.rl/.aqrl
  Correct funct5 (GNU as verified):
    amoadd=0x00 amoswap=0x01 amoxor=0x04
    amoand=0x0C  amoor=0x08  amomax=0x14
    amomaxu=0x1C amomin=0x10 amominu=0x18
  R-type opcode 0x2F, aq/rl in bits [26:25]

FCVT rounding modes (GNU operand syntax):
  fcvt.w.s rd, rs1 [, rtz/rne/rup] -- optional 3rd operand
  asm_fcvt_opcode() handler with asm_fcvt_rm() helper
  Keywords rne=0, rtz=1, rdn=2, rup=3, rmm=4

FCVT encoding fixes:
  fcvt.s.d: funct7 0x40->0x20
  fcvt.d.s: funct7 0x42->0x21

Tests: 144 CSR, 145 F/D cmp+cvt, 146 AMO, 147 fcvt round.
All verified against riscv64-linux-gnu-as 2.44 on Spacemit X100.
2026-05-06 19:18:17 +08:00
Meng Zhuo
f8011ea9b7 riscv64-asm: implement CSR pseudo-instructions
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.
2026-05-06 18:33:15 +08:00
Meng Zhuo
199369bb17 tests: generate test.ref with TCC instead of host CC
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.
2026-05-06 14:43:30 +08:00
Meng Zhuo
3935c3bb55 riscv64-asm: add basic F/D extension instructions (fadd/fsub/fmul/fdiv)
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.
2026-05-06 12:30:34 +08:00
Meng Zhuo
273978b927 riscv64-asm: implement pseudo-instructions sext.w, fmv, fneg
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.
2026-05-06 12:30:34 +08:00
Meng Zhuo
2a33daedca tccasm: extend .reloc directive for RISCV64 relocation types
Previously, .reloc unconditionally errored on RISCV64 — only ARM64
had permission to use R_AARCH64_CALL26.  Now supports common RISCV64
relocations: R_RISCV_CALL, CALL_PLT, BRANCH, JAL, PCREL_HI20,
PCREL_LO12_I/S, 32_PCREL, 32, 64.

Restructured to use string-to-reloc-type mapping, shared across all
targets.
2026-05-06 12:30:34 +08:00
Meng Zhuo
3257afa160 riscv64-link: add R_RISCV_RELATIVE case in relocate()
arm64-link.c has R_AARCH64_RELATIVE handling.  Without this case,
incoming R_RISCV_RELATIVE relocations from object files fall through
to the 'FIXME: handle reloc type' default path.

P1.7 (NEED_BUILD_GOT guards) is not needed: NEED_RELOC_TYPE and
NEED_BUILD_GOT are always defined in the build context (tcc.h:1608).
2026-05-06 12:30:34 +08:00
Meng Zhuo
419b527657 riscv64-asm: implement far branches via expansion
For branch targets that are external/static symbols (where the
offset cannot be determined at assembly time), expand to:
  b<inverse> .+8       # skip if condition false
  auipc t0, %pcrel_hi  # load target upper bits
  jalr x0, %pcrel_lo(t0)  # jump

This handles the ±4 KiB B-type limitation without requiring
linker relaxation (R_RISCV_RELAX).  Local immediates within
range still use the compact B-type encoding.
2026-05-06 12:30:34 +08:00
Meng Zhuo
5c2240a896 riscv64-asm: remove long long 'not implemented' errors in inline asm
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.
2026-05-06 12:30:34 +08:00
Meng Zhuo
366569eb7a riscv64-asm: fix neg/negw pseudo-instruction encoding
neg/negw were incorrectly using asm_emit_i (I-type/xori) with an
immediate of 1, producing 'xori rd, rs, 1' instead of the intended
'sub rd, x0, rs'.  Fixed to use asm_emit_r (R-type) with proper
SUB/SUBW opcode (0x33/0x3B, func3=0, func7=0x20).

The old code generated wrong machine code for these pseudo-instructions.
negw now also correctly uses OP-32 (0x3B) for 32-bit subtraction.
2026-05-06 12:30:34 +08:00
Meng Zhuo
ff917c09aa riscv64: implement gen_cvt_sxtw with addiw instruction
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).
2026-05-06 12:30:34 +08:00
Meng Zhuo
9f0915a506 riscv64: implement gen_cvt_csti for narrow int promotion
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.
2026-05-06 12:30:34 +08:00
Meng Zhuo
69c8e92566 riscv64: add PROMOTE_RET for narrow return ABI compliance
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).
2026-05-06 12:30:34 +08:00
grischka
30afb50e64 arm64-win32 review: fix problems and pass tests
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).
2026-05-04 12:51:10 +02:00
Benjamin Oldenburg
576cd2a923 arm64-win32 support: symbolic op-code constants
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 previous 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
2026-05-04 12:50:42 +02:00
Benjamin Oldenburg
44e6853cb1 arm64-win32 support: target PE & varargs 2026-05-04 12:49:53 +02:00
Benjamin Oldenburg
5c728d6506 arm64-win32 support: improve HFA & struct 2026-05-04 12:49:53 +02:00
Benjamin Oldenburg
90d17c9748 arm64-win32 support: use arm64_sub_sp() 2026-05-04 12:49:53 +02:00
Benjamin Oldenburg
99713bcbfa arm64-win32 support: use arm64_sym() 2026-05-04 12:49:53 +02:00
Benjamin Oldenburg
303badef22 arm64-win32 support: fix scaled mask 2026-05-04 12:49:53 +02:00
Benjamin Oldenburg
41bcb4a78f arm64-win32 support : linker & -run 2026-05-04 12:49:53 +02:00
Benjamin Oldenburg
f459aff5f6 arm64-win32 support : arm64 assembler 2026-05-04 12:49:53 +02:00
Benjamin Oldenburg
ea823189d6 arm64-win32 support : tests 2026-05-04 12:49:53 +02:00
Benjamin Oldenburg
03d58b0746 arm64-win32 support : runtime 2026-05-04 12:49:53 +02:00
Benjamin Oldenburg
ff5d3b4874 arm64-win32 support : configure & Makefiles
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
2026-05-04 12:49:25 +02:00
grischka
d9a6d9aec0 reverts (11/2025 - 04/2026)
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
2026-05-03 11:09:45 +02:00
H-language
a66ac623b2 win32: add missing AttachConsole export to kernel32.def
AttachConsole has been exported from kernel32.dll since Windows XP
but was absent from TCC's import definitions, causing
"undefined symbol 'AttachConsole'" at link time for code that uses it.
2026-05-01 09:58:03 +12:00
Cyan Ogilvie
4768b11737 tccrun: fix OOB read in elf_output_obj after tcc_relocate with -g
When -g is set, cleanup_sections() preserves all sections after relocate,
including the SHT_RELX relocation sections. cleanup_symbols() drops all
STB_LOCAL symbols from the symbol table, which renumbers the remaining
globals. The kept relas still hold r_info indices from the pre-cleanup
numbering, but nothing rewrites them. A later elf_output_obj() call ends
up in sort_syms() / update_relocs(), which allocates old_to_new_syms[]
sized for the post-cleanup (smaller) symtab and then indexes it with the
stale (larger) sym_index values from those rela entries, reading past
the end of the array.

Fix: when do_debug is set, build an old->new index map while re-adding
globals, then walk every retained SHT_RELX section linked to the symtab
and rewrite its r_info entries. Locals (and the undef sym at 0) map to
0 by tcc_mallocz, so relocations against dropped locals now refer to
SHN_UNDEF - the best we can do without preserving the locals themselves.

Reproducible with libtcc_debug.c if the program contains enough static
helpers / string literals for the symtab to actually shrink; observed
under valgrind in jitc (https://github.com/cyanogilvie/jitc) when
compiling re2c-generated state machines with -g.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 15:37:49 -03:00
84 changed files with 7994 additions and 1624 deletions

View File

@ -34,56 +34,49 @@ jobs:
timeout-minutes: 6
steps:
- uses: actions/checkout@v4
- name: make & test tcc (x86_64-win32)
- name: build tcc (x86_64-win32)
shell: cmd
run: |
echo ::group:: setup msys mingw64-gcc
set MSYS2_PATH_TYPE=inherit
set MSYSTEM=MINGW64
set CHERE_INVOKING=yes
C:\msys64\usr\bin\bash -l -c "pacman -S --noconfirm mingw-w64-x86_64-gcc"
echo ::endgroup::
C:\msys64\usr\bin\bash -l -c "./configure && make && make test -k"
- uses: ilammy/msvc-dev-cmd@v1
with:
arch: amd64
- name: build with MSVC (x86_64-win32)
shell: cmd
run: |
echo ::group:: run build-tcc.bat
cd win32
call build-tcc.bat -t 64 -c cl
echo ::endgroup::
.\tcc -I.. libtcc.dll -v ../tests/libtcc_test.c -o libtest.exe && .\libtest.exe
.\tcc -I.. libtcc.dll -run ../tests/libtcc_test.c
for /f "delims=" %%i in ('vswhere.exe -latest -property installationPath') do call "%%i\VC\Auxiliary\Build\vcvarsall.bat" amd64
call build-tcc.bat -c cl -t x86_64
- name: test (x86_64-win32)
shell: cmd
run: |
cd tests
call test-win32.bat all -k
test-i386-win32:
runs-on: windows-2025
timeout-minutes: 6
steps:
- uses: actions/checkout@v4
- name: make & test tcc (i386-win32)
- name: build tcc (i386-win32)
shell: cmd
run: |
echo ::group:: setup msys mingw32-gcc
set MSYS2_PATH_TYPE=inherit
set MSYSTEM=MINGW32
set CHERE_INVOKING=yes
C:\msys64\usr\bin\bash -l -c "pacman -S --noconfirm mingw-w64-i686-gcc"
echo ::endgroup::
C:\msys64\usr\bin\bash -l -c "./configure && make && make test -k"
- uses: ilammy/msvc-dev-cmd@v1
with:
arch: x86
- name: build with MSVC (i386-win32)
shell: cmd
run: |
echo ::group:: run build-tcc.bat
cd win32
call build-tcc.bat -t 32 -c cl
echo ::endgroup::
.\tcc -I.. libtcc.dll -v ../tests/libtcc_test.c -o libtest.exe && .\libtest.exe
.\tcc -I.. libtcc.dll -run ../tests/libtcc_test.c
for /f "delims=" %%i in ('vswhere.exe -latest -property installationPath') do call "%%i\VC\Auxiliary\Build\vcvarsall.bat" x86
call build-tcc.bat -c cl -t i386
- name: test (i386-win32)
shell: cmd
run: |
cd tests
call test-win32.bat -p c:\mingw32\bin all -k
test-arm64-win32:
runs-on: windows-11-arm
timeout-minutes: 6
steps:
- uses: actions/checkout@v4
- name: build and test (arm64-win32)
shell: cmd
run: |
cd win32
for /f "delims=" %%i in ('vswhere.exe -latest -property installationPath') do call "%%i\VC\Auxiliary\Build\vcvarsall.bat" amd64_arm64
call build-tcc.bat -c cl -t arm64
set "PATH=C:\Program Files\LLVM\bin;%CD%;%PATH%"
cd ..\tests
call test-win32.bat -c clang all -k
test-armv7-linux:
runs-on: ubuntu-22.04
@ -138,3 +131,13 @@ jobs:
run: |
echo "::endgroup::" # flatten 'run container'
./configure && make && make test -k
test-riscv64-linux-native:
runs-on: ubuntu-24.04-riscv
timeout-minutes: 8
steps:
- uses: actions/checkout@v4
- name: make & test tcc (riscv64-linux native)
run: |
uname -m # riscv64
./configure && make && make test -k

1
.gitignore vendored
View File

@ -62,7 +62,6 @@ tests/*-cc*
tests/*-tcc*
tests/libtcc_test
tests/libtcc_test_mt
tests/libtcc_test_xor_rex
tests/asm-c-connect
tests/asm-c-connect-sep
tests/vla_test

View File

@ -31,9 +31,11 @@ ifdef CONFIG_WIN32
LIBTCCDEF = libtcc.def
endif
ifneq ($(CONFIG_debug),yes)
LDFLAGS += -s
ifneq ($(CC_NAME),clang)
LDFLAGS += -s
endif
endif
NATIVE_TARGET = $(ARCH)-win$(if $(findstring arm,$(ARCH)),ce,32)
NATIVE_TARGET = $(if $(findstring arm64,$(ARCH)),arm64-win32,$(ARCH)-win$(if $(findstring arm,$(ARCH)),ce,32))
else
CFG = -unx
LIBS+=-lm
@ -113,6 +115,7 @@ DEF-arm64-osx = $(DEF-arm64) -DTCC_TARGET_MACHO
DEF-arm64-FreeBSD = $(DEF-arm64) -DTARGETOS_FreeBSD
DEF-arm64-NetBSD = $(DEF-arm64) -DTARGETOS_NetBSD
DEF-arm64-OpenBSD = $(DEF-arm64) -DTARGETOS_OpenBSD
DEF-arm64-win32 = $(DEF-arm64) -DTCC_TARGET_PE
DEF-riscv64 = -DTCC_TARGET_RISCV64
DEF-c67 = -DTCC_TARGET_C67 -w # disable warnigs
DEF-x86_64-FreeBSD = $(DEF-x86_64) -DTARGETOS_FreeBSD
@ -130,7 +133,7 @@ TCCDOCS = tcc.1 tcc-doc.html tcc-doc.info
all: $(PROGS) $(TCCLIBS) $(TCCDOCS)
# cross compiler targets to build
TCC_X = i386 x86_64 i386-win32 x86_64-win32 x86_64-osx arm arm64 arm-wince c67
TCC_X = i386 x86_64 i386-win32 x86_64-win32 x86_64-osx arm arm64 arm64-win32 arm-wince c67
TCC_X += riscv64 arm64-osx
# TCC_X += arm-fpa arm-fpa-ld arm-vfp arm-eabi
@ -155,10 +158,27 @@ all : cross
endif
# --------------------------------------------
T = $(or $(CROSS_TARGET),$(NATIVE_TARGET),unknown)
T = $(or $(CROSS_TARGET),$(NATIVE_TARGET))
X = $(if $(CROSS_TARGET),$(CROSS_TARGET)-)
ifneq ($(T),$(NATIVE_TARGET))
$(if $(DEF-$T),,$(error error: unknown target: '$T'))
ifneq ($(CONFIG_WIN32),yes)
DEF-win = -DCONFIG_TCCDIR="\"$(tccdir)/win32\""
endif
# some default config for cross compilers
TRIPLET-i386 = i686-linux-gnu
TRIPLET-x86_64 = x86_64-linux-gnu
TRIPLET-arm = arm-linux-gnueabihf
TRIPLET-arm64 = aarch64-linux-gnu
TRIPLET-riscv64 = riscv64-linux-gnu
ifneq ($(TRIPLET-$T),)
# assume support files in "/usr/<triplet>"
ROOT-$T = /usr/$(TRIPLET-$T)
INC-$T = {B}/include:{R}/include
LIB-$T = {R}/lib:{B}
CRT-$T = {R}/lib
endif
DEFINES += $(DEF-$T)
DEFINES += $(if $(ROOT-$T),-DCONFIG_SYSROOT="\"$(ROOT-$T)\"")
DEFINES += $(if $(CRT-$T),-DCONFIG_TCC_CRTPREFIX="\"$(CRT-$T)\"")
@ -166,36 +186,17 @@ DEFINES += $(if $(LIB-$T),-DCONFIG_TCC_LIBPATHS="\"$(LIB-$T)\"")
DEFINES += $(if $(INC-$T),-DCONFIG_TCC_SYSINCLUDEPATHS="\"$(INC-$T)\"")
DEFINES += $(if $(ELF-$T),-DCONFIG_TCC_ELFINTERP="\"$(ELF-$T)\"")
DEFINES += $(DEF-$(or $(findstring win,$T),unx))
ifneq ($(X),)
$(if $(DEF-$T),,$(error error: unknown target: '$T'))
DEF-$(NATIVE_TARGET) =
DEF-$T += -DCONFIG_TCC_CROSSPREFIX="\"$X\""
ifneq ($(CONFIG_WIN32),yes)
DEF-win += -DCONFIG_TCCDIR="\"$(tccdir)/win32\""
endif
else
# using values from config.h
DEF-$(NATIVE_TARGET) =
DEFINES += -DCONFIG_TCC_CROSSPREFIX="\"$X\""
endif
# include custom configuration (see make help)
-include config-extra.mak
ifneq ($(T),$(NATIVE_TARGET))
# assume support files for cross-targets in "/usr/<triplet>" by default
TRIPLET-i386 ?= i686-linux-gnu
TRIPLET-x86_64 ?= x86_64-linux-gnu
TRIPLET-arm ?= arm-linux-gnueabi
TRIPLET-arm64 ?= aarch64-linux-gnu
TRIPLET-riscv64 ?= riscv64-linux-gnu
MARCH-i386 ?= i386-linux-gnu
MARCH-$T ?= $(TRIPLET-$T)
TR = $(if $(TRIPLET-$T),$T,ignored)
CRT-$(TR) ?= /usr/$(TRIPLET-$T)/lib
LIB-$(TR) ?= {B}:/usr/$(TRIPLET-$T)/lib:/usr/lib/$(MARCH-$T)
INC-$(TR) ?= {B}/include:/usr/$(TRIPLET-$T)/include:/usr/include
endif
# so one can use: make EXTRA-DEFS=...
DEFINES += $(EXTRA-DEFS)
# find config.h with 'out of tree' builds
DEFINES += -I$(TOP)
CORE_FILES = tcc.c tcctools.c libtcc.c tccpp.c tccgen.c tccdbg.c tccelf.c tccasm.c tccrun.c
CORE_FILES += tcc.h config.h libtcc.h tcctok.h
@ -206,14 +207,14 @@ x86_64-win32_FILES = $(x86_64_FILES) tccpe.c
x86_64-osx_FILES = $(x86_64_FILES) tccmacho.c
arm_FILES = $(CORE_FILES) arm-gen.c arm-link.c arm-asm.c arm-tok.h
arm-wince_FILES = $(arm_FILES) tccpe.c
arm-eabihf_FILES = $(arm_FILES)
arm-fpa_FILES = $(arm_FILES)
arm-fpa-ld_FILES = $(arm_FILES)
arm-vfp_FILES = $(arm_FILES)
arm-eabi_FILES = $(arm_FILES)
arm-eabihf_FILES = $(arm_FILES)
arm64_FILES = $(CORE_FILES) arm64-gen.c arm64-link.c arm64-asm.c
arm64_FILES = $(CORE_FILES) arm64-gen.c arm64-link.c arm64-asm.c arm64-tok.h
arm64-osx_FILES = $(arm64_FILES) tccmacho.c
arm64-win32_FILES = $(arm64_FILES) tccpe.c
c67_FILES = $(CORE_FILES) c67-gen.c c67-link.c tcccoff.c
riscv64_FILES = $(CORE_FILES) riscv64-gen.c riscv64-link.c riscv64-asm.c
@ -237,7 +238,6 @@ $(CROSS_TARGET)-tcc.o : DEFINES += -DONE_SOURCE=0
endif
# native tcc always made from tcc.o and libtcc.[so|a]
tcc.o : DEFINES += -DONE_SOURCE=0
DEFINES += -I$(TOP)
GITHASH:=$(shell git rev-parse --abbrev-ref HEAD 2>/dev/null || echo no)
ifneq ($(GITHASH),no)
@ -336,7 +336,7 @@ FORCE:
# some versions of gnu-make do not recognize 'command' as a shell builtin
WHICH = sh -c 'command -v $1'
run-if = $(if $(shell $(call WHICH,$1)),$S $1 $2)
run-if = $(if $(shell $(call WHICH,$1x)),$S $1 $2,@true||echo "(skipping $@ - no $1)")
S = $(if $(findstring yes,$(SILENT)),@$(info * $@))
# --------------------------------------------------------------------------
@ -497,14 +497,14 @@ distclean: clean
help:
@echo "make"
@echo " build native compiler (from separate objects)"
@echo "make cross"
@echo " build cross compilers (from one source)"
@echo "make ONE_SOURCE=no/yes SILENT=no/yes"
@echo " force building from separate/one object(s), less/more silently"
@echo "make cross-TARGET"
@echo " build one specific cross compiler for 'TARGET'. Currently supported:"
@echo " $(wordlist 1,8,$(TCC_X))"
@echo " $(wordlist 9,99,$(TCC_X))"
@echo "make cross"
@echo " build all cross compilers"
@echo "make test"
@echo " run all tests"
@echo "make tests2.all / make tests2.37 / make tests2.37+"

110
arm-gen.c
View File

@ -124,16 +124,17 @@ enum {
#define LDOUBLE_ALIGN 4
#endif
#if LDOUBLE_SIZE == 8
# define TCC_USING_DOUBLE_FOR_LDOUBLE 1
#endif
/* maximum alignment (for aligned attribute support) */
#define MAX_ALIGN 8
#define CHAR_IS_UNSIGNED
#ifdef TCC_ARM_HARDFLOAT
# define ARM_FLOAT_ABI ARM_HARD_FLOAT
#else
# define ARM_FLOAT_ABI ARM_SOFTFP_FLOAT
#endif
#define ARM_SOFTFP_FLOAT 0
#define ARM_HARD_FLOAT 1
/******************************************************/
#else /* ! TARGET_DEFS_ONLY */
@ -156,8 +157,6 @@ ST_DATA const char * const target_machine_defs =
#endif
;
enum float_abi float_abi;
ST_DATA const int reg_classes[NB_REGS] = {
/* r0 */ RC_INT | RC_R0,
/* r1 */ RC_INT | RC_R1,
@ -176,6 +175,7 @@ ST_DATA const int reg_classes[NB_REGS] = {
#endif
};
static int float_abi;
static int func_sub_sp_offset, last_itod_magic;
static int leaffunc;
@ -237,16 +237,6 @@ static int regmask(int r) {
/******************************************************/
#if defined(TCC_ARM_EABI) && !defined(CONFIG_TCC_ELFINTERP)
const char *default_elfinterp(struct TCCState *s)
{
if (s->float_abi == ARM_HARD_FLOAT)
return "/lib/ld-linux-armhf.so.3";
else
return "/lib/ld-linux.so.3";
}
#endif
void o(uint32_t i)
{
/* this is a good place to start adding big-endian support*/
@ -582,7 +572,7 @@ static void load_value(SValue *sv, int r)
void load(int r, SValue *sv)
{
int v, ft, fc, fr, sign;
uint32_t op;
uint32_t op, base;
SValue v1;
fr = sv->r;
@ -598,7 +588,15 @@ void load(int r, SValue *sv)
v = fr & VT_VALMASK;
if (fr & VT_LVAL) {
uint32_t base = 0xB; // fp
if ((fr & VT_SYM) && sv->sym->type.t & VT_TLS) {
uint32_t op;
o(0xee1d0fe0); /* mrc p15, 0, lr, c13, c0, 3 */
op = 0xe510e000; /* ldr r, [lr, #0] */
greloca(cur_text_section, sv->sym, ind, R_ARM_TLS_LE32, 0);
o(op | (intr(r) << 12));
return;
}
base = 0xB; // fp
if(v == VT_LLOCAL) {
v1.type.t = VT_PTR;
v1.r = VT_LOCAL | VT_LVAL;
@ -635,12 +633,9 @@ void load(int r, SValue *sv)
op=0xED100100;
if(!sign)
op|=0x800000;
#if LDOUBLE_SIZE == 8
if ((ft & VT_BTYPE) != VT_FLOAT)
op|=0x8000;
#else
if ((ft & VT_BTYPE) == VT_DOUBLE)
op|=0x8000;
#if LDOUBLE_SIZE != 8
else if ((ft & VT_BTYPE) == VT_LDOUBLE)
op|=0x400000;
#endif
@ -716,7 +711,7 @@ void store(int r, SValue *sv)
{
SValue v1;
int v, ft, fc, fr, sign;
uint32_t op;
uint32_t op, base;
fr = sv->r;
ft = sv->type.t;
@ -731,7 +726,15 @@ void store(int r, SValue *sv)
v = fr & VT_VALMASK;
if (fr & VT_LVAL || fr == VT_LOCAL) {
uint32_t base = 0xb; /* fp */
if ((fr & VT_SYM) && sv->sym->type.t & VT_TLS) {
uint32_t op;
o(0xee1d0fe0); /* mrc p15, 0, lr, c13, c0, 3 */
op = 0xe500e000; /* str r, [lr, #0] */
greloca(cur_text_section, sv->sym, ind, R_ARM_TLS_LE32, 0);
o(op | (intr(r) << 12));
return;
}
base = 0xb; /* fp */
if(v < VT_CONST) {
base=intr(v);
v=VT_LOCAL;
@ -760,13 +763,10 @@ void store(int r, SValue *sv)
op=0xED000100;
if(!sign)
op|=0x800000;
#if LDOUBLE_SIZE == 8
if ((ft & VT_BTYPE) != VT_FLOAT)
op|=0x8000;
#else
if ((ft & VT_BTYPE) == VT_DOUBLE)
op|=0x8000;
if ((ft & VT_BTYPE) == VT_LDOUBLE)
#if LDOUBLE_SIZE != 8
else if ((ft & VT_BTYPE) == VT_LDOUBLE)
op|=0x400000;
#endif
o(op|(fpr(r)<<12)|(fc>>2)|(base<<16));
@ -904,15 +904,6 @@ static void gen_bounds_epilog(void)
}
#endif
static int unalias_ldbl(int btype)
{
#if LDOUBLE_SIZE == 8
if (btype == VT_LDOUBLE)
btype = VT_DOUBLE;
#endif
return btype;
}
/* Return whether a structure is an homogeneous float aggregate or not.
The answer is true if all the elements of the structure are of the same
primitive float type and there is less than 4 elements.
@ -926,9 +917,10 @@ static int is_hgen_float_aggr(CType *type)
ref = type->ref->next;
if (ref) {
btype = unalias_ldbl(ref->type.t & VT_BTYPE);
btype = ref->type.t & VT_BTYPE;
if (btype == VT_FLOAT || btype == VT_DOUBLE) {
for(; ref && btype == unalias_ldbl(ref->type.t & VT_BTYPE); ref = ref->next, nb_fields++);
for(; ref && btype == (ref->type.t & VT_BTYPE); ref = ref->next, nb_fields++)
;
return !ref && nb_fields <= 4;
}
}
@ -1254,7 +1246,6 @@ again:
size = 8;
else
size = LDOUBLE_SIZE;
if (size == 12)
r |= 0x400000;
else if(size == 8)
@ -1946,15 +1937,13 @@ void gen_opf(int op)
vswap();
c2 = is_fconst();
x=0xEE000100;
#if LDOUBLE_SIZE == 8
if ((vtop->type.t & VT_BTYPE) != VT_FLOAT)
x|=0x80;
#else
if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE)
x|=0x80;
#if LDOUBLE_SIZE != 8
else if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE)
x|=0x80000;
#endif
switch(op)
{
case '+':
@ -2190,6 +2179,12 @@ ST_FUNC void gen_cvt_itof(int t)
func=TOK___floatundisf;
else
func=TOK___floatdisf;
} else if((t & VT_BTYPE) == VT_DOUBLE) {
func_type = &func_double_type;
if(vtop->type.t & VT_UNSIGNED)
func=TOK___floatundidf;
else
func=TOK___floatdidf;
#if LDOUBLE_SIZE != 8
} else if((t & VT_BTYPE) == VT_LDOUBLE) {
func_type = &func_ldouble_type;
@ -2197,15 +2192,7 @@ ST_FUNC void gen_cvt_itof(int t)
func=TOK___floatundixf;
else
func=TOK___floatdixf;
} else if((t & VT_BTYPE) == VT_DOUBLE) {
#else
} else if((t & VT_BTYPE) == VT_DOUBLE || (t & VT_BTYPE) == VT_LDOUBLE) {
#endif
func_type = &func_double_type;
if(vtop->type.t & VT_UNSIGNED)
func=TOK___floatundidf;
else
func=TOK___floatdidf;
}
if(func_type) {
vpushsym(func_type, external_helper_sym(func));
@ -2239,14 +2226,12 @@ void gen_cvt_ftoi(int t)
if(u) {
if(r2 == VT_FLOAT)
func=TOK___fixunssfsi;
else if(r2 == VT_DOUBLE)
func=TOK___fixunsdfsi;
#if LDOUBLE_SIZE != 8
else if(r2 == VT_LDOUBLE)
func=TOK___fixunsxfsi;
else if(r2 == VT_DOUBLE)
#else
else if(r2 == VT_LDOUBLE || r2 == VT_DOUBLE)
#endif
func=TOK___fixunsdfsi;
} else {
r=fpr(gv(RC_FLOAT));
r2=intr(vtop->r=get_reg(RC_INT));
@ -2257,14 +2242,12 @@ void gen_cvt_ftoi(int t)
} else if(t == VT_LLONG) { // unsigned handled in gen_cvt_ftoi1
if(r2 == VT_FLOAT)
func=TOK___fixsfdi;
else if(r2 == VT_DOUBLE)
func=TOK___fixdfdi;
#if LDOUBLE_SIZE != 8
else if(r2 == VT_LDOUBLE)
func=TOK___fixxfdi;
else if(r2 == VT_DOUBLE)
#else
else if(r2 == VT_LDOUBLE || r2 == VT_DOUBLE)
#endif
func=TOK___fixdfdi;
}
if(func) {
vpush_helper_func(func);
@ -2283,8 +2266,9 @@ void gen_cvt_ftoi(int t)
void gen_cvt_ftof(int t)
{
#ifdef TCC_ARM_VFP
uint32_t r = gv(RC_FLOAT);
if(((vtop->type.t & VT_BTYPE) == VT_FLOAT) != ((t & VT_BTYPE) == VT_FLOAT)) {
uint32_t r = vfpr(gv(RC_FLOAT));
r = vfpr(r);
o(0xEEB70AC0|(r<<12)|r|T2CPR(vtop->type.t));
}
#else

View File

@ -18,11 +18,6 @@
#define PCRELATIVE_DLLPLT 1
#define RELOCATE_DLLPLT 1
enum float_abi {
ARM_SOFTFP_FLOAT,
ARM_HARD_FLOAT,
};
#else /* !TARGET_DEFS_ONLY */
#include "tcc.h"
@ -49,6 +44,7 @@ ST_FUNC int code_reloc (int reloc_type)
case R_ARM_TARGET1:
case R_ARM_MOVT_PREL:
case R_ARM_MOVW_PREL_NC:
case R_ARM_TLS_LE32:
return 0;
case R_ARM_PC24:
@ -75,6 +71,7 @@ ST_FUNC int gotplt_entry_type (int reloc_type)
case R_ARM_COPY:
case R_ARM_GLOB_DAT:
case R_ARM_JUMP_SLOT:
case R_ARM_TLS_LE32:
return NO_GOTPLT_ENTRY;
case R_ARM_PC24:
@ -435,6 +432,36 @@ ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr,
#endif
/* do nothing */
return;
case R_ARM_TLS_LE32:
{
ElfW(Sym) *sym;
Section *sec;
int32_t x;
addr_t tls_start = 0, tls_end = 0, tls_align = 1;
int i;
sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
sec = s1->sections[sym->st_shndx];
for (i = 1; i < s1->nb_sections; i++) {
Section *s = s1->sections[i];
if (s->sh_flags & SHF_TLS && s->sh_size) {
if (!tls_start || s->sh_addr < tls_start)
tls_start = s->sh_addr;
if (s->sh_addr + s->sh_size > tls_end)
tls_end = s->sh_addr + s->sh_size;
if (s->sh_addralign > tls_align)
tls_align = s->sh_addralign;
}
}
if (tls_end > tls_start) {
x = val - tls_start + 8;
} else {
x = val - sec->sh_addr - sec->data_offset + 8;
}
add32le(ptr, x);
}
return;
default:
fprintf(stderr,"FIXME: handle reloc type %d at %x [%p] to %x\n",
type, (unsigned)addr, ptr, (unsigned)val);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -43,6 +43,8 @@ ST_FUNC int code_reloc (int reloc_type)
case R_AARCH64_LDST32_ABS_LO12_NC:
case R_AARCH64_LDST16_ABS_LO12_NC:
case R_AARCH64_LDST8_ABS_LO12_NC:
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
case R_AARCH64_TLSLE_ADD_TPREL_LO12:
case R_AARCH64_GLOB_DAT:
case R_AARCH64_COPY:
return 0;
@ -50,6 +52,8 @@ ST_FUNC int code_reloc (int reloc_type)
case R_AARCH64_JUMP26:
case R_AARCH64_CALL26:
case R_AARCH64_JUMP_SLOT:
case R_AARCH64_CONDBR19:
case R_AARCH64_TSTBR14:
return 1;
}
return -1;
@ -76,6 +80,10 @@ ST_FUNC int gotplt_entry_type (int reloc_type)
case R_AARCH64_GLOB_DAT:
case R_AARCH64_JUMP_SLOT:
case R_AARCH64_COPY:
case R_AARCH64_CONDBR19:
case R_AARCH64_TSTBR14:
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
case R_AARCH64_TLSLE_ADD_TPREL_LO12:
return NO_GOTPLT_ENTRY;
case R_AARCH64_ABS32:
@ -127,17 +135,18 @@ ST_FUNC void relocate_plt(TCCState *s1)
uint64_t off = (got >> 12) - (plt >> 12);
if ((off + ((uint32_t)1 << 20)) >> 21)
tcc_error_noabort("Failed relocating PLT (off=0x%lx, got=0x%lx, plt=0x%lx)", (long)off, (long)got, (long)plt);
write32le(p, 0xa9bf7bf0); // stp x16,x30,[sp,#-16]!
write32le(p + 4, (0x90000010 | // adrp x16,...
write32le(p, ARM64_STP_X_PRE | ARM64_RT(16) | ARM64_RT2(30) |
ARM64_RN(31) | ARM64_IMM7(-2)); // stp x16,x30,[sp,#-16]!
write32le(p + 4, (ARM64_ADRP | ARM64_RD(16) | // adrp x16,...
(off & 0x1ffffc) << 3 | (off & 3) << 29));
write32le(p + 8, (0xf9400211 | // ldr x17,[x16,#...]
write32le(p + 8, (ARM64_LDR_X | ARM64_RT(17) | ARM64_RN(16) | // ldr x17,[x16,#...]
(got & 0xff8) << 7));
write32le(p + 12, (0x91000210 | // add x16,x16,#...
write32le(p + 12, (ARM64_ADD_IMM | ARM64_SF(1) | ARM64_RD(16) | ARM64_RN(16) | // add x16,x16,#...
(got & 0xfff) << 10));
write32le(p + 16, 0xd61f0220); // br x17
write32le(p + 20, 0xd503201f); // nop
write32le(p + 24, 0xd503201f); // nop
write32le(p + 28, 0xd503201f); // nop
write32le(p + 16, ARM64_BR | ARM64_RN(17)); // br x17
write32le(p + 20, ARM64_NOP); // nop
write32le(p + 24, ARM64_NOP); // nop
write32le(p + 28, ARM64_NOP); // nop
p += 32;
got = s1->got->sh_addr;
while (p < p_end) {
@ -146,13 +155,13 @@ ST_FUNC void relocate_plt(TCCState *s1)
uint64_t off = (addr >> 12) - (pc >> 12);
if ((off + ((uint32_t)1 << 20)) >> 21)
tcc_error_noabort("Failed relocating PLT (off=0x%lx, addr=0x%lx, pc=0x%lx)", (long)off, (long)addr, (long)pc);
write32le(p, (0x90000010 | // adrp x16,...
write32le(p, (ARM64_ADRP | ARM64_RD(16) | // adrp x16,...
(off & 0x1ffffc) << 3 | (off & 3) << 29));
write32le(p + 4, (0xf9400211 | // ldr x17,[x16,#...]
write32le(p + 4, (ARM64_LDR_X | ARM64_RT(17) | ARM64_RN(16) | // ldr x17,[x16,#...]
(addr & 0xff8) << 7));
write32le(p + 8, (0x91000210 | // add x16,x16,#...
write32le(p + 8, (ARM64_ADD_IMM | ARM64_SF(1) | ARM64_RD(16) | ARM64_RN(16) | // add x16,x16,#...
(addr & 0xfff) << 10));
write32le(p + 12, 0xd61f0220); // br x17
write32le(p + 12, ARM64_BR | ARM64_RN(17)); // br x17
p += 16;
}
}
@ -238,8 +247,23 @@ ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr,
return;
case R_AARCH64_ADR_PREL_PG_HI21: {
uint64_t off = (val >> 12) - (addr >> 12);
#ifdef TCC_TARGET_PE
/* Weak undefined symbols resolve to address 0 on PE. ADRP cannot
encode that from the default 64-bit image base, so materialize
zero directly and let the paired ADD handle any low addend. */
if ((off + ((uint64_t)1 << 20)) >> 21) {
ElfW(Sym) *sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
if (sym->st_shndx == SHN_UNDEF
&& ELFW(ST_BIND)(sym->st_info) == STB_WEAK) {
write32le(ptr, 0xd2800000 | (read32le(ptr) & 0x1f));
return;
}
tcc_error_noabort("R_AARCH64_ADR_PREL_PG_HI21 relocation failed");
}
#else
if ((off + ((uint64_t)1 << 20)) >> 21)
tcc_error_noabort("R_AARCH64_ADR_PREL_PG_HI21 relocation failed");
#endif
write32le(ptr, ((read32le(ptr) & 0x9f00001f) |
(off & 0x1ffffc) << 3 | (off & 3) << 29));
return;
@ -265,19 +289,58 @@ ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr,
write32le(ptr, ((read32le(ptr) & 0xffc003ff) |
(val & 0xff0) << 6));
return;
case R_AARCH64_CONDBR19:
/* Conditional branch: 19-bit signed offset, bits 23:5 */
#ifdef DEBUG_RELOC
printf ("reloc %d @ 0x%lx: val=0x%lx name=%s\n", type, addr, val,
(char *) symtab_section->link->data + sym->st_name);
#endif
if (((val - addr) + ((uint64_t)1 << 20)) & ~(uint64_t)0x1ffffc)
tcc_error_noabort("R_AARCH64_CONDBR19 relocation failed"
" (val=%lx, addr=%lx)", (long)val, (long)addr);
write32le(ptr, ((read32le(ptr) & 0xff00001f) |
(((val - addr) >> 2 & 0x7ffff) << 5)));
return;
case R_AARCH64_TSTBR14:
/* Test and branch: 14-bit signed offset, bits 20:5 */
#ifdef DEBUG_RELOC
printf ("reloc %d @ 0x%lx: val=0x%lx name=%s\n", type, addr, val,
(char *) symtab_section->link->data + sym->st_name);
#endif
if (((val - addr) + ((uint64_t)1 << 15)) & ~(uint64_t)0xfffc)
tcc_error_noabort("R_AARCH64_TSTBR14 relocation failed"
" (val=%lx, addr=%lx)", (long)val, (long)addr);
write32le(ptr, ((read32le(ptr) & 0xfff8001f) |
(((val - addr) >> 2 & 0x3fff) << 5)));
return;
case R_AARCH64_JUMP26:
case R_AARCH64_CALL26:
{
const char *name;
#ifdef DEBUG_RELOC
printf ("reloc %d @ 0x%lx: val=0x%lx name=%s\n", type, addr, val,
(char *) symtab_section->link->data + sym->st_name);
#endif
if (((val - addr) + ((uint64_t)1 << 27)) & ~(uint64_t)0xffffffc)
if (((val - addr) + ((uint64_t)1 << 27)) & ~(uint64_t)0xffffffc) {
#ifdef TCC_TARGET_PE
ElfW(Sym) *sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
if (sym->st_shndx == SHN_UNDEF
&& ELFW(ST_BIND)(sym->st_info) == STB_WEAK) {
write32le(ptr, ARM64_NOP); /* nop */
return;
}
#endif
name = (char *)symtab_section->link->data +
((ElfW(Sym) *)symtab_section->data)[sym_index].st_name;
tcc_error_noabort("R_AARCH64_(JUMP|CALL)26 relocation failed"
" (val=%lx, addr=%lx)", (long)val, (long)addr);
" for '%s' (val=%lx, addr=%lx)",
name, (long)val, (long)addr);
}
write32le(ptr, (0x14000000 |
(uint32_t)(type == R_AARCH64_CALL26) << 31 |
((val - addr) >> 2 & 0x3ffffff)));
return;
}
case R_AARCH64_ADR_GOT_PAGE: {
uint64_t off =
(((s1->got->sh_addr +
@ -306,6 +369,27 @@ ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr,
#endif
write64le(ptr, val - rel->r_addend);
return;
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
case R_AARCH64_TLSLE_ADD_TPREL_LO12: {
addr_t tls_start = 0;
int i;
for (i = 1; i < s1->nb_sections; i++) {
Section *s = s1->sections[i];
if (s->sh_flags & SHF_TLS && s->sh_size) {
if (!tls_start || s->sh_addr < tls_start)
tls_start = s->sh_addr;
}
}
/* glibc arm64: tp points to tcbhead_t (DTV), TLS data starts after it */
int64_t tp_offset = val - tls_start + 16;
int64_t imm;
if (type == R_AARCH64_TLSLE_ADD_TPREL_HI12)
imm = (tp_offset >> 12) & 0xfff;
else
imm = tp_offset & 0xfff;
write32le(ptr, ((read32le(ptr) & 0xffc003ff) | (imm << 10)));
return;
}
case R_AARCH64_RELATIVE:
#ifdef TCC_TARGET_PE
add32le(ptr, val - s1->pe_imagebase);

840
arm64-tok.h Normal file
View File

@ -0,0 +1,840 @@
/* ------------------------------------------------------------------ */
/* ARM64 (AArch64) assembler token definitions for TCC */
/* General purpose registers - 64-bit */
DEF_ASM(x0)
DEF_ASM(x1)
DEF_ASM(x2)
DEF_ASM(x3)
DEF_ASM(x4)
DEF_ASM(x5)
DEF_ASM(x6)
DEF_ASM(x7)
DEF_ASM(x8)
DEF_ASM(x9)
DEF_ASM(x10)
DEF_ASM(x11)
DEF_ASM(x12)
DEF_ASM(x13)
DEF_ASM(x14)
DEF_ASM(x15)
DEF_ASM(x16)
DEF_ASM(x17)
DEF_ASM(x18)
DEF_ASM(x19)
DEF_ASM(x20)
DEF_ASM(x21)
DEF_ASM(x22)
DEF_ASM(x23)
DEF_ASM(x24)
DEF_ASM(x25)
DEF_ASM(x26)
DEF_ASM(x27)
DEF_ASM(x28)
DEF_ASM(x29)
DEF_ASM(x30)
/* General purpose registers - 32-bit */
DEF_ASM(w0)
DEF_ASM(w1)
DEF_ASM(w2)
DEF_ASM(w3)
DEF_ASM(w4)
DEF_ASM(w5)
DEF_ASM(w6)
DEF_ASM(w7)
DEF_ASM(w8)
DEF_ASM(w9)
DEF_ASM(w10)
DEF_ASM(w11)
DEF_ASM(w12)
DEF_ASM(w13)
DEF_ASM(w14)
DEF_ASM(w15)
DEF_ASM(w16)
DEF_ASM(w17)
DEF_ASM(w18)
DEF_ASM(w19)
DEF_ASM(w20)
DEF_ASM(w21)
DEF_ASM(w22)
DEF_ASM(w23)
DEF_ASM(w24)
DEF_ASM(w25)
DEF_ASM(w26)
DEF_ASM(w27)
DEF_ASM(w28)
DEF_ASM(w29)
DEF_ASM(w30)
/* Special registers */
DEF_ASM(sp)
DEF_ASM(xzr)
DEF_ASM(wzr)
/* SIMD/FP registers - 128-bit views */
DEF_ASM(v0)
DEF_ASM(v1)
DEF_ASM(v2)
DEF_ASM(v3)
DEF_ASM(v4)
DEF_ASM(v5)
DEF_ASM(v6)
DEF_ASM(v7)
DEF_ASM(v8)
DEF_ASM(v9)
DEF_ASM(v10)
DEF_ASM(v11)
DEF_ASM(v12)
DEF_ASM(v13)
DEF_ASM(v14)
DEF_ASM(v15)
DEF_ASM(v16)
DEF_ASM(v17)
DEF_ASM(v18)
DEF_ASM(v19)
DEF_ASM(v20)
DEF_ASM(v21)
DEF_ASM(v22)
DEF_ASM(v23)
DEF_ASM(v24)
DEF_ASM(v25)
DEF_ASM(v26)
DEF_ASM(v27)
DEF_ASM(v28)
DEF_ASM(v29)
DEF_ASM(v30)
DEF_ASM(v31)
/* SIMD/FP registers - 64-bit views (double) */
DEF_ASM(d0)
DEF_ASM(d1)
DEF_ASM(d2)
DEF_ASM(d3)
DEF_ASM(d4)
DEF_ASM(d5)
DEF_ASM(d6)
DEF_ASM(d7)
DEF_ASM(d8)
DEF_ASM(d9)
DEF_ASM(d10)
DEF_ASM(d11)
DEF_ASM(d12)
DEF_ASM(d13)
DEF_ASM(d14)
DEF_ASM(d15)
DEF_ASM(d16)
DEF_ASM(d17)
DEF_ASM(d18)
DEF_ASM(d19)
DEF_ASM(d20)
DEF_ASM(d21)
DEF_ASM(d22)
DEF_ASM(d23)
DEF_ASM(d24)
DEF_ASM(d25)
DEF_ASM(d26)
DEF_ASM(d27)
DEF_ASM(d28)
DEF_ASM(d29)
DEF_ASM(d30)
DEF_ASM(d31)
/* SIMD/FP registers - 32-bit views (single) */
DEF_ASM(s0)
DEF_ASM(s1)
DEF_ASM(s2)
DEF_ASM(s3)
DEF_ASM(s4)
DEF_ASM(s5)
DEF_ASM(s6)
DEF_ASM(s7)
DEF_ASM(s8)
DEF_ASM(s9)
DEF_ASM(s10)
DEF_ASM(s11)
DEF_ASM(s12)
DEF_ASM(s13)
DEF_ASM(s14)
DEF_ASM(s15)
DEF_ASM(s16)
DEF_ASM(s17)
DEF_ASM(s18)
DEF_ASM(s19)
DEF_ASM(s20)
DEF_ASM(s21)
DEF_ASM(s22)
DEF_ASM(s23)
DEF_ASM(s24)
DEF_ASM(s25)
DEF_ASM(s26)
DEF_ASM(s27)
DEF_ASM(s28)
DEF_ASM(s29)
DEF_ASM(s30)
DEF_ASM(s31)
/* SIMD/FP registers - 16-bit views (half) */
DEF_ASM(h0)
DEF_ASM(h1)
DEF_ASM(h2)
DEF_ASM(h3)
DEF_ASM(h4)
DEF_ASM(h5)
DEF_ASM(h6)
DEF_ASM(h7)
DEF_ASM(h8)
DEF_ASM(h9)
DEF_ASM(h10)
DEF_ASM(h11)
DEF_ASM(h12)
DEF_ASM(h13)
DEF_ASM(h14)
DEF_ASM(h15)
DEF_ASM(h16)
DEF_ASM(h17)
DEF_ASM(h18)
DEF_ASM(h19)
DEF_ASM(h20)
DEF_ASM(h21)
DEF_ASM(h22)
DEF_ASM(h23)
DEF_ASM(h24)
DEF_ASM(h25)
DEF_ASM(h26)
DEF_ASM(h27)
DEF_ASM(h28)
DEF_ASM(h29)
DEF_ASM(h30)
DEF_ASM(h31)
/* SIMD/FP registers - 8-bit views (byte) */
DEF_ASM(b0)
DEF_ASM(b1)
DEF_ASM(b2)
DEF_ASM(b3)
DEF_ASM(b4)
DEF_ASM(b5)
DEF_ASM(b6)
DEF_ASM(b7)
DEF_ASM(b8)
DEF_ASM(b9)
DEF_ASM(b10)
DEF_ASM(b11)
DEF_ASM(b12)
DEF_ASM(b13)
DEF_ASM(b14)
DEF_ASM(b15)
DEF_ASM(b16)
DEF_ASM(b17)
DEF_ASM(b18)
DEF_ASM(b19)
DEF_ASM(b20)
DEF_ASM(b21)
DEF_ASM(b22)
DEF_ASM(b23)
DEF_ASM(b24)
DEF_ASM(b25)
DEF_ASM(b26)
DEF_ASM(b27)
DEF_ASM(b28)
DEF_ASM(b29)
DEF_ASM(b30)
DEF_ASM(b31)
/* Condition codes */
DEF_ASM(eq)
DEF_ASM(ne)
DEF_ASM(cs)
DEF_ASM(hs)
DEF_ASM(cc)
DEF_ASM(lo)
DEF_ASM(mi)
DEF_ASM(pl)
DEF_ASM(vs)
DEF_ASM(vc)
DEF_ASM(hi)
DEF_ASM(ls)
DEF_ASM(ge)
DEF_ASM(lt)
DEF_ASM(gt)
DEF_ASM(le)
DEF_ASM(al)
/* Data processing - arithmetic (no condition suffixes for ARM64) */
DEF_ASM(add)
DEF_ASM(adds)
DEF_ASM(sub)
DEF_ASM(subs)
DEF_ASM(cmn)
DEF_ASM(cmp)
DEF_ASM(neg)
DEF_ASM(negs)
DEF_ASM(adc)
DEF_ASM(adcs)
DEF_ASM(sbc)
DEF_ASM(sbcs)
DEF_ASM(ngc)
DEF_ASM(ngcs)
/* Data processing - bitwise */
DEF_ASM(and)
DEF_ASM(ands)
DEF_ASM(bic)
DEF_ASM(bics)
DEF_ASM(orr)
DEF_ASM(orn)
DEF_ASM(eor)
DEF_ASM(eon)
DEF_ASM(mvn)
DEF_ASM(mov)
/* Shifts */
DEF_ASM(lsl)
DEF_ASM(lsr)
DEF_ASM(asr)
DEF_ASM(ror)
/* Multiply/divide */
DEF_ASM(mul)
DEF_ASM(madd)
DEF_ASM(msub)
DEF_ASM(smaddl)
DEF_ASM(smsubl)
DEF_ASM(umaddl)
DEF_ASM(umsubl)
DEF_ASM(smulh)
DEF_ASM(umulh)
DEF_ASM(udiv)
DEF_ASM(sdiv)
/* Moves */
DEF_ASM(movz)
DEF_ASM(movn)
DEF_ASM(movk)
/* Compare/test */
DEF_ASM(tst)
DEF_ASM(teq)
/* Branch instructions */
DEF_ASM(b)
DEF_ASM(bl)
DEF_ASM(br)
DEF_ASM(blr)
DEF_ASM(ret)
DEF_ASM(cbz)
DEF_ASM(cbnz)
DEF_ASM(tbz)
DEF_ASM(tbnz)
/* Conditional branches */
DEF_ASM(beq)
DEF_ASM(bne)
DEF_ASM(bcs)
DEF_ASM(bhs)
DEF_ASM(bcc)
DEF_ASM(blo)
DEF_ASM(bmi)
DEF_ASM(bpl)
DEF_ASM(bvs)
DEF_ASM(bvc)
DEF_ASM(bhi)
DEF_ASM(bls)
DEF_ASM(bge)
DEF_ASM(blt)
DEF_ASM(bgt)
DEF_ASM(ble)
/* Conditional select */
DEF_ASM(csel)
DEF_ASM(csinc)
DEF_ASM(csinv)
DEF_ASM(csneg)
/* Load/Store */
DEF_ASM(ldr)
DEF_ASM(ldrb)
DEF_ASM(ldrh)
DEF_ASM(ldrsb)
DEF_ASM(ldrsh)
DEF_ASM(ldrsw)
DEF_ASM(str)
DEF_ASM(strb)
DEF_ASM(strh)
/* Load/Store - pair */
DEF_ASM(ldp)
DEF_ASM(stp)
DEF_ASM(ldpsw)
/* Address generation */
DEF_ASM(adr)
DEF_ASM(adrp)
/* System instructions */
DEF_ASM(mrs)
DEF_ASM(msr)
DEF_ASM(nop)
DEF_ASM(wfi)
DEF_ASM(wfe)
DEF_ASM(sev)
DEF_ASM(sevl)
DEF_ASM(isb)
DEF_ASM(dsb)
DEF_ASM(dmb)
/* Hints */
DEF_ASM(yield)
DEF_ASM(clrex)
/* Push/pop */
DEF_ASM(push)
DEF_ASM(pop)
/* Floating point */
DEF_ASM(fmov)
DEF_ASM(fadd)
DEF_ASM(fsub)
DEF_ASM(fmul)
DEF_ASM(fnmul)
DEF_ASM(fdiv)
DEF_ASM(fmax)
DEF_ASM(fmin)
DEF_ASM(fmaxnm)
DEF_ASM(fminnm)
DEF_ASM(fsqrt)
DEF_ASM(fabs)
DEF_ASM(fneg)
DEF_ASM(frintn)
DEF_ASM(frintp)
DEF_ASM(frintm)
DEF_ASM(frintz)
DEF_ASM(frinta)
DEF_ASM(frintx)
DEF_ASM(frinti)
DEF_ASM(fcmp)
DEF_ASM(fcmpe)
DEF_ASM(fccmp)
DEF_ASM(fccmpe)
DEF_ASM(fcvts)
DEF_ASM(fcvtd)
DEF_ASM(fcvth)
DEF_ASM(fcvtx)
DEF_ASM(scvtf)
DEF_ASM(ucvtf)
DEF_ASM(fcvtns)
DEF_ASM(fcvtnu)
DEF_ASM(fcvtps)
DEF_ASM(fcvtpu)
/* SIMD instructions */
DEF_ASM(addv)
DEF_ASM(faddp)
DEF_ASM(fmaxp)
DEF_ASM(fminp)
DEF_ASM(fmaxnmp)
DEF_ASM(fminnmp)
DEF_ASM(addp)
DEF_ASM(bif)
DEF_ASM(bit)
DEF_ASM(bsl)
DEF_ASM(dup)
DEF_ASM(ext)
DEF_ASM(ins)
DEF_ASM(movi)
DEF_ASM(mvni)
DEF_ASM(not)
DEF_ASM(shl)
DEF_ASM(shll)
DEF_ASM(shll2)
DEF_ASM(sli)
DEF_ASM(sri)
DEF_ASM(sqshl)
DEF_ASM(sqshlu)
DEF_ASM(srshl)
DEF_ASM(sshll)
DEF_ASM(sshll2)
DEF_ASM(sshr)
DEF_ASM(ushll)
DEF_ASM(ushll2)
DEF_ASM(ushr)
/* Misc */
DEF_ASM(bfm)
DEF_ASM(sbfm)
DEF_ASM(ubfm)
DEF_ASM(extr)
DEF_ASM(crc32b)
DEF_ASM(crc32h)
DEF_ASM(crc32w)
DEF_ASM(crc32x)
DEF_ASM(crc32cb)
DEF_ASM(crc32ch)
DEF_ASM(crc32cw)
DEF_ASM(crc32cx)
DEF_ASM(rev)
DEF_ASM(rev16)
DEF_ASM(rev32)
DEF_ASM(rev64)
DEF_ASM(clz)
DEF_ASM(cls)
DEF_ASM(rbit)
/* Exception generating */
DEF_ASM(svc)
DEF_ASM(hvc)
DEF_ASM(smc)
DEF_ASM(brk)
DEF_ASM(hlt)
DEF_ASM(dcps1)
DEF_ASM(dcps2)
DEF_ASM(dcps3)
/* Conditional branches */
DEF_ASM(b_eq)
DEF_ASM(b_ne)
DEF_ASM(b_cs)
DEF_ASM(b_cc)
DEF_ASM(b_mi)
DEF_ASM(b_pl)
DEF_ASM(b_vs)
DEF_ASM(b_vc)
DEF_ASM(b_hi)
DEF_ASM(b_ls)
DEF_ASM(b_ge)
DEF_ASM(b_lt)
DEF_ASM(b_gt)
DEF_ASM(b_le)
/* LD/ST exclusive */
DEF_ASM(ldxr)
DEF_ASM(ldxrb)
DEF_ASM(ldxrh)
DEF_ASM(stxr)
DEF_ASM(stxrb)
DEF_ASM(stxrh)
DEF_ASM(ldaxr)
DEF_ASM(ldaxrb)
DEF_ASM(ldaxrh)
DEF_ASM(stlxr)
DEF_ASM(stlxrb)
DEF_ASM(stlxrh)
/* LD/ST acquire-release */
DEF_ASM(ldar)
DEF_ASM(ldarb)
DEF_ASM(ldarh)
DEF_ASM(stlr)
DEF_ASM(stlrb)
DEF_ASM(stlrh)
DEF_ASM(ldalr)
DEF_ASM(ldalrb)
DEF_ASM(ldalrh)
DEF_ASM(stllr)
DEF_ASM(stllrb)
DEF_ASM(stllrh)
/* LD/ST unscaled immediate */
DEF_ASM(ldur)
DEF_ASM(ldurb)
DEF_ASM(ldurh)
DEF_ASM(ldursb)
DEF_ASM(ldursh)
DEF_ASM(ldursw)
DEF_ASM(stur)
DEF_ASM(sturb)
DEF_ASM(sturh)
/* Vector load/store */
DEF_ASM(ld1)
DEF_ASM(st1)
DEF_ASM(ld2)
DEF_ASM(st2)
DEF_ASM(ld3)
DEF_ASM(st3)
DEF_ASM(ld4)
DEF_ASM(st4)
/* ------------------------------------------------------------------ */
/* ARM64 instruction opcode constants and encoding helpers */
/* ------------------------------------------------------------------ */
/* Data processing - immediate */
#define ARM64_ADD_IMM 0x11000000U
#define ARM64_ADDS_IMM 0x2B000000U
#define ARM64_SUB_IMM 0x51000000U
#define ARM64_SUBS_IMM 0x6B000000U
/* Data processing - register */
#define ARM64_ADD_REG 0x0B000000U
#define ARM64_ADDS_REG 0x2B000000U
#define ARM64_SUB_REG 0x4B000000U
#define ARM64_SUBS_REG 0x6B000000U
#define ARM64_AND_REG 0x0A000000U
#define ARM64_ANDS_REG 0x6A000000U
#define ARM64_ORR_REG 0x2A000000U
#define ARM64_EOR_REG 0x4A000000U
#define ARM64_MUL_REG 0x1B000000U /* Base opcode, Rm/Rn/Rd must be filled in */
/* Move wide immediate */
#define ARM64_MOVZ 0x52800000U
#define ARM64_MOVN 0x12800000U
#define ARM64_MOVK 0xF2800000U
/* ARM64_MOVI_W/X removed: MOVI is a SIMD&FP instruction, not general-purpose */
/* Use MOVZ/MOVN/MOVK for general-purpose, or SIMD MOVI variants (0x0F000400, etc.) */
/* MOVZ/MOVN 64-bit base opcodes */
#define ARM64_MOVZ64 0xD2800000U /* MOVZ (64-bit), use with ARM64_HW() */
#define ARM64_MOVN64 0x92800000U /* MOVN (64-bit), use with ARM64_HW() */
/* Move wide immediate shift field (LSL #0/16/32/48 encoded as hw*16) */
#define ARM64_HW(v) (((uint32_t)(v) & 3) << 21)
/* Load/store register (unsigned immediate) */
#define ARM64_LDR_X 0xF9400000U
#define ARM64_LDR_W 0xB9400000U
#define ARM64_LDR_B 0x39400000U
#define ARM64_LDR_H 0x79400000U
#define ARM64_LDR_D 0xFD400000U
#define ARM64_LDR_S 0xBD400000U
#define ARM64_STR_X 0xF9000000U
#define ARM64_STR_W 0xB9000000U
#define ARM64_STR_B 0x39000000U
#define ARM64_STR_H 0x79000000U
#define ARM64_STR_D 0xFD000000U
#define ARM64_STR_S 0xBD000000U
/* Load/store register (unscaled immediate) */
#define ARM64_LDUR_X 0xF8400000U
#define ARM64_LDUR_W 0xB8400000U
#define ARM64_LDUR_B 0x38400000U
#define ARM64_LDUR_H 0x78400000U
#define ARM64_STUR_X 0xF8000000U
#define ARM64_STUR_W 0xB8000000U
#define ARM64_STUR_B 0x38000000U
#define ARM64_STUR_H 0x78000000U
/* Load/store register (register offset) */
#define ARM64_LDR_X_REG 0xF8606800U
#define ARM64_LDR_W_REG 0xB8606800U
#define ARM64_LDR_B_REG 0x38606800U
#define ARM64_LDR_H_REG 0x78606800U
#define ARM64_STR_X_REG 0xF8206800U
#define ARM64_STR_W_REG 0xB8206800U
#define ARM64_STR_B_REG 0x38206800U
#define ARM64_STR_H_REG 0x78206800U
/* Load/store (pre/post-indexed) */
#define ARM64_STR_X_PRE 0xF8000000U /* STR X pre-indexed base */
#define ARM64_LDR_X_POST 0xF8400000U /* LDR X post-indexed base */
/* SIMD load/store (unsigned immediate) */
#define ARM64_LDR_SCALAR 0x3D400000U /* Base for scalar load (size built dynamically) */
#define ARM64_LDR_S_VEC 0xBD400000U
#define ARM64_LDR_D_VEC 0xFD400000U
#define ARM64_LDR_Q_VEC 0x3DC00000U
#define ARM64_STR_SCALAR 0x3D000000U /* Base for scalar store (size built dynamically) */
#define ARM64_STR_S_VEC 0xBD000000U
#define ARM64_STR_D_VEC 0xFD000000U
#define ARM64_STR_Q_VEC 0x3D800000U
/* SIMD load/store (unscaled immediate) */
#define ARM64_LDUR_S_SIMD 0xBC400000U
#define ARM64_LDUR_D_SIMD 0xFC400000U
#define ARM64_LDUR_Q_SIMD 0x3C400000U
#define ARM64_STUR_S_SIMD 0xBC000000U
#define ARM64_STUR_D_SIMD 0xFC000000U
#define ARM64_STUR_Q_SIMD 0x3C000000U
/* SIMD load/store (register offset) */
#define ARM64_LDR_S_REG 0xBC606800U
#define ARM64_LDR_D_REG 0xFC606800U
#define ARM64_LDR_Q_REG 0x3C606800U
#define ARM64_STR_S_REG 0xBC206800U
#define ARM64_STR_D_REG 0xFC206800U
#define ARM64_STR_Q_REG 0x3C206800U
/* Load/store pair */
#define ARM64_LDP_X 0xA9400000U
#define ARM64_LDP_X_PRE 0xA9C00000U
#define ARM64_LDP_X_POST 0xA8C00000U
#define ARM64_STP_X 0xA9000000U
#define ARM64_STP_X_PRE 0xA9800000U
#define ARM64_STP_X_POST 0xA8800000U
#define ARM64_LDP_D 0x6D400000U
#define ARM64_LDP_D_PRE 0x6DC00000U
#define ARM64_LDP_D_POST 0x6CC00000U
#define ARM64_STP_D 0x6D000000U
#define ARM64_STP_D_PRE 0x6D800000U
#define ARM64_STP_D_POST 0x6C800000U
/* Branch instructions */
#define ARM64_B 0x14000000U
#define ARM64_BL 0x94000000U
#define ARM64_BR 0xD61F0000U
#define ARM64_BLR 0xD63F0000U
#define ARM64_RET 0xD65F0000U
/* Conditional branch */
#define ARM64_B_COND 0x54000000U
/* Compare and branch */
#define ARM64_CBZ 0x34000000U
#define ARM64_CBNZ 0x35000000U
/* System instructions */
#define ARM64_NOP 0xD503201FU
#define ARM64_ISB 0xD50330DFU
#define ARM64_DSB 0xD503309FU
#define ARM64_DMB 0xD50330BFU
#define ARM64_MRS 0xD5380000U
#define ARM64_MSR 0xD5180000U
#define ARM64_MRS_FPCR 0xD53B4400U
#define ARM64_MRS_FPSR 0xD53B4420U
#define ARM64_MSR_FPCR 0xD51B4400U
#define ARM64_MSR_FPSR 0xD51B4420U
/* Shifts (register) */
#define ARM64_LSL_REG 0x1AC02000U
#define ARM64_LSR_REG 0x1AC02400U
#define ARM64_ASR_REG 0x1AC02800U
#define ARM64_ROR_REG 0x1AC02C00U
/* Shifts (immediate - UBFM/SBFM) */
#define ARM64_LSL_IMM 0xD3400000U
#define ARM64_LSR_IMM 0xD3400000U
#define ARM64_LSR_IMM_32 0x53000000U /* 32-bit LSR base */
#define ARM64_ASR_IMM 0x93400000U
/* Shifted register encoding for ORR/AND/EOR */
#define ARM64_SHIFT_LSL(imm) (((uint32_t)(imm) & 63) << 10)
#define ARM64_SHIFT_LSR(imm) (0x00200000U | (((uint32_t)(imm) & 63) << 10))
#define ARM64_SHIFT_ASR(imm) (0x00400000U | (((uint32_t)(imm) & 63) << 10))
#define ARM64_SHIFT_ROR(imm) (0x00600000U | (((uint32_t)(imm) & 63) << 10))
/* UBFM/SBFM immediate fields (for LSL/LSR/ASR immediate aliases) */
#define ARM64_IMM_R(r) (((uint32_t)(r) & 0x3F) << 16)
#define ARM64_IMM_S(s) (((uint32_t)(s) & 0x3F) << 10)
/* Extended register encoding */
#define ARM64_EXTEND_LSL(lsl) (((uint32_t)(lsl) & 7) << 10)
/* MOV (register) - ORR with zero register */
#define ARM64_MOV_REG 0x2A0003E0U
/* Address generation */
#define ARM64_ADRP 0x90000000U
#define ARM64_ADR 0x10000000U
/* Logical immediate */
#define ARM64_AND_IMM 0x12000000U
#define ARM64_ORR_IMM_BASE 0x32000000U
#define ARM64_EOR_IMM 0x52000000U
#define ARM64_ANDS_IMM 0x72000000U
#define ARM64_ORR_IMM 0x320003E0U /* ORR immediate alias with Rn = XZR/WZR */
/* ------------------------------------------------------------------ */
/* ARM64 instruction encoding helper macros */
/* ------------------------------------------------------------------ */
/* Register field encodings */
#define ARM64_RD(r) ((uint32_t)(r) & 0x1FU)
#define ARM64_RN(r) (((uint32_t)(r) & 0x1FU) << 5)
#define ARM64_RM(r) (((uint32_t)(r) & 0x1FU) << 16)
#define ARM64_RT(r) ((uint32_t)(r) & 0x1FU)
#define ARM64_RT2(r) (((uint32_t)(r) & 0x1FU) << 10)
/* Immediate field encodings */
#define ARM64_IMM12(v) (((uint32_t)(v) & 0xFFFU) << 10)
#define ARM64_IMM7(v) (((uint32_t)(v) & 0x7FU) << 15)
#define ARM64_IMM14(v) (((uint32_t)(v) & 0x3FFFU) << 5)
#define ARM64_IMM16(v) (((uint32_t)(v) & 0xFFFFU) << 5)
#define ARM64_IMM_HW(v, hw) (((uint32_t)(v) & 0xFFFFU) << 5 | (((hw) & 3) << 21))
/* Shift and size encodings */
#define ARM64_SIZE(s) (((uint32_t)(s) & 3) << 30)
#define ARM64_SF(s) (((uint32_t)(s) & 1) << 31)
#define ARM64_S(v) (((uint32_t)(v) & 1) << 29)
#define ARM64_N(v) (((uint32_t)(v) & 1) << 22)
#define ARM64_SH(v) (((uint32_t)(v) & 1) << 22)
/* Condition code encoding */
#define ARM64_COND(c) ((uint32_t)(c) & 0xFU)
/* Branch offset encoding */
#define ARM64_OFFSET26(v) (((uint32_t)(v) >> 2) & 0x3FFFFFFU)
#define ARM64_OFFSET19(v) (((uint32_t)(v) >> 2) & 0x7FFFFU)
#define ARM64_OFFSET14(v) (((uint32_t)(v) >> 2) & 0x3FFFU)
/* Special register field (for MRS/MSR) */
#undef ARM64_SYSREG
#define ARM64_SYSREG(op0, op1, crn, crm, op2) \
((((op0) & 3) << 19) | (((op1) & 7) << 16) | \
(((crn) & 15) << 12) | (((crm) & 15) << 8) | (((op2) & 7) << 5))
/* Barrier option encoding */
#define ARM64_ISB_OPTION(opt) (((uint32_t)(opt) & 0xFU) << 8)
#define ARM64_DSB_OPTION(opt) (((uint32_t)(opt) & 0xFU) << 8)
#define ARM64_DMB_OPTION(opt) (((uint32_t)(opt) & 0xFU) << 8)
/* Additional opcodes for code generator - VERIFIED */
/* Note: Many of these are specific instances, not general templates */
/* Floating-point move - VERIFIED */
#define ARM64_FMOV_D_S 0x1E604000U /* FMOV Dd,Dn (scalar) */
#define ARM64_FMOV_X_D 0x9E660000U /* FMOV Xd,Dn (general to FP) */
#define ARM64_FMOV_W_S 0x1E260000U /* FMOV Wd,Sn (general to FP) */
/* ARM64_FMOV_S_D removed: 0x4EA01C00 is SIMD vector, not scalar FMOV */
/* Use 0x1E204000 for FMOV Sd,Sn or 0x1E604000 variant for cross-size */
/* FMOV variants for code generator */
#define ARM64_FMOV_SCALAR 0x1E604000U /* FMOV Dd, Dn (scalar FP) */
#define ARM64_FMOV_XD 0x9E660000U /* FMOV Xd, Dn (FP to GP 64-bit) */
#define ARM64_FMOV_WS 0x1E260000U /* FMOV Wd, Sn (FP to GP 32-bit) */
/* MOV vector (ORR vector register alias) */
#define ARM64_MOV_V16B 0x4EA01C00U /* MOV Vd.16B, Vn.16B (ORR vector, Rm=Rn alias) */
/* Load/Store SIMD&FP - Base opcodes (register fields must be filled in) */
#define ARM64_STR_Q_PRE 0x3C800000U /* STR Q pre-index base */
#define ARM64_LDR_Q_POST 0x3CC00000U /* LDR Q post-index base */
/* LDPSW - Base opcode (register fields must be filled in) */
/* Use gen_ldst_pair() with appropriate mode for LDPSW */
/* Base encodings: 0x68C00000 (post), 0x69400000 (offset), 0x69C00000 (pre) */
/* ARM64_LDR_S_SIMD removed: 0x0D00801C is not a standard encoding */
/* Use ARM64_LDR_S (0xBD400000) for scalar S or ARM64_LDR_S_VEC for SIMD */
/* MOV between SIMD and general - Use UMOV/SMOV instead */
/* ARM64_MOV_V_D removed: 0x4E083C00 is UMOV/SMOV encoding */
/* Use appropriate UMOV/SMOV base: 0x0E002C00/0x0E003C00 (32-bit) */
/* or 0x4E002C00/0x4E003C00 (64-bit) */
/* Verified from previous section */
#define ARM64_FCMP 0x1E202008U /* FCMP with zero */
#define ARM64_SDIV 0x1AC00C00U /* SDIV (32-bit) */
/* EXTR (Extract) */
#define ARM64_EXTR 0x13800000U /* EXTR Wd, Wn, Wm, #imm (32-bit) */
#define ARM64_EXTR64 0x93C00000U /* EXTR Xd, Xn, Xm, #imm (64-bit) */
/* ARM64_MUL removed - use ARM64_MUL_REG with gen_dp_reg() */
/* ORR shifted - Base opcodes (register fields must be filled in) */
#define ARM64_ORR_REG_LSL 0x2A000000U /* ORR (shifted register) base */
/* ARM64_ORR_REG_LSL32 removed: use ARM64_ORR_REG_LSL with SF=1 */
/* ARM64_ORR_REG_MOV is duplicate of ARM64_MOV_REG */
/* LSR immediate - These are UBFM encodings, use gen_shift() instead */
/* Base UBFM encodings: 0x53000000 (W), 0xD3400000 (X) */
/* gen_shift() handles immr/imms encoding for LSR/LSL/ASR */
/* ARM64_LSR_W_8, ARM64_LSR_X_8, ARM64_LSR_X_16, ARM64_LSR_X_24 removed */
/* They are specific instances, not templates */
/* SUB shifted - Base opcode (use gen_sub_reg or asm handler) */
#define ARM64_SUB_REG_LSL 0xCB000000U /* SUB (shifted register) base */
/* Duplicates removed: ARM64_LDP_X, ARM64_B, ARM64_BL, ARM64_BR, ARM64_NOP */
/* These are already defined in their respective sections above */

29
configure vendored
View File

@ -35,13 +35,11 @@ mingw32="no"
LIBSUF=".a"
EXESUF=""
DLLSUF=".so"
tcc_usrinclude=""
tcc_sysincludepaths=""
tcc_libpaths=""
tcc_crtprefix=""
tcc_elfinterp=""
triplet=
tcc_lddir=
confvars=
suggest="yes"
gcc_major=0
@ -53,6 +51,7 @@ cpuver=
dwarf=
targetos=
build_cross=
quiet=
# use CC/AR from environment when set
test -n "$CC" && cc="$CC"
@ -160,8 +159,6 @@ for opt do
;;
--debug) confvars_set debug
;;
--with-libgcc) confvars_set libgcc
;;
--with-selinux) confvars_set selinux
;;
--tcc-switches=*) assign_opt "$opt" tcc_switches
@ -174,6 +171,8 @@ for opt do
;;
--help|-h) show_help="yes"
;;
-q) quiet=yes
;;
*) echo "configure: WARNING: unrecognized option $opt"
;;
esac
@ -197,6 +196,7 @@ Standard options:
--docdir=DIR documentation in DIR [SHAREDIR/doc/tcc]
--mandir=DIR man documentation in DIR [SHAREDIR/man]
--infodir=DIR info documentation in DIR [SHAREDIR/info]
-q be quiet
Advanced options (experts only):
--source-path=PATH path of source code [$source_path]
@ -210,9 +210,8 @@ Advanced options (experts only):
--disable-static make libtcc.so instead of libtcc.a
--enable-static make libtcc.a instead of libtcc.dll (win32)
--disable-rpath disable use of -rpath with libtcc.so
--with-libgcc use libgcc_s.so.1 instead of libtcc1.a
--with-selinux use mmap for executable memory (tcc -run)
--enable-cross build cross compilers (see also 'make help')
--enable-cross build all cross compilers (see also 'make help')
--sysincludepaths=... specify system include paths, colon separated
--libpaths=... specify system library paths, colon separated
@ -266,7 +265,11 @@ default os_release "$(uname -r)"
case $buildos in
Windows_NT|MINGW*|MSYS*|CYGWIN*)
buildos="WIN32"
test "$MSYSTEM" = "MINGW32" && cpu_sys=i386
case "$MSYSTEM" in
MINGW32) cpu_sys=i386 ;;
MINGW64) cpu_sys=x86_64 ;;
CLANGARM64|MINGW_ARM64) cpu_sys=arm64 ;;
esac
;;
Linux)
if test "$(uname -o)" = "Android"; then
@ -363,6 +366,7 @@ case $targetos in
cc=`command -v cc`
cc=`readlink $cc || echo clang`
tcc_usrinclude="`xcrun --show-sdk-path`/usr/include"
default tcc_sysincludepaths "{B}/include:$tcc_usrinclude"
if test "${confvars%new_macho*}" = "${confvars}"; then
# if new_macho was not specified and (known) ver <= 10, use old (=no)
osxver=$(sw_vers -productVersion 2>/dev/null) # X.Y.Z
@ -474,7 +478,8 @@ if test -z "$build_cross"; then
if test -z "$triplet"; then
case $cpu in x86_64|arm64|riscv64)
if test -f "/usr/lib64/crti.o" ; then
tcc_lddir="lib64"
default tcc_libpaths "{B}:/usr/lib64"
default tcc_crtprefix "/usr/lib64"
fi
esac
fi
@ -548,6 +553,7 @@ fi
fcho() { if test -n "$2"; then echo "$1$2"; fi }
if test -z "$quiet"; then
fcho "Binary directory " "$bindir"
fcho "TinyCC directory " "$tccdir"
fcho "Library directory " "$libdir"
@ -556,7 +562,6 @@ fcho "Manual directory " "$mandir"
fcho "Info directory " "$infodir"
fcho "Doc directory " "$docdir"
fcho "Target root prefix " "$sysroot"
fcho "/usr/include dir " "$tcc_usrinclude"
echo "Source path $source_path"
echo "Build OS $(uname -m -s)"
echo "C compiler $cc ($gcc_major.$gcc_minor)"
@ -570,6 +575,7 @@ fcho "Elfinterp " "$tcc_elfinterp"
fcho "Switches " "$tcc_switches"
fcho "Config " "${confvars# }"
echo "Creating config.mak and config.h"
fi
version=$(head "$source_path/VERSION")
@ -687,7 +693,6 @@ for v in $cpu $confvars ; do
esac
;;
# other
CONFIG_libgcc=yes) print_num CONFIG_USE_LIBGCC 1 ;;
CONFIG_selinux=yes) print_num CONFIG_SELINUX 1 ;;
CONFIG_pie=yes) print_num CONFIG_TCC_PIE 1 ;;
CONFIG_pic=yes) print_num CONFIG_TCC_PIC 1 ;;
@ -702,13 +707,11 @@ for v in $cpu $confvars ; do
esac
done
print_str CONFIG_USR_INCLUDE "$tcc_usrinclude"
print_str CONFIG_TCC_SYSINCLUDEPATHS "$tcc_sysincludepaths"
print_str CONFIG_TCC_LIBPATHS "$tcc_libpaths"
print_str CONFIG_TCC_CRTPREFIX "$tcc_crtprefix"
print_str CONFIG_TCC_ELFINTERP "$tcc_elfinterp"
print_str CONFIG_TCC_SWITCHES "$tcc_switches"
print_str CONFIG_LDDIR "$tcc_lddir"
print_str CONFIG_TRIPLET "$triplet"
print_str CONFIG_OS_RELEASE "$os_release"
echo "#endif" >> $TMPH && echo >> $TMPH
@ -720,7 +723,7 @@ print_num CONFIG_TCC_PREDEFS "$predefs"
diff $TMPH config.h >/dev/null 2>&1
if test $? -ne 0 ; then
mv -f $TMPH config.h
else
elif test -z "$quiet"; then
echo "config.h is unchanged"
fi

3
elf.h
View File

@ -2336,7 +2336,8 @@ typedef Elf32_Addr Elf32_Conflict;
/* Processor specific values for the Shdr sh_type field. */
#define SHT_ARM_EXIDX (SHT_LOPROC + 1) /* ARM unwind section. */
#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) /* Preemption details. */
#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) /* ARM attributes section. */
#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) /* ARM attributes section. */
#define SHT_RISCV_ATTRIBUTES 0x70000003
/* AArch64 relocs. */

View File

@ -494,13 +494,6 @@ ST_FUNC void gen_expr32(ExprValue *pe)
gen_addr32(pe->sym ? VT_SYM : 0, pe->sym, pe->v);
}
#ifdef TCC_TARGET_X86_64
ST_FUNC void gen_expr64(ExprValue *pe)
{
gen_addr64(pe->sym ? VT_SYM : 0, pe->sym, pe->v);
}
#endif
/* XXX: unify with C code output ? */
static void gen_disp32(ExprValue *pe)
{

View File

@ -271,7 +271,7 @@ static void gen_modrm(int opc, int op_r2, int r, Sym *sym, int c)
} else if ((r & VT_VALMASK) == VT_LOCAL) {
o(opc);
/* currently, we use only ebp as base */
if (c == (char)c) {
if (c == (signed char)c) {
/* short reference */
o(0x45 | op_reg);
g(c);
@ -313,6 +313,16 @@ ST_FUNC void load(int r, SValue *sv)
#endif
if (fr & VT_LVAL) {
if ((fr & VT_SYM) && sv->sym->type.t & VT_TLS) {
int dst_reg = REG_VALUE(r);
o(0x65); /* gs segment prefix */
o(0x8b); /* mov r/m, r */
o(0x04 | (dst_reg << 3)); /* modrm: [sib] | destreg */
o(0x25); /* sib: disp32 */
greloca(cur_text_section, sv->sym, ind, R_386_TLS_LE, fc);
gen_le32(0);
return;
}
if (v == VT_LLOCAL) {
v1.type.t = VT_INT;
v1.r = VT_LOCAL | VT_LVAL;
@ -429,6 +439,16 @@ ST_FUNC void store(int r, SValue *v)
} else
#endif
if ((fr & VT_SYM) && v->sym->type.t & VT_TLS) {
o(0x65); /* gs segment prefix */
o(opc);
o(0x04 | (REG_VALUE(r) << 3)); /* modrm: [sib] | srcreg */
o(0x25); /* sib: disp32 */
greloca(cur_text_section, v->sym, ind, R_386_TLS_LE, fc);
gen_le32(0);
return;
}
if (fr == VT_CONST || fr == VT_LOCAL || (v->r & VT_LVAL)) {
gen_modrm(opc, r, v->r, v->sym, fc);
} else if (fr != r) {
@ -439,7 +459,7 @@ ST_FUNC void store(int r, SValue *v)
static void gadd_sp(int val)
{
if (val == (char)val) {
if (val == (signed char)val) {
o(0xc483);
g(val);
} else {
@ -774,7 +794,7 @@ ST_FUNC void gjmp_addr(int a)
{
int r;
r = a - ind - 2;
if (r == (char)r) {
if (r == (signed char)r) {
g(0xeb);
g(r);
} else {
@ -787,7 +807,7 @@ ST_FUNC void gjmp_addr(int a)
ST_FUNC void gjmp_cond_addr(int a, int op)
{
int r = a - ind - 2;
if (r == (char)r)
if (r == (signed char)r)
g(op - 32), g(r);
else
g(0x0f), gjmp2(op - 16, r - 4);
@ -830,7 +850,7 @@ ST_FUNC void gen_opi(int op)
r = gv(RC_INT);
vswap();
c = vtop->c.i;
if (c == (char)c) {
if (c == (signed char)c) {
/* generate inc and dec for smaller code */
if ((c == 1 || c == -1) && (op == '+' || op == '-')) {
opc = (c == 1) ^ (op == '+');

View File

@ -305,7 +305,6 @@ ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr,
}
return;
case R_386_TLS_LDO_32:
case R_386_TLS_LE:
{
ElfW(Sym) *sym;
Section *sec;
@ -317,6 +316,38 @@ ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr,
add32le(ptr, x);
}
return;
case R_386_TLS_LE:
{
ElfW(Sym) *sym;
Section *sec;
int32_t x;
addr_t tls_start = 0, tls_end = 0, tls_align = 1;
int i;
sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
sec = s1->sections[sym->st_shndx];
for (i = 1; i < s1->nb_sections; i++) {
Section *s = s1->sections[i];
if (s->sh_flags & SHF_TLS && s->sh_size) {
if (!tls_start || s->sh_addr < tls_start)
tls_start = s->sh_addr;
if (s->sh_addr + s->sh_size > tls_end)
tls_end = s->sh_addr + s->sh_size;
if (s->sh_addralign > tls_align)
tls_align = s->sh_addralign;
}
}
if (tls_end > tls_start) {
addr_t tls_size = tls_end - tls_start;
addr_t aligned_size = (tls_size + tls_align - 1) & ~(tls_align - 1);
x = val - (tls_start + aligned_size);
} else {
x = val - sec->sh_addr - sec->data_offset;
}
add32le(ptr, x);
}
return;
case R_386_NONE:
return;
default:

View File

@ -90,8 +90,6 @@
#define __NO_TLS 1
#define __RUNETYPE_INTERNAL 1
# if __SIZEOF_POINTER__ == 8
/* FIXME, __int128_t is used by setjump */
#define __int128_t struct { unsigned char _dummy[16] __attribute((aligned(16))); }
#define __SIZEOF_SIZE_T__ 8
#define __SIZEOF_PTRDIFF_T__ 8
#else
@ -142,12 +140,6 @@
#endif
#define __INT32_TYPE__ int
#if defined __aarch64__
/* GCC's __uint128_t appears in some Linux/OSX header files. Make it a
synonym for long double to get the size and alignment right. */
#define __uint128_t long double
#endif
#if !defined _WIN32
/* glibc defines. We do not support __USER_NAME_PREFIX__ */
#define __REDIRECT(name, proto, alias) name proto __asm__ (#alias)
@ -187,14 +179,17 @@
# endif
#endif
/* GCC's __uint128_t appears in some Linux/OSX header files.
Just make it some type with same size and alignment. */
struct __uint128__ { char x[16]; } __attribute((__aligned__(16)));
#define __int128_t struct __uint128__
#define __uint128_t struct __uint128__
/* __builtin_va_list */
#if defined __x86_64__
#if !defined _WIN32
/* GCC compatible definition of va_list. */
enum __va_arg_type {
__va_gen_reg, __va_float_reg, __va_stack
};
/* This should be in sync with the declaration in our lib/va_list.c */
typedef struct {
unsigned gp_offset, fp_offset;
union {
@ -204,43 +199,7 @@
char *reg_save_area;
} __builtin_va_list[1];
static inline void *__va_arg(__builtin_va_list ap, int arg_type,
int size, int align)
{
size = (size + 7) & ~7;
align = (align + 7) & ~7;
switch ((enum __va_arg_type)arg_type) {
case __va_gen_reg:
if (ap->gp_offset + size <= 48) {
ap->gp_offset += size;
return ap->reg_save_area + ap->gp_offset - size;
}
goto use_overflow_area;
case __va_float_reg:
if (ap->fp_offset < 128 + 48) {
ap->fp_offset += 16;
if (size == 8)
return ap->reg_save_area + ap->fp_offset - 16;
if (ap->fp_offset < 128 + 48) {
double *p = (double *)(ap->reg_save_area + ap->fp_offset);
p[-1] = p[0];
ap->fp_offset += 16;
return ap->reg_save_area + ap->fp_offset - 32;
}
}
goto use_overflow_area;
case __va_stack:
use_overflow_area:
ap->overflow_arg_area += size;
ap->overflow_arg_area =
(char*)((long long)(ap->overflow_arg_area + align - 1) & -align);
return ap->overflow_arg_area - size;
default: /* should never happen */
char *a = (char *)0; *a = 0; // abort
return 0;
}
}
void *__va_arg(__builtin_va_list ap, int arg_type, int size, int align);
#define __builtin_va_start(ap, last) \
(*(ap) = *(__builtin_va_list)((char*)__builtin_frame_address(0) - 24))
#define __builtin_va_arg(ap, t) \
@ -263,7 +222,9 @@
&~3), *(type *)(ap - ((sizeof(type)+3)&~3)))
#elif defined __aarch64__
#if defined __APPLE__
#if defined _WIN32
typedef char *__builtin_va_list;
#elif defined __APPLE__
typedef struct {
void *__stack;
} __builtin_va_list;

View File

@ -41,7 +41,7 @@ ARM_O = libtcc1.o armeabi.o armflush.o $(COMMON_O)
ARM64_O = lib-arm64.o $(COMMON_O)
RISCV64_O = lib-arm64.o $(COMMON_O)
COMMON_O = stdatomic.o atomic.o builtin.o alloca.o alloca-bt.o
WIN_O = crt1.o crt1w.o wincrt1.o wincrt1w.o dllcrt1.o dllmain.o
WIN_O = crt1.o crt1w.o wincrt1.o wincrt1w.o dllcrt1.o dllmain.o winex.o
LIN_O = dsohandle.o
OSX_O =
@ -59,12 +59,13 @@ $(Cbc)COMMON_O += bcheck.o
EXTRA_O = runmain.o bt-exe.o bt-dll.o bt-log.o bcheck.o
OBJ-i386 = $(I386_O) pic86.o $(LIN_O)
OBJ-x86_64 = $(X86_64_O) $(LIN_O)
OBJ-x86_64-osx = $(X86_64_O) $(OSX_O)
OBJ-x86_64 = $(X86_64_O) va_list.o $(LIN_O)
OBJ-x86_64-osx = $(X86_64_O) va_list.o $(OSX_O)
OBJ-i386-win32 = $(I386_O) chkstk.o $(WIN_O)
OBJ-x86_64-win32 = $(X86_64_O) chkstk.o $(WIN_O)
OBJ-arm64 = $(ARM64_O) $(LIN_O)
OBJ-arm64 = $(ARM64_O) armflush.o $(LIN_O)
OBJ-arm64-osx = $(ARM64_O) $(OSX_O)
OBJ-arm64-win32 = $(ARM64_O) chkstk.o $(WIN_O)
OBJ-arm = $(ARM_O) $(LIN_O)
OBJ-arm-fpa = $(OBJ-arm)
OBJ-arm-fpa-ld = $(OBJ-arm)
@ -72,7 +73,7 @@ OBJ-arm-vfp = $(OBJ-arm)
OBJ-arm-eabi = $(OBJ-arm)
OBJ-arm-eabihf = $(OBJ-arm)
OBJ-arm-wince = $(ARM_O) $(WIN_O)
OBJ-riscv64 = $(RISCV64_O) $(LIN_O)
OBJ-riscv64 = $(RISCV64_O) lib-riscv.o $(LIN_O)
OBJ-extra = $(filter $(EXTRA_O),$(OBJ-$T))
OBJ-libtcc1 = $(addprefix $(X),$(filter-out $(OBJ-extra),$(OBJ-$T)))

View File

@ -4,6 +4,9 @@
intrinsic with gcc. However tcc in order to compile
itself needs this function */
/* ------------------------------------------------------------- */
#if defined __arm__
#ifdef __TINYC__
/* syscall wrapper */
@ -49,3 +52,13 @@ void __clear_cache(void *beginning, void *end)
* However, there is no ARM asm parser in tcc so we use it for now */
syscall(__ARM_NR_cacheflush, beginning, end, 0);
}
/* ------------------------------------------------------------- */
#elif defined __aarch64__
void __clear_cache(void *beg, void *end)
{
__arm64_clear_cache(beg, end);
}
/* ------------------------------------------------------------- */
#endif

View File

@ -34,7 +34,11 @@
REDIR(__bound_strncmp) \
REDIR(__bound_strcat) \
REDIR(__bound_strchr) \
REDIR(__bound_strdup)
REDIR(__bound_strdup) \
REDIR(__bound_strncat) \
REDIR(__bound_strrchr) \
REDIR(__bound_setjmp) \
REDIR(__bound_longjmp)
#ifdef __leading_underscore
#define _(s) "_"#s
@ -45,11 +49,28 @@
#define REDIR(s) void *s;
static struct { REDIR_ALL } all_ptrs;
#undef REDIR
#define REDIR(s) #s"\0"
static const char all_names[] = REDIR_ALL;
#undef REDIR
#define REDIR(s) __asm__(".global " _(s) ";" _(s) ": jmp *%0" : : "m" (all_ptrs.s) );
static void all_jmps() { REDIR_ALL }
#if __aarch64__
# define REDIR(s) \
__asm__(".global "_(s)";"_(s)":"); \
__asm__(".int 0x58000090"); /* ldr x16, [pc, #16] */ \
__asm__(".int 0xf9400210"); /* ldr x16, [x16] */ \
__asm__(".int 0xd61f0200"); /* br x16 */ \
__asm__(".int 0xd503201f"); /* nop for alignment */ \
__asm__(".quad all_ptrs + (. - all_jmps - 16) / 24 * 8"); \
__asm__(".type "_(s)",function\n.size "_(s)",.-"_(s));
__asm__(".text\n.align 8\nall_jmps:");
REDIR_ALL
#else
# define REDIR(s) \
__asm__(".global "_(s)";"_(s)":"); goto *all_ptrs.s;
static void all_jmps() { REDIR_ALL }
#endif
#undef REDIR
void __bt_init_dll(int bcheck)

View File

@ -11,6 +11,21 @@
# define __declspec(n)
#endif
#ifdef _WIN64
static void bt_init_pe_prog_base(rt_context *p)
{
MEMORY_BASIC_INFORMATION mbi;
addr_t imagebase;
if (!p->prog_base)
return;
if (!VirtualQuery(p, &mbi, sizeof(mbi)) || !mbi.AllocationBase)
return;
imagebase = (addr_t)mbi.AllocationBase - p->prog_base;
p->prog_base = (addr_t)mbi.AllocationBase - (imagebase & 0xffffffffu);
}
#endif
__declspec(dllexport)
void __bt_init(rt_context *p, int is_exe)
{
@ -24,6 +39,10 @@ void __bt_init(rt_context *p, int is_exe)
if (p->bounds_start)
__bound_init(p->bounds_start, -1);
#ifdef _WIN64
bt_init_pe_prog_base(p);
#endif
/* add to chain */
rt_wait_sem();
p->next = g_rc, g_rc = p;

View File

@ -18,49 +18,40 @@ typedef int int32_t;
typedef unsigned uint32_t;
typedef long long int64_t;
typedef unsigned long long uint64_t;
static void *memcpy(void* d, void* s, __SIZE_TYPE__ c) {
char *d_, *s_;
d_ = d; s_ = s;
for (__SIZE_TYPE__ i = 0; i < c; ++i) {
d_[i] = s_[i];
}
return d;
}
#else
#include <stdint.h>
#include <string.h>
#endif
#if !defined __riscv && !defined __APPLE__
void __clear_cache(void *beg, void *end)
{
__arm64_clear_cache(beg, end);
}
#endif
typedef struct {
uint64_t x0, x1;
typedef union {
struct { uint64_t x0, x1; };
long double f;
} u128_t;
typedef union {
uint64_t x;
double f;
} u64_t;
typedef union {
uint32_t x;
float f;
} u32_t;
static long double f3_zero(int sgn)
{
long double f;
u128_t x = { 0, (uint64_t)sgn << 63 };
memcpy(&f, &x, 16);
return f;
return x.f;
}
static long double f3_infinity(int sgn)
{
long double f;
u128_t x = { 0, (uint64_t)sgn << 63 | 0x7fff000000000000 };
memcpy(&f, &x, 16);
return f;
return x.f;
}
static long double f3_NaN(void)
{
long double f;
#if 0
// ARM's default NaN usually has just the top fraction bit set:
u128_t x = { 0, 0x7fff800000000000 };
@ -68,28 +59,31 @@ static long double f3_NaN(void)
// GCC's library sets all fraction bits:
u128_t x = { -1, 0x7fffffffffffffff };
#endif
memcpy(&f, &x, 16);
return f;
return x.f;
}
static int fp3_convert_NaN(long double *f, int sgn, u128_t mnt)
static int fp3_convert_NaN(long double *f, int sgn, u128_t *mnt)
{
u128_t x = { mnt.x0,
mnt.x1 | 0x7fff800000000000 | (uint64_t)sgn << 63 };
memcpy(f, &x, 16);
u128_t x = { mnt->x0,
mnt->x1 | 0x7fff800000000000 | (uint64_t)sgn << 63 };
*f = x.f;
return 1;
#define fp3_convert_NaN(a,b,c) fp3_convert_NaN(a,b,&c)
}
static int fp3_detect_NaNs(long double *f,
int a_sgn, int a_exp, u128_t a,
int b_sgn, int b_exp, u128_t b)
int a_sgn, int a_exp, u128_t *a,
int b_sgn, int b_exp, u128_t *b)
#define a (*a)
#define b (*b)
{
#if 0
// Detect signalling NaNs:
if (a_exp == 32767 && (a.x0 | a.x1 << 16) && !(a.x1 >> 47 & 1))
return fp3_convert_NaN(f, a_sgn, a);
if (b_exp == 32767 && (b.x0 | b.x1 << 16) && !(b.x1 >> 47 & 1))
return fp3_convert_NaN(f, b_sgn, b);
#endif
// Detect quiet NaNs:
if (a_exp == 32767 && (a.x0 | a.x1 << 16))
return fp3_convert_NaN(f, a_sgn, a);
@ -97,12 +91,16 @@ static int fp3_detect_NaNs(long double *f,
return fp3_convert_NaN(f, b_sgn, b);
return 0;
#undef a
#undef b
#define fp3_detect_NaNs(a,b,c,d,e,f,g) fp3_detect_NaNs(a,b,c,&d,e,f,&g)
}
static void f3_unpack(int *sgn, int32_t *exp, u128_t *mnt, long double f)
{
u128_t x;
memcpy(&x, &f, 16);
x.f = f;
*sgn = x.x1 >> 63;
*exp = x.x1 >> 48 & 32767;
x.x1 = x.x1 << 16 >> 16;
@ -110,7 +108,7 @@ static void f3_unpack(int *sgn, int32_t *exp, u128_t *mnt, long double f)
x.x1 |= (uint64_t)1 << 48;
else
*exp = 1;
memcpy(mnt, &x, 16);
mnt->f = x.f;
}
static void f3_normalise(int32_t *exp, u128_t *mnt)
@ -184,8 +182,7 @@ static long double f3_round(int sgn, int32_t exp, u128_t *x)
return f3_infinity(sgn);
x->x1 = x->x1 << 16 >> 16 | (uint64_t)exp << 48 | (uint64_t)sgn << 63;
memcpy(&f, x, 16);
return f;
return x->f;
}
static long double f3_add(long double fa, long double fb, int neg)
@ -380,23 +377,20 @@ long double __divtf3(long double fa, long double fb)
long double __negtf2(long double f)
{
u128_t a;
memcpy(&a, &f, 16);
a.x1 ^= 1UL << 63;
memcpy(&f, &a, 16);
((u128_t*)&f)->x1 ^= 1UL << 63;
return f;
}
long double __extendsftf2(float f)
{
long double fx;
u128_t x;
u32_t u;
uint32_t a;
uint64_t aa;
memcpy(&a, &f, 4);
u.f = f, a = u.x;
aa = a;
x.x0 = 0;
if (!(a << 1))
x.x1 = aa << 32;
@ -411,16 +405,17 @@ long double __extendsftf2(float f)
} else
x.x1 = (aa >> 31 << 63 | ((aa >> 23 & 255) + 16256) << 48 |
aa << 41 >> 16);
memcpy(&fx, &x, 16);
return fx;
return x.f;
}
long double __extenddftf2(double f)
{
long double fx;
u128_t x;
u64_t u;
uint64_t a;
memcpy(&a, &f, 8);
u.f = f, a = u.x;
x.x0 = a << 60;
if (!(a << 1))
x.x1 = a;
@ -435,8 +430,7 @@ long double __extenddftf2(double f)
x.x1 = a >> 63 << 63 | (15360 - adj + 1) << 48 | a << adj << 12 >> 16;
} else
x.x1 = a >> 63 << 63 | ((a >> 52 & 2047) + 15360) << 48 | a << 12 >> 16;
memcpy(&fx, &x, 16);
return fx;
return x.f;
}
float __trunctfsf2(long double f)
@ -444,11 +438,10 @@ float __trunctfsf2(long double f)
u128_t mnt;
int32_t exp;
int sgn;
uint32_t x;
float fx;
u32_t x;
#define x x.x
f3_unpack(&sgn, &exp, &mnt, f);
if (exp == 32767 && (mnt.x0 | mnt.x1 << 16))
x = 0x7fc00000 | (uint32_t)sgn << 31 | (mnt.x1 >> 25 & 0x007fffff);
else if (exp > 16510)
@ -466,8 +459,8 @@ float __trunctfsf2(long double f)
x += 4;
x = ((x >> 2) + (exp << 23)) | (uint32_t)sgn << 31;
}
memcpy(&fx, &x, 4);
return fx;
#undef x
return x.f;
}
double __trunctfdf2(long double f)
@ -475,11 +468,10 @@ double __trunctfdf2(long double f)
u128_t mnt;
int32_t exp;
int sgn;
uint64_t x;
double fx;
u64_t x;
#define x x.x
f3_unpack(&sgn, &exp, &mnt, f);
if (exp == 32767 && (mnt.x0 | mnt.x1 << 16))
x = (0x7ff8000000000000 | (uint64_t)sgn << 63 |
mnt.x1 << 16 >> 12 | mnt.x0 >> 60);
@ -498,8 +490,8 @@ double __trunctfdf2(long double f)
x += 4;
x = ((x >> 2) + ((uint64_t)exp << 52)) | (uint64_t)sgn << 63;
}
memcpy(&fx, &x, 8);
return fx;
#undef x
return x.f;
}
int32_t __fixtfsi(long double fa)
@ -564,7 +556,6 @@ long double __floatsitf(int32_t a)
int exp = 16414;
uint32_t mnt = a;
u128_t x = { 0, 0 };
long double f;
int i;
if (a) {
if (a < 0) {
@ -579,8 +570,7 @@ long double __floatsitf(int32_t a)
x.x1 = ((uint64_t)sgn << 63 | (uint64_t)exp << 48 |
(uint64_t)(mnt << 1) << 16);
}
memcpy(&f, &x, 16);
return f;
return x.f;
}
long double __floatditf(int64_t a)
@ -589,7 +579,6 @@ long double __floatditf(int64_t a)
int exp = 16446;
uint64_t mnt = a;
u128_t x = { 0, 0 };
long double f;
int i;
if (a) {
if (a < 0) {
@ -604,8 +593,7 @@ long double __floatditf(int64_t a)
x.x0 = mnt << 49;
x.x1 = (uint64_t)sgn << 63 | (uint64_t)exp << 48 | mnt << 1 >> 16;
}
memcpy(&f, &x, 16);
return f;
return x.f;
}
long double __floatunsitf(uint32_t a)
@ -613,7 +601,6 @@ long double __floatunsitf(uint32_t a)
int exp = 16414;
uint32_t mnt = a;
u128_t x = { 0, 0 };
long double f;
int i;
if (a) {
for (i = 16; i; i >>= 1)
@ -623,8 +610,7 @@ long double __floatunsitf(uint32_t a)
}
x.x1 = (uint64_t)exp << 48 | (uint64_t)(mnt << 1) << 16;
}
memcpy(&f, &x, 16);
return f;
return x.f;
}
long double __floatunditf(uint64_t a)
@ -643,15 +629,14 @@ long double __floatunditf(uint64_t a)
x.x0 = mnt << 49;
x.x1 = (uint64_t)exp << 48 | mnt << 1 >> 16;
}
memcpy(&f, &x, 16);
return f;
return x.f;
}
static int f3_cmp(long double fa, long double fb)
{
u128_t a, b;
memcpy(&a, &fa, 16);
memcpy(&b, &fb, 16);
a.f = fa;
b.f = fb;
return (!(a.x0 | a.x1 << 1 | b.x0 | b.x1 << 1) ? 0 :
((a.x1 << 1 >> 49 == 0x7fff && (a.x0 | a.x1 << 16)) ||
(b.x1 << 1 >> 49 == 0x7fff && (b.x0 | b.x1 << 16))) ? 2 :

20
lib/lib-riscv.c Normal file
View File

@ -0,0 +1,20 @@
/*
* TCC runtime library for riscv64.
*
* Copyright (c) 2026
*
* Copying and distribution of this file, with or without modification,
* are permitted in any medium without royalty provided the copyright
* notice and this notice are preserved. This file is offered as-is,
* without any warranty.
*/
/* ------------------------------------------------------------- */
/* __clear_cache is used in tccrun.c. It is a built-in
intrinsic with gcc. However tcc in order to compile
itself needs this function */
void __clear_cache(void *beg, void *end)
{
__riscv64_clear_cache(beg, end);
}

View File

@ -107,10 +107,9 @@ union float_long {
};
/* XXX: we don't support several builtin supports for now */
#if !defined __x86_64__ && !defined __arm__ && !defined __riscv && !defined __aarch64__
#if defined __i386__
/* XXX: use gcc/tcc intrinsic ? */
#if defined __i386__
#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
__asm__ ("subl %5,%1\n\tsbbl %3,%0" \
: "=r" ((USItype) (sh)), \
@ -139,9 +138,6 @@ union float_long {
: "=r" (__cbtmp) : "rm" ((USItype) (x))); \
(count) = __cbtmp ^ 31; \
} while (0)
#else
#error unsupported CPU type
#endif
/* most of this code is taken from libgcc2.c from gcc */
@ -478,7 +474,7 @@ long long __ashldi3(long long a, int b)
#endif
}
#endif /* !__x86_64__ */
#endif /* __i386__ */
/* XXX: fix tcc's code generator to do this instead */
float __floatundisf(unsigned long long a)
@ -625,11 +621,3 @@ long long __fixxfdi (long double a1)
return s ? ret : -ret;
}
#endif /* !ARM */
#if defined _WIN64
/* MSVC x64 intrinsic */
void __faststorefence(void)
{
__asm__("lock; orl $0,(%rsp)");
}
#endif

67
lib/va_list.c Normal file
View File

@ -0,0 +1,67 @@
/* va_list.c - tinycc support for va_list on X86_64 */
#if defined __x86_64__
/* Avoid include files, they may not be available when cross compiling */
extern void abort(void);
/* This should be in sync with our include/stdarg.h */
enum __va_arg_type {
__va_gen_reg, __va_float_reg, __va_stack
};
/* GCC compatible definition of va_list. */
/*predefined by TCC (tcc_predefs.h):
typedef struct {
unsigned int gp_offset;
unsigned int fp_offset;
union {
unsigned int overflow_offset;
char *overflow_arg_area;
};
char *reg_save_area;
} __builtin_va_list[1];
*/
extern void *memcpy(void *dest, const void *src, unsigned long n);
void *__va_arg(__builtin_va_list ap,
int arg_type,
int size, int align)
{
size = (size + 7) & ~7;
align = (align + 7) & ~7;
switch ((enum __va_arg_type)arg_type) {
case __va_gen_reg:
if (ap->gp_offset + size <= 48) {
ap->gp_offset += size;
return ap->reg_save_area + ap->gp_offset - size;
}
goto use_overflow_area;
case __va_float_reg:
if (ap->fp_offset < 128 + 48) {
ap->fp_offset += 16;
if (size == 8)
return ap->reg_save_area + ap->fp_offset - 16;
if (ap->fp_offset < 128 + 48) {
memcpy(ap->reg_save_area + ap->fp_offset - 8,
ap->reg_save_area + ap->fp_offset, 8);
ap->fp_offset += 16;
return ap->reg_save_area + ap->fp_offset - 32;
}
}
goto use_overflow_area;
case __va_stack:
use_overflow_area:
ap->overflow_arg_area += size;
ap->overflow_arg_area = (char*)((long long)(ap->overflow_arg_area + align - 1) & -align);
return ap->overflow_arg_area - size;
default: /* should never happen */
abort();
return 0;
}
}
#endif

163
libtcc.c
View File

@ -124,8 +124,15 @@ static void tcc_add_systemdir(TCCState *s)
tcc_add_library_path(s, normalize_slashes(buf));
}
#endif
/* for tcc -E : On windows (depending on compiler) a FILE*
must be created by the same module where it is used. */
PUB_FUNC FILE *tcc_fopen(const char *f, const char *m) {
return fopen(f, m);
}
PUB_FUNC int tcc_fclose(FILE *f) {
return fclose(f);
}
#endif
/********************************************************/
PUB_FUNC void tcc_enter_state(TCCState *s1)
@ -245,7 +252,7 @@ static void *default_reallocator(void *ptr, unsigned long size)
else {
ptr1 = realloc(ptr, size);
if (!ptr1) {
fprintf(stderr, "memory full\n");
fprintf(stderr, "tcc: memory full\n");
exit (1);
}
}
@ -791,7 +798,7 @@ ST_FUNC int tcc_open(TCCState *s1, const char *filename)
}
/* compile the file opened in 'file'. Return non zero if errors. */
static int tcc_compile(TCCState *s1, int filetype, const char *str, int fd, const char *filename)
static int tcc_compile(TCCState *s1, int filetype, const char *str, int fd)
{
/* Here we enter the code section where we use the global variables for
parsing and code generation (tccpp.c, tccgen.c, <target>-gen.c).
@ -807,16 +814,8 @@ static int tcc_compile(TCCState *s1, int filetype, const char *str, int fd, cons
if (fd == -1) {
int len = strlen(str);
tcc_open_bf(s1, filename ? filename : "<string>", len);
tcc_open_bf(s1, "<string>", len);
memcpy(file->buffer, str, len);
if (s1->do_debug && filename) {
FILE *fp = fopen(filename, "w");
if (fp) {
fputs(str, fp);
fclose(fp);
}
}
} else {
tcc_open_bf(s1, str, 0);
file->fd = fd;
@ -846,12 +845,7 @@ static int tcc_compile(TCCState *s1, int filetype, const char *str, int fd, cons
LIBTCCAPI int tcc_compile_string(TCCState *s, const char *str)
{
return tcc_compile(s, s->filetype, str, -1, NULL);
}
LIBTCCAPI int tcc_compile_string_file(TCCState *s, const char *str, const char *filename)
{
return tcc_compile(s, s->filetype, str, -1, filename);
return tcc_compile(s, s->filetype, str, -1);
}
/* define a preprocessor symbol. value can be NULL, sym can be "sym=val" */
@ -902,8 +896,8 @@ LIBTCCAPI TCCState *tcc_new(void)
#if defined TCC_TARGET_MACHO /* || defined TCC_TARGET_PE */
s->leading_underscore = 1;
#endif
#ifdef TCC_TARGET_ARM
s->float_abi = ARM_FLOAT_ABI;
#ifdef TCC_ARM_HARDFLOAT
s->float_abi = ARM_HARD_FLOAT;
#endif
#ifdef CONFIG_NEW_DTAGS
s->enable_new_dtags = 1;
@ -1001,17 +995,19 @@ LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type)
/* allow linking with system dll's directly */
tcc_add_systemdir(s);
# endif
#elif defined TCC_TARGET_MACHO
# ifdef TCC_IS_NATIVE
tcc_add_macos_sdkpath(s);
# endif
#else
/* paths for crt objects */
tcc_split_path(s, &s->crt_paths, &s->nb_crt_paths, CONFIG_TCC_CRTPREFIX);
if (output_type != TCC_OUTPUT_MEMORY && !s->nostdlib)
tccelf_add_crtbegin(s);
tccelf_add_crtbegin(s); /* may produce errors */
#endif
return 0;
return s->nb_errors ? -1 : 0;
}
LIBTCCAPI int tcc_add_include_path(TCCState *s, const char *pathname)
@ -1246,7 +1242,7 @@ ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags)
return tcc_add_binary(s1, flags, filename, fd);
dynarray_add(&s1->target_deps, &s1->nb_target_deps, tcc_strdup(filename));
return tcc_compile(s1, flags, filename, fd, NULL);
return tcc_compile(s1, flags, filename, fd);
}
LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename)
@ -1368,10 +1364,11 @@ struct lopt {
/* match linker option */
static int link_option(struct lopt *o, const char *q)
{
const char *p = o->opt;
const char *p;
int c;
redo:
/* there should be 1 or 2 dashes */
p = o->opt;
if (*p++ != '-')
return 0;
if (*p == '-')
@ -1384,16 +1381,22 @@ static int link_option(struct lopt *o, const char *q)
goto succ; /* -Wl,-opt=arg */
++q;
}
if (c == '=' || c == ':') {
if (*p == '\0') {
if (*p == '\0') {
if (c == '|')
goto succ;
if (c == '=' || c == ':') {
if (o->s->link_optind + 1 < o->s->link_argc) {
p = o->s->link_argv[++o->s->link_optind];
goto succ; /* -Wl,-opt,arg */
}
o->match = 1; /* -Wl,-opt -Wl,arg */
} else if (c == ':')
goto succ; /* -Wl,-Iarg */
}
return 0;
}
} else if (c == ':')
goto succ; /* -Wl,-Iarg */
while (*q)
if (*q++ == '|')
goto redo;
return 0;
succ:
o->arg = p;
@ -1403,6 +1406,20 @@ succ:
static void args_parser_add_file(TCCState *s, const char* filename, int filetype);
#ifdef TCC_TARGET_PE
static void tcc_pe_set_dll_characteristics(TCCState *s, unsigned flags)
{
s->pe_dll_characteristics |= flags;
s->pe_dll_characteristics_clear &= ~flags;
}
static void tcc_pe_clear_dll_characteristics(TCCState *s, unsigned flags)
{
s->pe_dll_characteristics &= ~flags;
s->pe_dll_characteristics_clear |= flags;
}
#endif
/* set linker options */
static int tcc_set_linker(TCCState *s, const char *optarg)
{
@ -1421,9 +1438,9 @@ static int tcc_set_linker(TCCState *s, const char *optarg)
s->symbolic = 1;
} else if (link_option(&o, "nostdlib")) {
s->nostdlib_paths = 1;
} else if (link_option(&o, "e=") || link_option(&o, "entry=")) {
} else if (link_option(&o, "e=|entry=")) {
tcc_set_str(&s->elf_entryname, o.arg);
} else if (link_option(&o, "image-base=") || link_option(&o, "Ttext=")) {
} else if (link_option(&o, "image-base=|Ttext=")) {
s->text_addr = strtoull(o.arg, &end, 16);
s->has_text_addr = 1;
} else if (link_option(&o, "init=")) {
@ -1452,18 +1469,17 @@ static int tcc_set_linker(TCCState *s, const char *optarg)
#endif
else
goto err;
} else if (link_option(&o, "export-all-symbols")
|| link_option(&o, "export-dynamic")) {
} else if (link_option(&o, "export-all-symbols|export-dynamic|E")) {
s->rdynamic = 1;
} else if (link_option(&o, "rpath=")) {
tcc_concat_str(&s->rpath, o.arg, ':');
} else if (link_option(&o, "dynamic-linker=") || link_option(&o, "I:")) {
} else if (link_option(&o, "dynamic-linker=|I:")) {
tcc_set_str(&s->elfint, o.arg);
} else if (link_option(&o, "enable-new-dtags")) {
s->enable_new_dtags = 1;
} else if (link_option(&o, "section-alignment=")) {
s->section_align = strtoul(o.arg, &end, 16);
} else if (link_option(&o, "soname=") || link_option(&o, "install_name=")) {
} else if (link_option(&o, "soname=|install_name=")) {
tcc_set_str(&s->soname, o.arg);
} else if (link_option(&o, "whole-archive")) {
s->filetype |= AFF_WHOLE_ARCHIVE;
@ -1473,7 +1489,31 @@ static int tcc_set_linker(TCCState *s, const char *optarg)
s->znodelete = 1;
#ifdef TCC_TARGET_PE
} else if (link_option(&o, "large-address-aware")) {
s->pe_characteristics |= 0x20;
s->pe_characteristics |= PE_IMAGE_FILE_LARGE_ADDRESS_AWARE;
} else if (link_option(&o, "dynamicbase")) {
tcc_pe_set_dll_characteristics(s, PE_DLLCHARACTERISTICS_DYNAMIC_BASE);
} else if (link_option(&o, "disable-dynamicbase|no-dynamicbase")) {
tcc_pe_clear_dll_characteristics(s,
PE_DLLCHARACTERISTICS_DYNAMIC_BASE |
PE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA);
} else if (link_option(&o, "nxcompat")) {
tcc_pe_set_dll_characteristics(s, PE_DLLCHARACTERISTICS_NX_COMPAT);
} else if (link_option(&o, "disable-nxcompat|no-nxcompat")) {
tcc_pe_clear_dll_characteristics(s, PE_DLLCHARACTERISTICS_NX_COMPAT);
} else if (link_option(&o, "high-entropy-va")) {
# if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
tcc_pe_set_dll_characteristics(s,
PE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA |
PE_DLLCHARACTERISTICS_DYNAMIC_BASE);
# else
goto err;
# endif
} else if (link_option(&o, "disable-high-entropy-va|no-high-entropy-va")) {
tcc_pe_clear_dll_characteristics(s, PE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA);
} else if (link_option(&o, "tsaware")) {
tcc_pe_set_dll_characteristics(s, PE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE);
} else if (link_option(&o, "disable-tsaware|no-tsaware")) {
tcc_pe_clear_dll_characteristics(s, PE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE);
} else if (link_option(&o, "file-alignment=")) {
s->pe_file_align = strtoul(o.arg, &end, 16);
} else if (link_option(&o, "stack=")) {
@ -1585,30 +1625,6 @@ enum {
#define TCC_OPTION_HAS_ARG 0x0001
#define TCC_OPTION_NOSEP 0x0002 /* cannot have space before option and arg */
/*
* in tcc_options, if opt-string A is a prefix of opt-string B,
* it's un-ambiguous if and only if option A is without TCC_OPTION_HAS_ARG.
* otherwise (A with HAS_ARG), if, for instance, A is FOO and B is FOOBAR,
* then "-FOOBAR" is either A with arg BAR, or B (-FOOBARX too, if B HAS_ARG).
*
* tcc_parse_args searches tcc_options in order, so if ambiguous:
* - if the shorter (A) is earlier: the longer (B) is completely unreachable.
* - else B wins, and A can't be used with adjacent arg if it also matches B.
*
* there are few clashes currently, and the longer is always earlier/reachable.
* when it's ambiguous, shorter-concat-arg is not useful currently.
* the sh(1) script 'optclash' can identifiy clashes (tcc root dir, try "-h").
* at the time of writing, running './optclash' prints this:
-Wl,... (1642) overrides -W... (1644)
-Wp,... (1643) overrides -W... (1644)
-dumpmachine (1630) overrides -d... (1632)
-dumpversion (1631) overrides -d... (1632)
-dynamiclib (1623) overrides -d... (1632)
-flat_namespace (1624) overrides -f... (1650)
-mfloat-abi... (1647) overrides -m... (1649)
*/
static const TCCOption tcc_options[] = {
{ "h", TCC_OPTION_HELP, 0 },
{ "-help", TCC_OPTION_HELP, 0 },
@ -1848,27 +1864,6 @@ static void args_parser_add_file(TCCState *s, const char* filename, int filetype
++s->nb_libraries;
}
/* parsing is between getopt(3) and getopt_long(3), and permuting-like:
* - an option is 1 or more chars.
* - at most 1 option per arg in argv.
* - an option in argv is "-OPT[...]" (few are --OPT, if OPT is "-...").
* - optarg is next arg, or adjacent non-empty (no '='. -std=.. is arg "=..").
* - supports also adjacent-only optarg (typically optional).
* - supports mixed options and operands ("--" is ignored, except with -run).
* - -OPT[...] can be ambiguous, which is resolved using tcc_options's order.
* (see tcc_options for details)
*
* specifically, per arg of argv, in order:
* - if arg begins with '@' and is not exactly "@": process as @listfile.
* - elif arg is exactly "-" or doesn't begin with '-': process as input file.
* - if -run... is already set: also stop, arg... become argv of run_main.
* - elif arg is "--":
* - if -run... is already set: stop, arg... become argv of run_main.
* - else ignore it.
* - else ("-STRING") try to apply it as option, maybe with next (opt)arg.
*
* after all args, if -run... but no "stop": run_main gets our argv (tcc ...)
*/
/* using * to argc/argv to let "tcc -ar" benefit from @listfile expansion */
PUB_FUNC int tcc_parse_args(TCCState *s, int *pargc, char ***pargv)
{
@ -2075,7 +2070,7 @@ PUB_FUNC int tcc_parse_args(TCCState *s, int *pargc, char ***pargv)
s->float_abi = ARM_HARD_FLOAT;
else
return tcc_error_noabort("unsupported float abi '%s'", optarg);
break;
continue;
#endif
case TCC_OPTION_m:
if (set_flag(s, options_m, optarg) < 0) {
@ -2217,7 +2212,7 @@ unsupported_option:
if (run) {
if (*run && tcc_set_options(s, run) < 0)
return -1;
x = 0;
x = 0, r = 0;
goto extra_action;
}
if (!empty)

View File

@ -105,18 +105,6 @@ LIBTCCAPI void tcc_list_symbols(TCCState *s, void *ctx,
LIBTCCAPI void *_tcc_setjmp(TCCState *s1, void *jmp_buf, void *top_func, void *longjmp);
#define tcc_setjmp(s1,jb,f) setjmp(_tcc_setjmp(s1, jb, f, longjmp))
/* debugging */
/* For debugging to work you have to enable it with tcc_set_options */
/* compile a string containing a C source. Return -1 if error.
Write the string to file filename if debug is set. */
LIBTCCAPI int tcc_compile_string_file(TCCState *s, const char *buf, const char *filename);
/* Output object file. This must be done after tcc_relocate.
It only generates the file if debug is set.
The filename can be loaded with gdb command add-symbol-file */
LIBTCCAPI int elf_output_obj(TCCState *s1, const char *filename);
/* custom error printer for runtime exceptions. Returning 0 stops backtrace */
typedef int TCCBtFunc(void *udata, void *pc, const char *file, int line, const char* func, const char *msg);
LIBTCCAPI void tcc_set_backtrace_func(TCCState *s1, void* userdata, TCCBtFunc*);

View File

@ -1,38 +0,0 @@
#!/bin/sh
export LC_ALL=C
defname=libtcc.c
# $1 is the line number, $2... is the actual source line
extract_opts() { awk '/tcc_options\[\]/ {x=1}; x; x && $2~/^}/ {exit}'; }
case $1 in -h|--help)
echo "Usage: $0 [INFILE]"
echo "Detect tcc_options[] clashes in $defname-like INFILE."
echo "If INFILE is missing, use $defname at the same dir as this script."
echo "Clashes are reported as longer-overrides, or longer-unreachable."
exit
esac
f=${1-$(dirname "$0")/$defname}
[ -r "$f" ] || { >&2 echo "$0: can't read -- $f"; exit 1; }
nl -b a <"$f" | extract_opts | tr \" ' ' | awk '$2=="{"' | sort -b -k 3 |
# "<line-num> { <unquoted-opt> <rest-of-line>" sorted-up by opt
# unavoidable O(N^2). the sort simplifies the awk code - only test prior opts.
awk '
{
n=$1; opt=$3; h=/HAS_ARG/
for (pn in prevs) { # pn: line-num
po = prevs[pn] # po: opt-with-has-arg
if (index(opt,po) == 1) {
clash=1
printf("-%s%s (%d) %s -%s... (%s)\n", opt,h?"...":"",n,
n>pn? "is not reachable! by":"overrides", po,pn)
}
}
}
h {prevs[n] = opt}
END {if (clash) exit 1; print "no clashes"}
'

View File

@ -247,11 +247,9 @@ static void parse_branch_offset_operand(TCCState *s1, Operand *op){
if ((int) op->e.v >= -0x1000 && (int) op->e.v < 0x1000)
op->type = OP_IM12S;
} else if (op->e.sym->type.t & (VT_EXTERN | VT_STATIC)) {
greloca(cur_text_section, op->e.sym, ind, R_RISCV_BRANCH, 0);
/* XXX: Implement far branches */
op->type = OP_IM12S;
/* For extern/static symbols, always use far-branch expansion
since linker relaxation (R_RISCV_RELAX) is not implemented. */
op->type = OP_IM32;
op->e.v = 0;
} else {
expect("operand");
@ -678,15 +676,37 @@ static void asm_binary_opcode(TCCState* s1, int token)
asm_emit_i(token, (0x4 << 2) | 3 | (4 << 12), &ops[0], &ops[1], &imm);
return;
case TOK_ASM_neg:
/* sub rd, x0, rs */
imm.e.v = 1;
asm_emit_i(token, (0x4 << 2) | 3 | (4 << 12), &ops[0], &zero, &imm);
/* sub rd, x0, rs2 */
asm_emit_r(token, (0xC << 2) | 3 | (32 << 25), &ops[0], &zero, &ops[1]);
return;
case TOK_ASM_negw:
/* sub rd, x0, rs */
imm.e.v = 1;
asm_emit_i(token, (0x4 << 2) | 3 | (4 << 12), &ops[0], &zero, &imm);
/* subw rd, x0, rs2 */
asm_emit_r(token, (0xE << 2) | 3 | (32 << 25), &ops[0], &zero, &ops[1]);
return;
case TOK_ASM_sext_w:
/* addiw rd, rs, 0 */
asm_emit_i(token, 0x1b, &ops[0], &ops[1], &zimm);
return;
case TOK_ASM_fneg_s:
/* fsgnjn.s rd, rs, rs */
asm_emit_f(token, 0x53 | (1 << 12) | (0 << 25) | (4 << 27), &ops[0], &ops[1], &ops[1]);
return;
case TOK_ASM_fneg_d:
/* fsgnjn.d rd, rs, rs */
asm_emit_f(token, 0x53 | (1 << 12) | (1 << 25) | (4 << 27), &ops[0], &ops[1], &ops[1]);
return;
case TOK_ASM_fmv_s:
/* fsgnj.s rd, rs, rs */
asm_emit_f(token, 0x53 | (0 << 12) | (0 << 25) | (4 << 27), &ops[0], &ops[1], &ops[1]);
return;
case TOK_ASM_fmv_d:
/* fsgnj.d rd, rs, rs */
asm_emit_f(token, 0x53 | (0 << 12) | (1 << 25) | (4 << 27), &ops[0], &ops[1], &ops[1]);
return;
/* FCVT instructions now handled by asm_fcvt_opcode() with
optional rounding mode operand (GNU as syntax:
fcvt.w.s rd, rs1 [, rtz/rne/...) */
case TOK_ASM_jump:
/* auipc x5, 0 */
asm_emit_opcode(3 | (5 << 2) | ENCODE_RD(5));
@ -722,6 +742,15 @@ static void asm_binary_opcode(TCCState* s1, int token)
asm_emit_f(token, 0x53 | (4 << 27) | (0 << 25) | (2 << 12), &ops[0], &ops[1], &ops[1]);
return;
/* CSR pseudo-instructions */
case TOK_ASM_csrr:
/* csrrs rd, csr, x0 */
asm_emit_opcode(0x73 | (2 << 12) | (ops[1].e.v << 20) | ENCODE_RD(ops[0].reg));
return;
case TOK_ASM_csrw:
/* csrrw x0, csr, rs */
asm_emit_opcode(0x73 | (1 << 12) | (ops[0].e.v << 20) | ENCODE_RS1(ops[1].reg));
return;
case TOK_ASM_csrs:
/* csrrs x0, csr, rs */
asm_emit_opcode(0x73 | (2 << 12) | (ops[0].e.v << 20) | ENCODE_RS1(ops[1].reg));
@ -738,6 +767,18 @@ static void asm_binary_opcode(TCCState* s1, int token)
/* csrrw rd, fcsr, rs */
asm_emit_opcode(0x73 | (1 << 12) | (3 << 20) | ENCODE_RD(ops[0].reg) | ENCODE_RS1(ops[1].reg));
return;
case TOK_ASM_csrwi:
/* csrrwi x0, csr, uimm */
asm_emit_opcode(0x73 | (5 << 12) | (ops[0].e.v << 20) | ENCODE_RS1(ops[1].e.v));
return;
case TOK_ASM_csrsi:
/* csrrsi x0, csr, uimm */
asm_emit_opcode(0x73 | (6 << 12) | (ops[0].e.v << 20) | ENCODE_RS1(ops[1].e.v));
return;
case TOK_ASM_csrci:
/* csrrci x0, csr, uimm */
asm_emit_opcode(0x73 | (7 << 12) | (ops[0].e.v << 20) | ENCODE_RS1(ops[1].e.v));
return;
default:
expect("binary instruction");
}
@ -1213,6 +1254,30 @@ static void asm_ternary_opcode(TCCState *s1, int token)
return;
/* F/D extension */
case TOK_ASM_fadd_d:
asm_emit_f(token, 0x53 | (0 << 27) | (1 << 25) | (7 << 12), ops, ops + 1, ops + 2);
return;
case TOK_ASM_fadd_s:
asm_emit_f(token, 0x53 | (0 << 27) | (0 << 25) | (7 << 12), ops, ops + 1, ops + 2);
return;
case TOK_ASM_fsub_d:
asm_emit_f(token, 0x53 | (1 << 27) | (1 << 25) | (7 << 12), ops, ops + 1, ops + 2);
return;
case TOK_ASM_fsub_s:
asm_emit_f(token, 0x53 | (1 << 27) | (0 << 25) | (7 << 12), ops, ops + 1, ops + 2);
return;
case TOK_ASM_fmul_d:
asm_emit_f(token, 0x53 | (2 << 27) | (1 << 25) | (7 << 12), ops, ops + 1, ops + 2);
return;
case TOK_ASM_fmul_s:
asm_emit_f(token, 0x53 | (2 << 27) | (0 << 25) | (7 << 12), ops, ops + 1, ops + 2);
return;
case TOK_ASM_fdiv_d:
asm_emit_f(token, 0x53 | (3 << 27) | (1 << 25) | (7 << 12), ops, ops + 1, ops + 2);
return;
case TOK_ASM_fdiv_s:
asm_emit_f(token, 0x53 | (3 << 27) | (0 << 25) | (7 << 12), ops, ops + 1, ops + 2);
return;
case TOK_ASM_fsgnj_d:
asm_emit_f(token, 0x53 | (4 << 27) | (1 << 25) | (0 << 12), ops, ops + 1, ops + 2);
return;
@ -1232,6 +1297,26 @@ static void asm_ternary_opcode(TCCState *s1, int token)
asm_emit_f(token, 0x53 | (5 << 27) | (0 << 25) | (0 << 12), ops, ops + 1, ops + 2);
return;
/* F/D comparison: produce integer result, encode manually */
case TOK_ASM_feq_s:
asm_emit_opcode(0x53 | (0x14 << 27) | (0 << 25) | (2 << 12) | ENCODE_RD(ops[0].reg) | ENCODE_RS1(ops[1].reg) | ENCODE_RS2(ops[2].reg));
return;
case TOK_ASM_feq_d:
asm_emit_opcode(0x53 | (0x14 << 27) | (1 << 25) | (2 << 12) | ENCODE_RD(ops[0].reg) | ENCODE_RS1(ops[1].reg) | ENCODE_RS2(ops[2].reg));
return;
case TOK_ASM_flt_s:
asm_emit_opcode(0x53 | (0x14 << 27) | (0 << 25) | (1 << 12) | ENCODE_RD(ops[0].reg) | ENCODE_RS1(ops[1].reg) | ENCODE_RS2(ops[2].reg));
return;
case TOK_ASM_flt_d:
asm_emit_opcode(0x53 | (0x14 << 27) | (1 << 25) | (1 << 12) | ENCODE_RD(ops[0].reg) | ENCODE_RS1(ops[1].reg) | ENCODE_RS2(ops[2].reg));
return;
case TOK_ASM_fle_s:
asm_emit_opcode(0x53 | (0x14 << 27) | (0 << 25) | (0 << 12) | ENCODE_RD(ops[0].reg) | ENCODE_RS1(ops[1].reg) | ENCODE_RS2(ops[2].reg));
return;
case TOK_ASM_fle_d:
asm_emit_opcode(0x53 | (0x14 << 27) | (1 << 25) | (0 << 12) | ENCODE_RD(ops[0].reg) | ENCODE_RS1(ops[1].reg) | ENCODE_RS2(ops[2].reg));
return;
default:
expect("ternary instruction");
}
@ -1275,56 +1360,205 @@ static void asm_atomic_opcode(TCCState *s1, int token)
switch(token){
case TOK_ASM_lr_w:
asm_emit_a(token, 0x2F | 0x2<<12 | 0x2<<27, &ops[0], &ops[1], &ops[2], 0, 0);
asm_emit_a(token, 0x2F | 0x2<<12 | 0xC<<27, &ops[0], &ops[1], &ops[2], 0, 0);
break;
case TOK_ASM_lr_w_aq:
asm_emit_a(token, 0x2F | 0x2<<12 | 0x2<<27, &ops[0], &ops[1], &ops[2], 1, 0);
asm_emit_a(token, 0x2F | 0x2<<12 | 0xC<<27, &ops[0], &ops[1], &ops[2], 1, 0);
break;
case TOK_ASM_lr_w_rl:
asm_emit_a(token, 0x2F | 0x2<<12 | 0x2<<27, &ops[0], &ops[1], &ops[2], 0, 1);
asm_emit_a(token, 0x2F | 0x2<<12 | 0xC<<27, &ops[0], &ops[1], &ops[2], 0, 1);
break;
case TOK_ASM_lr_w_aqrl:
asm_emit_a(token, 0x2F | 0x2<<12 | 0x2<<27, &ops[0], &ops[1], &ops[2], 1, 1);
asm_emit_a(token, 0x2F | 0x2<<12 | 0xC<<27, &ops[0], &ops[1], &ops[2], 1, 1);
break;
case TOK_ASM_lr_d:
asm_emit_a(token, 0x2F | 0x3<<12 | 0x2<<27, &ops[0], &ops[1], &ops[2], 0, 0);
asm_emit_a(token, 0x2F | 0x3<<12 | 0xC<<27, &ops[0], &ops[1], &ops[2], 0, 0);
break;
case TOK_ASM_lr_d_aq:
asm_emit_a(token, 0x2F | 0x3<<12 | 0x2<<27, &ops[0], &ops[1], &ops[2], 1, 0);
asm_emit_a(token, 0x2F | 0x3<<12 | 0xC<<27, &ops[0], &ops[1], &ops[2], 1, 0);
break;
case TOK_ASM_lr_d_rl:
asm_emit_a(token, 0x2F | 0x3<<12 | 0x2<<27, &ops[0], &ops[1], &ops[2], 0, 1);
asm_emit_a(token, 0x2F | 0x3<<12 | 0xC<<27, &ops[0], &ops[1], &ops[2], 0, 1);
break;
case TOK_ASM_lr_d_aqrl:
asm_emit_a(token, 0x2F | 0x3<<12 | 0x2<<27, &ops[0], &ops[1], &ops[2], 1, 1);
asm_emit_a(token, 0x2F | 0x3<<12 | 0xC<<27, &ops[0], &ops[1], &ops[2], 1, 1);
break;
case TOK_ASM_sc_w:
asm_emit_a(token, 0x2F | 0x2<<12 | 0x3<<27, &ops[0], &ops[1], &ops[2], 0, 0);
asm_emit_a(token, 0x2F | 0x2<<12 | 0x18<<27, &ops[0], &ops[1], &ops[2], 0, 0);
break;
case TOK_ASM_sc_w_aq:
asm_emit_a(token, 0x2F | 0x2<<12 | 0x3<<27, &ops[0], &ops[1], &ops[2], 1, 0);
asm_emit_a(token, 0x2F | 0x2<<12 | 0x18<<27, &ops[0], &ops[1], &ops[2], 1, 0);
break;
case TOK_ASM_sc_w_rl:
asm_emit_a(token, 0x2F | 0x2<<12 | 0x3<<27, &ops[0], &ops[1], &ops[2], 0, 1);
asm_emit_a(token, 0x2F | 0x2<<12 | 0x18<<27, &ops[0], &ops[1], &ops[2], 0, 1);
break;
case TOK_ASM_sc_w_aqrl:
asm_emit_a(token, 0x2F | 0x2<<12 | 0x3<<27, &ops[0], &ops[1], &ops[2], 1, 1);
asm_emit_a(token, 0x2F | 0x2<<12 | 0x18<<27, &ops[0], &ops[1], &ops[2], 1, 1);
break;
case TOK_ASM_sc_d:
asm_emit_a(token, 0x2F | 0x3<<12 | 0x3<<27, &ops[0], &ops[1], &ops[2], 0, 0);
asm_emit_a(token, 0x2F | 0x3<<12 | 0x18<<27, &ops[0], &ops[1], &ops[2], 0, 0);
break;
case TOK_ASM_sc_d_aq:
asm_emit_a(token, 0x2F | 0x3<<12 | 0x3<<27, &ops[0], &ops[1], &ops[2], 1, 0);
asm_emit_a(token, 0x2F | 0x3<<12 | 0x18<<27, &ops[0], &ops[1], &ops[2], 1, 0);
break;
case TOK_ASM_sc_d_rl:
asm_emit_a(token, 0x2F | 0x3<<12 | 0x3<<27, &ops[0], &ops[1], &ops[2], 0, 1);
asm_emit_a(token, 0x2F | 0x3<<12 | 0x18<<27, &ops[0], &ops[1], &ops[2], 0, 1);
break;
case TOK_ASM_sc_d_aqrl:
asm_emit_a(token, 0x2F | 0x3<<12 | 0x3<<27, &ops[0], &ops[1], &ops[2], 1, 1);
asm_emit_a(token, 0x2F | 0x3<<12 | 0x18<<27, &ops[0], &ops[1], &ops[2], 1, 1);
break;
/* AMO instructions (base, aq=0 rl=0) */
case TOK_ASM_amoadd_w:
asm_emit_a(token, 0x2F | 0x2<<12 | 0x0<<27, &ops[0], &ops[1], &ops[2], 0, 0); break;
case TOK_ASM_amoadd_d:
asm_emit_a(token, 0x2F | 0x3<<12 | 0x0<<27, &ops[0], &ops[1], &ops[2], 0, 0); break;
case TOK_ASM_amoswap_w:
asm_emit_a(token, 0x2F | 0x2<<12 | 0x1<<27, &ops[0], &ops[1], &ops[2], 0, 0); break;
case TOK_ASM_amoswap_d:
asm_emit_a(token, 0x2F | 0x3<<12 | 0x1<<27, &ops[0], &ops[1], &ops[2], 0, 0); break;
case TOK_ASM_amoand_w:
asm_emit_a(token, 0x2F | 0x2<<12 | 0xC<<27, &ops[0], &ops[1], &ops[2], 0, 0); break;
case TOK_ASM_amoand_d:
asm_emit_a(token, 0x2F | 0x3<<12 | 0xC<<27, &ops[0], &ops[1], &ops[2], 0, 0); break;
case TOK_ASM_amoor_w:
asm_emit_a(token, 0x2F | 0x2<<12 | 0x18<<27, &ops[0], &ops[1], &ops[2], 0, 0); break;
case TOK_ASM_amoor_d:
asm_emit_a(token, 0x2F | 0x3<<12 | 0x18<<27, &ops[0], &ops[1], &ops[2], 0, 0); break;
case TOK_ASM_amoxor_w:
asm_emit_a(token, 0x2F | 0x2<<12 | 0x4<<27, &ops[0], &ops[1], &ops[2], 0, 0); break;
case TOK_ASM_amoxor_d:
asm_emit_a(token, 0x2F | 0x3<<12 | 0x4<<27, &ops[0], &ops[1], &ops[2], 0, 0); break;
case TOK_ASM_amomax_w:
asm_emit_a(token, 0x2F | 0x2<<12 | 0x14<<27, &ops[0], &ops[1], &ops[2], 0, 0); break;
case TOK_ASM_amomax_d:
asm_emit_a(token, 0x2F | 0x3<<12 | 0x14<<27, &ops[0], &ops[1], &ops[2], 0, 0); break;
case TOK_ASM_amomaxu_w:
asm_emit_a(token, 0x2F | 0x2<<12 | 0x1C<<27, &ops[0], &ops[1], &ops[2], 0, 0); break;
case TOK_ASM_amomaxu_d:
asm_emit_a(token, 0x2F | 0x3<<12 | 0x1C<<27, &ops[0], &ops[1], &ops[2], 0, 0); break;
case TOK_ASM_amomin_w:
asm_emit_a(token, 0x2F | 0x2<<12 | 0x10<<27, &ops[0], &ops[1], &ops[2], 0, 0); break;
case TOK_ASM_amomin_d:
asm_emit_a(token, 0x2F | 0x3<<12 | 0x10<<27, &ops[0], &ops[1], &ops[2], 0, 0); break;
case TOK_ASM_amominu_w:
asm_emit_a(token, 0x2F | 0x2<<12 | 0x18<<27, &ops[0], &ops[1], &ops[2], 0, 0); break;
case TOK_ASM_amominu_d:
asm_emit_a(token, 0x2F | 0x3<<12 | 0x18<<27, &ops[0], &ops[1], &ops[2], 0, 0); break;
/* AMO aq/rl variants */
case TOK_ASM_amoadd_w_aq:
asm_emit_a(token, 0x2F | 0x2<<12 | 0x0<<27, &ops[0], &ops[1], &ops[2], 1, 0); break;
case TOK_ASM_amoadd_w_rl:
asm_emit_a(token, 0x2F | 0x2<<12 | 0x0<<27, &ops[0], &ops[1], &ops[2], 0, 1); break;
case TOK_ASM_amoadd_w_aqrl:
asm_emit_a(token, 0x2F | 0x2<<12 | 0x0<<27, &ops[0], &ops[1], &ops[2], 1, 1); break;
case TOK_ASM_amoadd_d_aq:
asm_emit_a(token, 0x2F | 0x3<<12 | 0x0<<27, &ops[0], &ops[1], &ops[2], 1, 0); break;
case TOK_ASM_amoadd_d_rl:
asm_emit_a(token, 0x2F | 0x3<<12 | 0x0<<27, &ops[0], &ops[1], &ops[2], 0, 1); break;
case TOK_ASM_amoadd_d_aqrl:
asm_emit_a(token, 0x2F | 0x3<<12 | 0x0<<27, &ops[0], &ops[1], &ops[2], 1, 1); break;
case TOK_ASM_amoswap_w_aq:
asm_emit_a(token, 0x2F | 2<<12 | 0x1<<27, &ops[0], &ops[1], &ops[2], 1, 0); break;
case TOK_ASM_amoswap_w_rl:
asm_emit_a(token, 0x2F | 2<<12 | 0x1<<27, &ops[0], &ops[1], &ops[2], 0, 1); break;
case TOK_ASM_amoswap_w_aqrl:
asm_emit_a(token, 0x2F | 2<<12 | 0x1<<27, &ops[0], &ops[1], &ops[2], 1, 1); break;
case TOK_ASM_amoswap_d_aq:
asm_emit_a(token, 0x2F | 3<<12 | 0x1<<27, &ops[0], &ops[1], &ops[2], 1, 0); break;
case TOK_ASM_amoswap_d_rl:
asm_emit_a(token, 0x2F | 3<<12 | 0x1<<27, &ops[0], &ops[1], &ops[2], 0, 1); break;
case TOK_ASM_amoswap_d_aqrl:
asm_emit_a(token, 0x2F | 3<<12 | 0x1<<27, &ops[0], &ops[1], &ops[2], 1, 1); break;
case TOK_ASM_amoand_w_aq:
asm_emit_a(token, 0x2F | 2<<12 | 0xc<<27, &ops[0], &ops[1], &ops[2], 1, 0); break;
case TOK_ASM_amoand_w_rl:
asm_emit_a(token, 0x2F | 2<<12 | 0xc<<27, &ops[0], &ops[1], &ops[2], 0, 1); break;
case TOK_ASM_amoand_w_aqrl:
asm_emit_a(token, 0x2F | 2<<12 | 0xc<<27, &ops[0], &ops[1], &ops[2], 1, 1); break;
case TOK_ASM_amoand_d_aq:
asm_emit_a(token, 0x2F | 3<<12 | 0xc<<27, &ops[0], &ops[1], &ops[2], 1, 0); break;
case TOK_ASM_amoand_d_rl:
asm_emit_a(token, 0x2F | 3<<12 | 0xc<<27, &ops[0], &ops[1], &ops[2], 0, 1); break;
case TOK_ASM_amoand_d_aqrl:
asm_emit_a(token, 0x2F | 3<<12 | 0xc<<27, &ops[0], &ops[1], &ops[2], 1, 1); break;
case TOK_ASM_amoor_w_aq:
asm_emit_a(token, 0x2F | 2<<12 | 0x8<<27, &ops[0], &ops[1], &ops[2], 1, 0); break;
case TOK_ASM_amoor_w_rl:
asm_emit_a(token, 0x2F | 2<<12 | 0x8<<27, &ops[0], &ops[1], &ops[2], 0, 1); break;
case TOK_ASM_amoor_w_aqrl:
asm_emit_a(token, 0x2F | 2<<12 | 0x8<<27, &ops[0], &ops[1], &ops[2], 1, 1); break;
case TOK_ASM_amoor_d_aq:
asm_emit_a(token, 0x2F | 3<<12 | 0x8<<27, &ops[0], &ops[1], &ops[2], 1, 0); break;
case TOK_ASM_amoor_d_rl:
asm_emit_a(token, 0x2F | 3<<12 | 0x8<<27, &ops[0], &ops[1], &ops[2], 0, 1); break;
case TOK_ASM_amoor_d_aqrl:
asm_emit_a(token, 0x2F | 3<<12 | 0x8<<27, &ops[0], &ops[1], &ops[2], 1, 1); break;
case TOK_ASM_amoxor_w_aq:
asm_emit_a(token, 0x2F | 2<<12 | 0x4<<27, &ops[0], &ops[1], &ops[2], 1, 0); break;
case TOK_ASM_amoxor_w_rl:
asm_emit_a(token, 0x2F | 2<<12 | 0x4<<27, &ops[0], &ops[1], &ops[2], 0, 1); break;
case TOK_ASM_amoxor_w_aqrl:
asm_emit_a(token, 0x2F | 2<<12 | 0x4<<27, &ops[0], &ops[1], &ops[2], 1, 1); break;
case TOK_ASM_amoxor_d_aq:
asm_emit_a(token, 0x2F | 3<<12 | 0x4<<27, &ops[0], &ops[1], &ops[2], 1, 0); break;
case TOK_ASM_amoxor_d_rl:
asm_emit_a(token, 0x2F | 3<<12 | 0x4<<27, &ops[0], &ops[1], &ops[2], 0, 1); break;
case TOK_ASM_amoxor_d_aqrl:
asm_emit_a(token, 0x2F | 3<<12 | 0x4<<27, &ops[0], &ops[1], &ops[2], 1, 1); break;
case TOK_ASM_amomax_w_aq:
asm_emit_a(token, 0x2F | 2<<12 | 0x14<<27, &ops[0], &ops[1], &ops[2], 1, 0); break;
case TOK_ASM_amomax_w_rl:
asm_emit_a(token, 0x2F | 2<<12 | 0x14<<27, &ops[0], &ops[1], &ops[2], 0, 1); break;
case TOK_ASM_amomax_w_aqrl:
asm_emit_a(token, 0x2F | 2<<12 | 0x14<<27, &ops[0], &ops[1], &ops[2], 1, 1); break;
case TOK_ASM_amomax_d_aq:
asm_emit_a(token, 0x2F | 3<<12 | 0x14<<27, &ops[0], &ops[1], &ops[2], 1, 0); break;
case TOK_ASM_amomax_d_rl:
asm_emit_a(token, 0x2F | 3<<12 | 0x14<<27, &ops[0], &ops[1], &ops[2], 0, 1); break;
case TOK_ASM_amomax_d_aqrl:
asm_emit_a(token, 0x2F | 3<<12 | 0x14<<27, &ops[0], &ops[1], &ops[2], 1, 1); break;
case TOK_ASM_amomaxu_w_aq:
asm_emit_a(token, 0x2F | 2<<12 | 0x1c<<27, &ops[0], &ops[1], &ops[2], 1, 0); break;
case TOK_ASM_amomaxu_w_rl:
asm_emit_a(token, 0x2F | 2<<12 | 0x1c<<27, &ops[0], &ops[1], &ops[2], 0, 1); break;
case TOK_ASM_amomaxu_w_aqrl:
asm_emit_a(token, 0x2F | 2<<12 | 0x1c<<27, &ops[0], &ops[1], &ops[2], 1, 1); break;
case TOK_ASM_amomaxu_d_aq:
asm_emit_a(token, 0x2F | 3<<12 | 0x1c<<27, &ops[0], &ops[1], &ops[2], 1, 0); break;
case TOK_ASM_amomaxu_d_rl:
asm_emit_a(token, 0x2F | 3<<12 | 0x1c<<27, &ops[0], &ops[1], &ops[2], 0, 1); break;
case TOK_ASM_amomaxu_d_aqrl:
asm_emit_a(token, 0x2F | 3<<12 | 0x1c<<27, &ops[0], &ops[1], &ops[2], 1, 1); break;
case TOK_ASM_amomin_w_aq:
asm_emit_a(token, 0x2F | 2<<12 | 0x10<<27, &ops[0], &ops[1], &ops[2], 1, 0); break;
case TOK_ASM_amomin_w_rl:
asm_emit_a(token, 0x2F | 2<<12 | 0x10<<27, &ops[0], &ops[1], &ops[2], 0, 1); break;
case TOK_ASM_amomin_w_aqrl:
asm_emit_a(token, 0x2F | 2<<12 | 0x10<<27, &ops[0], &ops[1], &ops[2], 1, 1); break;
case TOK_ASM_amomin_d_aq:
asm_emit_a(token, 0x2F | 3<<12 | 0x10<<27, &ops[0], &ops[1], &ops[2], 1, 0); break;
case TOK_ASM_amomin_d_rl:
asm_emit_a(token, 0x2F | 3<<12 | 0x10<<27, &ops[0], &ops[1], &ops[2], 0, 1); break;
case TOK_ASM_amomin_d_aqrl:
asm_emit_a(token, 0x2F | 3<<12 | 0x10<<27, &ops[0], &ops[1], &ops[2], 1, 1); break;
case TOK_ASM_amominu_w_aq:
asm_emit_a(token, 0x2F | 2<<12 | 0x18<<27, &ops[0], &ops[1], &ops[2], 1, 0); break;
case TOK_ASM_amominu_w_rl:
asm_emit_a(token, 0x2F | 2<<12 | 0x18<<27, &ops[0], &ops[1], &ops[2], 0, 1); break;
case TOK_ASM_amominu_w_aqrl:
asm_emit_a(token, 0x2F | 2<<12 | 0x18<<27, &ops[0], &ops[1], &ops[2], 1, 1); break;
case TOK_ASM_amominu_d_aq:
asm_emit_a(token, 0x2F | 3<<12 | 0x18<<27, &ops[0], &ops[1], &ops[2], 1, 0); break;
case TOK_ASM_amominu_d_rl:
asm_emit_a(token, 0x2F | 3<<12 | 0x18<<27, &ops[0], &ops[1], &ops[2], 0, 1); break;
case TOK_ASM_amominu_d_aqrl:
asm_emit_a(token, 0x2F | 3<<12 | 0x18<<27, &ops[0], &ops[1], &ops[2], 1, 1); break;
}
}
@ -1386,6 +1620,26 @@ static void asm_emit_b(int token, uint32_t opcode, const Operand *rs1, const Ope
if (rs2->type != OP_REG) {
tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL));
}
if (imm->type == OP_IM32 && imm->e.sym) {
/* far branch: expand to inverted short branch + auipc + jalr */
int b_ofs = ind;
uint32_t inv_func3 = ((opcode >> 12) & 7) ^ 1;
uint32_t inv_opcode = (opcode & ~(7 << 12)) | (inv_func3 << 12);
/* b<inverse> .+8 */
asm_emit_opcode(inv_opcode | ENCODE_RS1(rs1->reg)
| ENCODE_RS2(rs2->reg) | (1 << 8));
/* auipc t0, 0 */
greloca(cur_text_section, imm->e.sym, ind, R_RISCV_CALL, 0);
asm_emit_opcode(0x17 | ENCODE_RD(5));
/* jalr x0, 0(t0) */
write32le(cur_text_section->data + b_ofs,
read32le(cur_text_section->data + b_ofs)
| (((ind - b_ofs) >> 1) & 0xf) << 8
| (((ind - b_ofs) >> 5) & 0x3f) << 25
| (((ind - b_ofs) >> 11) & 1) << 7
| (((ind - b_ofs) >> 12) & 1) << 31);
return;
}
if (imm->type != OP_IM12S) {
tcc_error("'%s': Expected second source operand that is an immediate value between 0 and 8191", get_tok_str(token, NULL));
}
@ -1404,6 +1658,62 @@ static void asm_emit_b(int token, uint32_t opcode, const Operand *rs1, const Ope
asm_emit_opcode(opcode | ENCODE_RS1(rs1->reg) | ENCODE_RS2(rs2->reg) | (((offset >> 1) & 0xF) << 8) | (((offset >> 5) & 0x1f) << 25) | (((offset >> 11) & 1) << 7) | (((offset >> 12) & 1) << 31));
}
/* FCVT helper: parse optional rounding mode operand (GNU as syntax:
fcvt.w.s rd, rs1 [, rtz/rne/rdn/rup/rmm]) */
static int asm_fcvt_rm(TCCState *s1)
{
int rm = 7; /* dynamic */
if (tok == ',') {
next();
switch (tok) {
case TOK_ASM_rne: rm = 0; next(); break;
case TOK_ASM_rtz: rm = 1; next(); break;
case TOK_ASM_rdn: rm = 2; next(); break;
case TOK_ASM_rup: rm = 3; next(); break;
case TOK_ASM_rmm: rm = 4; next(); break;
default: expect("rounding mode"); break;
}
}
return rm;
}
/* fcvt + fclass handler (all are binary with optional rounding operand) */
static void asm_fcvt_opcode(TCCState *s1, int token)
{
Operand ops[2];
int rm;
uint32_t enc = 0;
parse_operand(s1, &ops[0]);
skip(',');
parse_operand(s1, &ops[1]);
switch (token) {
case TOK_ASM_fcvt_w_s: rm = asm_fcvt_rm(s1); enc = 0x53 | (0x60 << 25) | (rm << 12); break;
case TOK_ASM_fcvt_wu_s: rm = asm_fcvt_rm(s1); enc = 0x53 | (0x60 << 25) | (rm << 12) | (1 << 20); break;
case TOK_ASM_fcvt_l_s: rm = asm_fcvt_rm(s1); enc = 0x53 | (0x60 << 25) | (rm << 12) | (2 << 20); break;
case TOK_ASM_fcvt_lu_s: rm = asm_fcvt_rm(s1); enc = 0x53 | (0x60 << 25) | (rm << 12) | (3 << 20); break;
case TOK_ASM_fcvt_s_w: enc = 0x53 | (0x68 << 25) | (7 << 12); break;
case TOK_ASM_fcvt_s_wu: enc = 0x53 | (0x68 << 25) | (7 << 12) | (1 << 20); break;
case TOK_ASM_fcvt_s_l: enc = 0x53 | (0x68 << 25) | (7 << 12) | (2 << 20); break;
case TOK_ASM_fcvt_s_lu: enc = 0x53 | (0x68 << 25) | (7 << 12) | (3 << 20); break;
case TOK_ASM_fcvt_w_d: rm = asm_fcvt_rm(s1); enc = 0x53 | (0x61 << 25) | (rm << 12); break;
case TOK_ASM_fcvt_wu_d: rm = asm_fcvt_rm(s1); enc = 0x53 | (0x61 << 25) | (rm << 12) | (1 << 20); break;
case TOK_ASM_fcvt_l_d: rm = asm_fcvt_rm(s1); enc = 0x53 | (0x61 << 25) | (rm << 12) | (2 << 20); break;
case TOK_ASM_fcvt_lu_d: rm = asm_fcvt_rm(s1); enc = 0x53 | (0x61 << 25) | (rm << 12) | (3 << 20); break;
case TOK_ASM_fcvt_d_w: enc = 0x53 | (0x69 << 25) | (7 << 12); break;
case TOK_ASM_fcvt_d_wu: enc = 0x53 | (0x69 << 25) | (7 << 12) | (1 << 20); break;
case TOK_ASM_fcvt_d_l: enc = 0x53 | (0x69 << 25) | (7 << 12) | (2 << 20); break;
case TOK_ASM_fcvt_d_lu: enc = 0x53 | (0x69 << 25) | (7 << 12) | (3 << 20); break;
case TOK_ASM_fcvt_s_d: enc = 0x53 | (0x20 << 25) | (7 << 12) | (1 << 20); break;
case TOK_ASM_fcvt_d_s: enc = 0x53 | (0x21 << 25) | (7 << 12); break;
case TOK_ASM_fclass_s: enc = 0x53 | (0x70 << 25) | (1 << 12); break;
case TOK_ASM_fclass_d: enc = 0x53 | (0x71 << 25) | (1 << 12); break;
default: expect("fcvt/fclass instruction"); return;
}
asm_emit_opcode(enc | ENCODE_RD(ops[0].reg) | ENCODE_RS1(ops[1].reg));
}
ST_FUNC void asm_opcode(TCCState *s1, int token)
{
switch (token) {
@ -1437,6 +1747,30 @@ ST_FUNC void asm_opcode(TCCState *s1, int token)
asm_binary_opcode(s1, token);
return;
/* fcvt/fclass — separate handler for optional rounding operand */
case TOK_ASM_fcvt_w_s:
case TOK_ASM_fcvt_wu_s:
case TOK_ASM_fcvt_l_s:
case TOK_ASM_fcvt_lu_s:
case TOK_ASM_fcvt_s_w:
case TOK_ASM_fcvt_s_wu:
case TOK_ASM_fcvt_s_l:
case TOK_ASM_fcvt_s_lu:
case TOK_ASM_fcvt_w_d:
case TOK_ASM_fcvt_wu_d:
case TOK_ASM_fcvt_l_d:
case TOK_ASM_fcvt_lu_d:
case TOK_ASM_fcvt_d_w:
case TOK_ASM_fcvt_d_wu:
case TOK_ASM_fcvt_d_l:
case TOK_ASM_fcvt_d_lu:
case TOK_ASM_fcvt_s_d:
case TOK_ASM_fcvt_d_s:
case TOK_ASM_fclass_s:
case TOK_ASM_fclass_d:
asm_fcvt_opcode(s1, token);
return;
case TOK_ASM_lb:
case TOK_ASM_lh:
case TOK_ASM_lw:
@ -1513,12 +1847,26 @@ ST_FUNC void asm_opcode(TCCState *s1, int token)
case TOK_ASM_csrrw:
case TOK_ASM_csrrwi:
/* F/D extension */
case TOK_ASM_fadd_s:
case TOK_ASM_fadd_d:
case TOK_ASM_fsub_s:
case TOK_ASM_fsub_d:
case TOK_ASM_fmul_s:
case TOK_ASM_fmul_d:
case TOK_ASM_fdiv_s:
case TOK_ASM_fdiv_d:
case TOK_ASM_fsgnj_d:
case TOK_ASM_fsgnj_s:
case TOK_ASM_fmax_s:
case TOK_ASM_fmax_d:
case TOK_ASM_fmin_s:
case TOK_ASM_fmin_d:
case TOK_ASM_feq_s:
case TOK_ASM_feq_d:
case TOK_ASM_flt_s:
case TOK_ASM_flt_d:
case TOK_ASM_fle_s:
case TOK_ASM_fle_d:
asm_ternary_opcode(s1, token);
return;
case TOK_ASM_fmadd_d:
@ -1618,10 +1966,20 @@ ST_FUNC void asm_opcode(TCCState *s1, int token)
case TOK_ASM_not:
case TOK_ASM_neg:
case TOK_ASM_negw:
case TOK_ASM_sext_w:
case TOK_ASM_fabs_s:
case TOK_ASM_fabs_d:
case TOK_ASM_fmv_s:
case TOK_ASM_fmv_d:
case TOK_ASM_fneg_s:
case TOK_ASM_fneg_d:
case TOK_ASM_csrc:
case TOK_ASM_csrs:
case TOK_ASM_csrr:
case TOK_ASM_csrw:
case TOK_ASM_csrwi:
case TOK_ASM_csrsi:
case TOK_ASM_csrci:
case TOK_ASM_fsrm:
case TOK_ASM_fscsr:
asm_binary_opcode(s1, token);
@ -1660,7 +2018,83 @@ ST_FUNC void asm_opcode(TCCState *s1, int token)
case TOK_ASM_sc_d_aq:
case TOK_ASM_sc_d_rl:
case TOK_ASM_sc_d_aqrl:
asm_atomic_opcode(s1, token);
/* AMO instructions */
case TOK_ASM_amoadd_w:
case TOK_ASM_amoadd_d:
case TOK_ASM_amoswap_w:
case TOK_ASM_amoswap_d:
case TOK_ASM_amoand_w:
case TOK_ASM_amoand_d:
case TOK_ASM_amoor_w:
case TOK_ASM_amoor_d:
case TOK_ASM_amoxor_w:
case TOK_ASM_amoxor_d:
case TOK_ASM_amomax_w:
case TOK_ASM_amomax_d:
case TOK_ASM_amomaxu_w:
case TOK_ASM_amomaxu_d:
case TOK_ASM_amomin_w:
case TOK_ASM_amomin_d:
case TOK_ASM_amominu_w:
case TOK_ASM_amominu_d:
/* AMO aq/rl */
case TOK_ASM_amoadd_w_aq:
case TOK_ASM_amoadd_w_rl:
case TOK_ASM_amoadd_w_aqrl:
case TOK_ASM_amoadd_d_aq:
case TOK_ASM_amoadd_d_rl:
case TOK_ASM_amoadd_d_aqrl:
/* AMO aq/rl (all ops) */
case TOK_ASM_amoswap_w_aq:
case TOK_ASM_amoswap_w_rl:
case TOK_ASM_amoswap_w_aqrl:
case TOK_ASM_amoswap_d_aq:
case TOK_ASM_amoswap_d_rl:
case TOK_ASM_amoswap_d_aqrl:
case TOK_ASM_amoand_w_aq:
case TOK_ASM_amoand_w_rl:
case TOK_ASM_amoand_w_aqrl:
case TOK_ASM_amoand_d_aq:
case TOK_ASM_amoand_d_rl:
case TOK_ASM_amoand_d_aqrl:
case TOK_ASM_amoor_w_aq:
case TOK_ASM_amoor_w_rl:
case TOK_ASM_amoor_w_aqrl:
case TOK_ASM_amoor_d_aq:
case TOK_ASM_amoor_d_rl:
case TOK_ASM_amoor_d_aqrl:
case TOK_ASM_amoxor_w_aq:
case TOK_ASM_amoxor_w_rl:
case TOK_ASM_amoxor_w_aqrl:
case TOK_ASM_amoxor_d_aq:
case TOK_ASM_amoxor_d_rl:
case TOK_ASM_amoxor_d_aqrl:
case TOK_ASM_amomax_w_aq:
case TOK_ASM_amomax_w_rl:
case TOK_ASM_amomax_w_aqrl:
case TOK_ASM_amomax_d_aq:
case TOK_ASM_amomax_d_rl:
case TOK_ASM_amomax_d_aqrl:
case TOK_ASM_amomaxu_w_aq:
case TOK_ASM_amomaxu_w_rl:
case TOK_ASM_amomaxu_w_aqrl:
case TOK_ASM_amomaxu_d_aq:
case TOK_ASM_amomaxu_d_rl:
case TOK_ASM_amomaxu_d_aqrl:
case TOK_ASM_amomin_w_aq:
case TOK_ASM_amomin_w_rl:
case TOK_ASM_amomin_w_aqrl:
case TOK_ASM_amomin_d_aq:
case TOK_ASM_amomin_d_rl:
case TOK_ASM_amomin_d_aqrl:
case TOK_ASM_amominu_w_aq:
case TOK_ASM_amominu_w_rl:
case TOK_ASM_amominu_w_aqrl:
case TOK_ASM_amominu_d_aq:
case TOK_ASM_amominu_d_rl:
case TOK_ASM_amominu_d_aqrl:
asm_atomic_opcode(s1, token);
break;
default:
@ -1839,9 +2273,8 @@ ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands,
} else {
load(tcc_ireg(op->reg), op->vt);
}
if (op->is_llong) {
tcc_error("long long not implemented");
}
/* RV64: long long fits in a single 64-bit register;
the load/store above already handles it correctly */
}
}
}
@ -1869,9 +2302,7 @@ ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands,
} else {
store(tcc_ireg(op->reg), op->vt);
}
if (op->is_llong) {
tcc_error("long long not implemented");
}
/* RV64: long long fits in a single 64-bit register */
}
}
}

View File

@ -30,6 +30,10 @@
#define CHAR_IS_UNSIGNED
/* define if return values need to be extended explicitely
at caller side (for interfacing with non-TCC compilers) */
#define PROMOTE_RET
#else
#define USING_GLOBALS
#include "tcc.h"
@ -175,6 +179,19 @@ static int load_symofs(int r, SValue *sv, int forstore, int *new_fc)
if (sv->r & VT_SYM) {
Sym label = {0};
assert(v == VT_CONST);
if (sv->sym->type.t & VT_TLS) {
/* TLS Local Exec model: lui + addi + add tp */
rr = is_ireg(r) ? ireg(r) : 5;
greloca(cur_text_section, sv->sym, ind,
R_RISCV_TPREL_HI20, sv->c.i);
o(0x37 | (rr << 7)); // lui RR, 0 %tprel_hi(sym)
greloca(cur_text_section, sv->sym, ind,
R_RISCV_TPREL_LO12_I, 0);
EI(0x13, 0, rr, rr, 0); // addi RR, RR, 0 %tprel_lo(sym)
ER(0x33, 0, rr, rr, 4, 0); // add RR, RR, tp
*new_fc = 0;
return rr;
}
if (sv->sym->type.t & VT_STATIC) { // XXX do this per linker relax
greloca(cur_text_section, sv->sym, ind,
R_RISCV_PCREL_HI20, sv->c.i);
@ -277,13 +294,29 @@ ST_FUNC void load(int r, SValue *sv)
EI(opcode, func3, rr, br, fc); // l[bhwd][u] / fl[wd] RR, fc(BR)
} else if (v == VT_CONST) {
int rb = 0, do32bit = 8, zext = 0;
assert((!is_float(sv->type.t) && is_ireg(r)) || bt == VT_LDOUBLE);
if (is_float(sv->type.t) && bt != VT_LDOUBLE) {
/* load float/double constant: move bit pattern from int reg */
uint64_t val = sv->c.i;
int is_dbl = bt == VT_DOUBLE;
if (val == 0) {
o(0x53 | (rr << 7) | ((unsigned)(0x78 | is_dbl) << 25));
return;
}
if (is_dbl) {
load_large_constant(6, (int)val, (int)(val >> 32));
} else {
if (LOW_OVERFLOW(fc))
o(0x37 | (6 << 7) | UPPER(fc)); // lui t1, upper
EI(0x13 | 8, 0, 6, LOW_OVERFLOW(fc) ? 6 : 0, SIGN11(fc)); // addiw t1,...
}
o(0x53 | (rr << 7) | (6 << 15) | ((unsigned)(0x78 | is_dbl) << 25));
return;
}
assert(is_ireg(r) || bt == VT_LDOUBLE);
if (fr & VT_SYM) {
rb = load_symofs(r, sv, 0, &fc);
do32bit = 0;
}
if (is_float(sv->type.t) && bt != VT_LDOUBLE)
tcc_error("unimp: load(float)");
if (do32bit && fc != sv->c.i) {
int64_t si = sv->c.i;
si >>= 32;
@ -1241,10 +1274,31 @@ ST_FUNC void gen_opf(int op)
}
}
ST_FUNC void gen_cvt_csti(int t)
{
int r = ireg(gv(RC_INT));
if ((t & VT_BTYPE) == VT_SHORT) {
if (t & VT_UNSIGNED) {
EI(0x13, 1, r, r, 48); // slli r, r, 48
EI(0x13, 5, r, r, 48); // srli r, r, 48
} else {
EI(0x13, 1, r, r, 48); // slli r, r, 48
EIu(0x13, 5, r, r, 0x400 | 48); // srai r, r, 48
}
} else {
if (t & VT_UNSIGNED) {
EI(0x13, 7, r, r, 0xff); // andi r, r, 0xff
} else {
EI(0x13, 1, r, r, 56); // slli r, r, 56
EIu(0x13, 5, r, r, 0x400 | 56); // srai r, r, 56
}
}
}
ST_FUNC void gen_cvt_sxtw(void)
{
/* XXX on risc-v the registers are usually sign-extended already.
Let's try to not do anything here. */
int r = ireg(gv(RC_INT));
EI(0x1b, 0, r, r, 0); // addiw r, r, 0
}
ST_FUNC void gen_cvt_itof(int t)
@ -1431,4 +1485,12 @@ ST_FUNC void gen_vla_alloc(CType *type, int align)
}
#endif
}
ST_FUNC void gen_clear_cache(void)
{
/* Zifencei extension: fence + fence.i for I/D synchronization.
Required by RISC-V Linux ABI, present on all Linux-capable cores. */
o(0x0ff0000f); // fence iorw, iorw
o(0x0000100f); // fence.i
}
#endif

View File

@ -53,6 +53,8 @@ ST_FUNC int code_reloc (int reloc_type)
case R_RISCV_64:
case R_RISCV_SET_ULEB128:
case R_RISCV_SUB_ULEB128:
case R_RISCV_TPREL_HI20:
case R_RISCV_TPREL_LO12_I:
return 0;
case R_RISCV_CALL_PLT:
@ -101,6 +103,10 @@ ST_FUNC int gotplt_entry_type (int reloc_type)
case R_RISCV_GOT_HI20:
return ALWAYS_GOTPLT_ENTRY;
case R_RISCV_TPREL_HI20:
case R_RISCV_TPREL_LO12_I:
return NO_GOTPLT_ENTRY;
}
return -1;
}
@ -175,37 +181,23 @@ ST_FUNC void relocate_plt(TCCState *s1)
static void riscv64_record_pcrel_hi(TCCState *s1, addr_t addr, addr_t val)
{
int n = s1->nb_pcrel_hi_entries;
if (n >= s1->alloc_pcrel_hi_entries) {
int new_alloc = s1->alloc_pcrel_hi_entries ? s1->alloc_pcrel_hi_entries * 2 : 64;
s1->pcrel_hi_entries = tcc_realloc(s1->pcrel_hi_entries,
new_alloc * sizeof(*s1->pcrel_hi_entries));
s1->alloc_pcrel_hi_entries = new_alloc;
}
s1->pcrel_hi_entries[n].addr = addr;
s1->pcrel_hi_entries[n].val = val;
s1->nb_pcrel_hi_entries = n + 1;
last_hi.addr = addr;
last_hi.val = val;
struct pcrel_hi *entry = tcc_malloc(sizeof *entry);
entry->addr = addr;
entry->val = val;
dynarray_add(&s1->pcrel_hi_entries, &s1->nb_pcrel_hi_entries, entry);
}
static int riscv64_lookup_pcrel_hi(TCCState *s1, addr_t hi_addr, addr_t *hi_val)
{
int i;
struct pcrel_hi *entry;
if (s1->nb_pcrel_hi_entries && hi_addr == last_hi.addr) {
*hi_val = last_hi.val;
return 1;
}
for (i = s1->nb_pcrel_hi_entries - 1; i >= 0; --i) {
entry = &s1->pcrel_hi_entries[i];
for (i = s1->nb_pcrel_hi_entries; i > 0; ) {
struct pcrel_hi *entry = s1->pcrel_hi_entries[--i];
if (entry->addr == hi_addr) {
last_hi = *entry;
*hi_val = entry->val;
return 1;
return 0;
}
}
return 0;
return tcc_error_noabort("unsupported hi/lo pcrel reloc scheme");
}
ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr,
@ -279,15 +271,13 @@ ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr,
printf("PCREL_LO12_I: val=%lx addr=%lx\n", (long)val, (long)addr);
#endif
addr = val;
if (!riscv64_lookup_pcrel_hi(s1, addr, &val))
tcc_error_noabort("unsupported hi/lo pcrel reloc scheme");
riscv64_lookup_pcrel_hi(s1, addr, &val);
write32le(ptr, (read32le(ptr) & 0xfffff)
| (((val - addr) & 0xfff) << 20));
return;
case R_RISCV_PCREL_LO12_S:
addr = val;
if (!riscv64_lookup_pcrel_hi(s1, addr, &val))
tcc_error_noabort("unsupported hi/lo pcrel reloc scheme");
riscv64_lookup_pcrel_hi(s1, addr, &val);
off32 = val - addr;
write32le(ptr, (read32le(ptr) & ~0xfe000f80)
| ((off32 & 0xfe0) << 20)
@ -409,6 +399,37 @@ ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr,
case R_RISCV_COPY:
/* XXX */
return;
case R_RISCV_RELATIVE:
/* R_RISCV_RELATIVE value is already applied in R_RISCV_32/64
dynamic output paths, but we need this case for incoming
RELATIVE relocations from object files. */
return;
case R_RISCV_TPREL_HI20:
case R_RISCV_TPREL_LO12_I: {
addr_t tls_start = 0;
int64_t tp_offset;
int i;
for (i = 1; i < s1->nb_sections; i++) {
Section *s = s1->sections[i];
if (s->sh_flags & SHF_TLS && s->sh_size) {
if (!tls_start || s->sh_addr < tls_start)
tls_start = s->sh_addr;
}
}
tp_offset = val - tls_start;
if (type == R_RISCV_TPREL_HI20) {
off64 = (int64_t)(tp_offset + 0x800) >> 12;
if ((off64 + ((uint64_t)1 << 20)) >> 21)
tcc_error_noabort("R_RISCV_TPREL_HI20 relocation failed");
write32le(ptr, (read32le(ptr) & 0xfff)
| ((off64 & 0xfffff) << 12));
} else {
write32le(ptr, (read32le(ptr) & 0xfffff)
| (((tp_offset) & 0xfff) << 20));
}
return;
}
default:
fprintf(stderr, "FIXME: handle reloc type %x at %x [%p] to %x\n",

View File

@ -270,6 +270,14 @@
/* enough implemented for musl */
DEF_ASM_WITH_SUFFIX(fsgnj, s)
DEF_ASM_WITH_SUFFIX(fsgnj, d)
DEF_ASM_WITH_SUFFIX(fadd, s)
DEF_ASM_WITH_SUFFIX(fadd, d)
DEF_ASM_WITH_SUFFIX(fsub, s)
DEF_ASM_WITH_SUFFIX(fsub, d)
DEF_ASM_WITH_SUFFIX(fmul, s)
DEF_ASM_WITH_SUFFIX(fmul, d)
DEF_ASM_WITH_SUFFIX(fdiv, s)
DEF_ASM_WITH_SUFFIX(fdiv, d)
DEF_ASM_WITH_SUFFIX(fmadd, s)
DEF_ASM_WITH_SUFFIX(fmadd, d)
DEF_ASM_WITH_SUFFIX(fmax, s)
@ -279,7 +287,35 @@
DEF_ASM_WITH_SUFFIX(fsqrt, s)
DEF_ASM_WITH_SUFFIX(fsqrt, d)
/* "C" Extension for Compressed Instructions, V2.0 */
/* F/D comparison and conversion (not needed by musl, added for completeness) */
DEF_ASM_WITH_SUFFIX(feq, s)
DEF_ASM_WITH_SUFFIX(feq, d)
DEF_ASM_WITH_SUFFIX(flt, s)
DEF_ASM_WITH_SUFFIX(flt, d)
DEF_ASM_WITH_SUFFIX(fle, s)
DEF_ASM_WITH_SUFFIX(fle, d)
DEF_ASM_WITH_SUFFIX(fclass, s)
DEF_ASM_WITH_SUFFIX(fclass, d)
DEF_ASM_WITH_SUFFIXES(fcvt, w, s)
DEF_ASM_WITH_SUFFIXES(fcvt, wu, s)
DEF_ASM_WITH_SUFFIXES(fcvt, l, s)
DEF_ASM_WITH_SUFFIXES(fcvt, lu, s)
DEF_ASM_WITH_SUFFIXES(fcvt, s, w)
DEF_ASM_WITH_SUFFIXES(fcvt, s, wu)
DEF_ASM_WITH_SUFFIXES(fcvt, s, l)
DEF_ASM_WITH_SUFFIXES(fcvt, s, lu)
DEF_ASM_WITH_SUFFIXES(fcvt, w, d)
DEF_ASM_WITH_SUFFIXES(fcvt, wu, d)
DEF_ASM_WITH_SUFFIXES(fcvt, l, d)
DEF_ASM_WITH_SUFFIXES(fcvt, lu, d)
DEF_ASM_WITH_SUFFIXES(fcvt, d, w)
DEF_ASM_WITH_SUFFIXES(fcvt, d, wu)
DEF_ASM_WITH_SUFFIXES(fcvt, d, l)
DEF_ASM_WITH_SUFFIXES(fcvt, d, lu)
DEF_ASM_WITH_SUFFIXES(fcvt, s, d)
DEF_ASM_WITH_SUFFIXES(fcvt, d, s)
/* "C" Extension for Compressed Instructions, V2.0 */
DEF_ASM_WITH_SUFFIX(c, nop)
/* Loads */
DEF_ASM_WITH_SUFFIX(c, li)
@ -464,7 +500,93 @@
DEF_ASM_WITH_SUFFIXES(sc, d, rl)
DEF_ASM_WITH_SUFFIXES(sc, d, aqrl)
/* `fence` arguments */
/* "A" Extension for Atomic Operations, V2.1 (base, no aq/rl suffixes) */
DEF_ASM_WITH_SUFFIX(amoadd, w)
DEF_ASM_WITH_SUFFIX(amoadd, d)
DEF_ASM_WITH_SUFFIX(amoswap, w)
DEF_ASM_WITH_SUFFIX(amoswap, d)
DEF_ASM_WITH_SUFFIX(amoand, w)
DEF_ASM_WITH_SUFFIX(amoand, d)
DEF_ASM_WITH_SUFFIX(amoor, w)
DEF_ASM_WITH_SUFFIX(amoor, d)
DEF_ASM_WITH_SUFFIX(amoxor, w)
DEF_ASM_WITH_SUFFIX(amoxor, d)
DEF_ASM_WITH_SUFFIX(amomax, w)
DEF_ASM_WITH_SUFFIX(amomax, d)
DEF_ASM_WITH_SUFFIX(amomaxu, w)
DEF_ASM_WITH_SUFFIX(amomaxu, d)
DEF_ASM_WITH_SUFFIX(amomin, w)
DEF_ASM_WITH_SUFFIX(amomin, d)
DEF_ASM_WITH_SUFFIX(amominu, w)
DEF_ASM_WITH_SUFFIX(amominu, d)
/* AMO aq/rl ordering suffixes */
DEF_ASM_WITH_SUFFIXES(amoadd, w, aq)
DEF_ASM_WITH_SUFFIXES(amoadd, w, rl)
DEF_ASM_WITH_SUFFIXES(amoadd, w, aqrl)
DEF_ASM_WITH_SUFFIXES(amoadd, d, aq)
DEF_ASM_WITH_SUFFIXES(amoadd, d, rl)
DEF_ASM_WITH_SUFFIXES(amoadd, d, aqrl)
/* Complete AMO aq/rl ordering suffixes (all ops) */
DEF_ASM_WITH_SUFFIXES(amoswap, w, aq)
DEF_ASM_WITH_SUFFIXES(amoswap, w, rl)
DEF_ASM_WITH_SUFFIXES(amoswap, w, aqrl)
DEF_ASM_WITH_SUFFIXES(amoswap, d, aq)
DEF_ASM_WITH_SUFFIXES(amoswap, d, rl)
DEF_ASM_WITH_SUFFIXES(amoswap, d, aqrl)
DEF_ASM_WITH_SUFFIXES(amoand, w, aq)
DEF_ASM_WITH_SUFFIXES(amoand, w, rl)
DEF_ASM_WITH_SUFFIXES(amoand, w, aqrl)
DEF_ASM_WITH_SUFFIXES(amoand, d, aq)
DEF_ASM_WITH_SUFFIXES(amoand, d, rl)
DEF_ASM_WITH_SUFFIXES(amoand, d, aqrl)
DEF_ASM_WITH_SUFFIXES(amoor, w, aq)
DEF_ASM_WITH_SUFFIXES(amoor, w, rl)
DEF_ASM_WITH_SUFFIXES(amoor, w, aqrl)
DEF_ASM_WITH_SUFFIXES(amoor, d, aq)
DEF_ASM_WITH_SUFFIXES(amoor, d, rl)
DEF_ASM_WITH_SUFFIXES(amoor, d, aqrl)
DEF_ASM_WITH_SUFFIXES(amoxor, w, aq)
DEF_ASM_WITH_SUFFIXES(amoxor, w, rl)
DEF_ASM_WITH_SUFFIXES(amoxor, w, aqrl)
DEF_ASM_WITH_SUFFIXES(amoxor, d, aq)
DEF_ASM_WITH_SUFFIXES(amoxor, d, rl)
DEF_ASM_WITH_SUFFIXES(amoxor, d, aqrl)
DEF_ASM_WITH_SUFFIXES(amomax, w, aq)
DEF_ASM_WITH_SUFFIXES(amomax, w, rl)
DEF_ASM_WITH_SUFFIXES(amomax, w, aqrl)
DEF_ASM_WITH_SUFFIXES(amomax, d, aq)
DEF_ASM_WITH_SUFFIXES(amomax, d, rl)
DEF_ASM_WITH_SUFFIXES(amomax, d, aqrl)
DEF_ASM_WITH_SUFFIXES(amomaxu, w, aq)
DEF_ASM_WITH_SUFFIXES(amomaxu, w, rl)
DEF_ASM_WITH_SUFFIXES(amomaxu, w, aqrl)
DEF_ASM_WITH_SUFFIXES(amomaxu, d, aq)
DEF_ASM_WITH_SUFFIXES(amomaxu, d, rl)
DEF_ASM_WITH_SUFFIXES(amomaxu, d, aqrl)
DEF_ASM_WITH_SUFFIXES(amomin, w, aq)
DEF_ASM_WITH_SUFFIXES(amomin, w, rl)
DEF_ASM_WITH_SUFFIXES(amomin, w, aqrl)
DEF_ASM_WITH_SUFFIXES(amomin, d, aq)
DEF_ASM_WITH_SUFFIXES(amomin, d, rl)
DEF_ASM_WITH_SUFFIXES(amomin, d, aqrl)
DEF_ASM_WITH_SUFFIXES(amominu, w, aq)
DEF_ASM_WITH_SUFFIXES(amominu, w, rl)
DEF_ASM_WITH_SUFFIXES(amominu, w, aqrl)
DEF_ASM_WITH_SUFFIXES(amominu, d, aq)
DEF_ASM_WITH_SUFFIXES(amominu, d, rl)
DEF_ASM_WITH_SUFFIXES(amominu, d, aqrl)
/* rounding mode keywords (used as fcvt operand: fcvt.w.s rd, rs1, rtz) */
DEF_ASM(rne)
DEF_ASM(rtz)
DEF_ASM(rdn)
DEF_ASM(rup)
DEF_ASM(rmm)
/* `fence` arguments */
/* NOTE: Order is important */
DEF_ASM_FENCE(w)
DEF_ASM_FENCE(r)

View File

@ -386,6 +386,15 @@ Set type for PE (Windows) executables.
@item -Wl,-[Ttext=# | section-alignment=# | file-alignment=# | image-base=# | stack=#]
Modify executable layout.
@item -Wl,-[dynamicbase | nxcompat | high-entropy-va | tsaware]
@item -Wl,-[no-dynamicbase | no-nxcompat | no-high-entropy-va | no-tsaware]
@item -Wl,-[disable-dynamicbase | disable-nxcompat | disable-high-entropy-va | disable-tsaware]
Set or clear PE (Windows) executable header hardening flags. The
@option{-Wl,-high-entropy-va} option is supported on x86-64 and ARM64 PE
targets and implies @option{-Wl,-dynamicbase}. Clearing dynamicbase also
clears high-entropy-va. When @option{-Wl,-dynamicbase} is used for an
executable, TCC also enables base relocation emission for Windows ASLR.
@item -Wl,-Bsymbolic
Set DT_SYMBOLIC tag.

28
tcc.c
View File

@ -35,7 +35,7 @@ static const char help[] =
"General options:\n"
" -c compile only - generate an object file\n"
" -o outfile set output filename\n"
" -run run compiled source [with custom stdin: -rstdin FILE]\n"
" -run run compiled source\n"
" -fflag set or reset (with 'no-' prefix) 'flag' (see tcc -hh)\n"
" -Wwarning set or reset (with 'no-' prefix) 'warning' (see tcc -hh)\n"
" -w disable all warnings\n"
@ -105,6 +105,7 @@ static const char help2[] =
" -static link to static libraries (not recommended)\n"
" -dumpversion print version\n"
" -print-search-dirs print search paths\n"
" -rstdin file with -run: use 'file' as custom stdin\n"
" -dt with -run/-E: auto-define 'test_...' macros\n"
"Ignored options:\n"
" -arch -C --param -pedantic -pipe -s -traditional\n"
@ -225,7 +226,7 @@ static void print_search_dirs(TCCState *s)
printf("libtcc1:\n %s/%s\n", s->library_paths[0], CONFIG_TCC_CROSSPREFIX TCC_LIBTCC1);
#ifdef TCC_TARGET_UNIX
print_dirs("crt", s->crt_paths, s->nb_crt_paths);
printf("elfinterp:\n %s\n", DEFAULT_ELFINTERP(s));
printf("elfinterp:\n %s\n", s->elfint);
#endif
}
@ -293,7 +294,7 @@ int main(int argc, char **argv)
const char *first_file;
int argc0 = argc;
char **argv0 = argv;
FILE *ppfp = stdout;
FILE *ppfp = NULL;
redo:
argc = argc0, argv = argv0;
@ -335,7 +336,7 @@ redo:
tcc_error_noabort("no input files");
} else if (s->output_type == TCC_OUTPUT_PREPROCESS) {
if (s->outfile && 0!=strcmp("-",s->outfile)) {
ppfp = fopen(s->outfile, "wb");
ppfp = tcc_fopen(s->outfile, "wb");
if (!ppfp)
tcc_error_noabort("could not write '%s'", s->outfile);
}
@ -354,8 +355,9 @@ redo:
set_environment(s);
if (s->output_type == 0)
s->output_type = TCC_OUTPUT_EXE;
tcc_set_output_type(s, s->output_type);
s->ppfp = ppfp;
ret = tcc_set_output_type(s, s->output_type);
if (ppfp)
s->ppfp = ppfp;
if ((s->output_type == TCC_OUTPUT_MEMORY
|| s->output_type == TCC_OUTPUT_PREPROCESS)
@ -369,7 +371,7 @@ redo:
/* compile or add each files or library */
first_file = NULL;
do {
while (0 == ret) {
struct filespec *f = s->files[n];
s->filetype = f->type;
if (f->type & AFF_TYPE_LIB) {
@ -381,9 +383,11 @@ redo:
first_file = f->name;
ret = tcc_add_file(s, f->name);
}
} while (++n < s->nb_files
&& 0 == ret
&& (s->output_type != TCC_OUTPUT_OBJ || s->option_r));
if (++n == s->nb_files)
break;
if (s->output_type == TCC_OUTPUT_OBJ && !s->option_r)
break;
}
if (s->do_bench)
end_time = getclock_ms();
@ -422,7 +426,7 @@ redo:
tcc_delete(s);
if (!done)
goto redo;
if (ppfp && ppfp != stdout)
fclose(ppfp);
if (ppfp)
tcc_fclose(ppfp);
return ret;
}

117
tcc.h
View File

@ -52,6 +52,9 @@ extern long double strtold (const char *__nptr, char **__endptr);
#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN 1
# ifndef _WIN32_WINNT
# define _WIN32_WINNT 0x502 /* AddVectoredExceptionHandler */
# endif
# include <windows.h>
# include <io.h> /* open, close etc. */
# include <direct.h> /* getcwd */
@ -86,6 +89,9 @@ extern long double strtold (const char *__nptr, char **__endptr);
# define __x86_64__ 1
# endif
# endif
# if defined(_M_ARM64) && !defined(__aarch64__)
# define __aarch64__ 1
# endif
# ifndef va_copy
# define va_copy(a,b) a = b
# endif
@ -228,8 +234,7 @@ extern long double strtold (const char *__nptr, char **__endptr);
/* No ten-byte long doubles on window and macos except in
cross-compilers made by a mingw-GCC */
#if defined TCC_TARGET_PE \
|| (defined TCC_TARGET_MACHO && defined TCC_TARGET_ARM64) \
|| (defined _WIN32 && !defined __GNUC__)
|| (defined TCC_TARGET_MACHO && defined TCC_TARGET_ARM64)
# define TCC_USING_DOUBLE_FOR_LDOUBLE 1
#endif
@ -247,15 +252,14 @@ extern long double strtold (const char *__nptr, char **__endptr);
#ifndef CONFIG_SYSROOT
# define CONFIG_SYSROOT ""
#endif
#if !defined CONFIG_TCCDIR && !defined _WIN32
# define CONFIG_TCCDIR "/usr/local/lib/tcc"
#endif
#ifndef CONFIG_LDDIR
# define CONFIG_LDDIR "lib"
#endif
#ifdef CONFIG_TRIPLET
# define USE_TRIPLET(s) s "/" CONFIG_TRIPLET
# define ALSO_TRIPLET(s) USE_TRIPLET(s) ":" s
# define ALSO_TRIPLET(s) USE_TRIPLET(s) PATHSEP s
#else
# define USE_TRIPLET(s) s
# define ALSO_TRIPLET(s) s
@ -263,11 +267,8 @@ extern long double strtold (const char *__nptr, char **__endptr);
/* path to find crt1.o, crti.o and crtn.o */
#ifndef CONFIG_TCC_CRTPREFIX
# define CONFIG_TCC_CRTPREFIX USE_TRIPLET(CONFIG_SYSROOT "/usr/" CONFIG_LDDIR)
#endif
#ifndef CONFIG_USR_INCLUDE
# define CONFIG_USR_INCLUDE "/usr/include"
# define CONFIG_TCC_CRTPREFIX \
USE_TRIPLET(CONFIG_SYSROOT "/usr/lib")
#endif
/* Below: {B} is substituted by CONFIG_TCCDIR (rsp. -B option) */
@ -275,25 +276,22 @@ extern long double strtold (const char *__nptr, char **__endptr);
/* system include paths */
#ifndef CONFIG_TCC_SYSINCLUDEPATHS
# if defined TCC_TARGET_PE || defined _WIN32
# define CONFIG_TCC_SYSINCLUDEPATHS "{B}/include"PATHSEP"{B}/include/winapi"
# define CONFIG_TCC_SYSINCLUDEPATHS \
"{B}/include" PATHSEP "{B}/include/winapi"
# else
# define CONFIG_TCC_SYSINCLUDEPATHS \
"{B}/include" \
":" ALSO_TRIPLET(CONFIG_SYSROOT "/usr/local/include") \
":" ALSO_TRIPLET(CONFIG_SYSROOT CONFIG_USR_INCLUDE)
"{B}/include" PATHSEP ALSO_TRIPLET(CONFIG_SYSROOT "/usr/include")
# endif
#endif
/* library search paths */
#ifndef CONFIG_TCC_LIBPATHS
# if defined TCC_TARGET_PE || defined _WIN32
# define CONFIG_TCC_LIBPATHS "{B}/lib"
# define CONFIG_TCC_LIBPATHS \
"{B}/lib"
# else
# define CONFIG_TCC_LIBPATHS \
"{B}" \
":" ALSO_TRIPLET(CONFIG_SYSROOT "/usr/" CONFIG_LDDIR) \
":" ALSO_TRIPLET(CONFIG_SYSROOT "/" CONFIG_LDDIR) \
":" ALSO_TRIPLET(CONFIG_SYSROOT "/usr/local/" CONFIG_LDDIR)
"{B}" PATHSEP ALSO_TRIPLET(CONFIG_SYSROOT "/usr/lib")
# endif
#endif
@ -309,28 +307,19 @@ extern long double strtold (const char *__nptr, char **__endptr);
# define CONFIG_TCC_ELFINTERP "/lib64/ld-linux-x86-64.so.2"
# elif defined(TCC_TARGET_RISCV64)
# define CONFIG_TCC_ELFINTERP "/lib/ld-linux-riscv64-lp64d.so.1"
# elif defined(TCC_ARM_EABI)
# define DEFAULT_ELFINTERP(s) default_elfinterp(s)
# elif defined(TCC_TARGET_ARM)
# define CONFIG_TCC_ELFINTERP "/lib/ld-linux.so.3"
# define CONFIG_TCC_ELFINTERP_ARMHF "/lib/ld-linux-armhf.so.3"
# else
# define CONFIG_TCC_ELFINTERP "/lib/ld-linux.so.2"
# endif
#endif
/* var elf_interp dans *-gen.c */
#ifndef DEFAULT_ELFINTERP
# define DEFAULT_ELFINTERP(s) CONFIG_TCC_ELFINTERP
#endif
/* (target specific) libtcc1.a */
#ifndef TCC_LIBTCC1
# define TCC_LIBTCC1 "libtcc1.a"
#endif
/* library to use with CONFIG_USE_LIBGCC instead of libtcc1.a */
#if defined CONFIG_USE_LIBGCC && !defined TCC_LIBGCC
#define TCC_LIBGCC USE_TRIPLET(CONFIG_SYSROOT "/" CONFIG_LDDIR) "/libgcc_s.so.1"
#endif
/* <cross-prefix-to->libtcc1.a */
#ifndef CONFIG_TCC_CROSSPREFIX
# define CONFIG_TCC_CROSSPREFIX ""
@ -470,6 +459,9 @@ typedef struct CType {
struct Sym *ref;
} CType;
/* long double words on host(!) platform */
#define LDOUBLE_WORDS ((sizeof(long double)+3)/4)
/* constant value */
typedef union CValue {
long double ld;
@ -480,7 +472,7 @@ typedef union CValue {
char *data;
int size;
} str;
int tab[LDOUBLE_SIZE/4];
int tab[LDOUBLE_WORDS];
} CValue;
/* value on stack */
@ -735,6 +727,15 @@ struct sym_attr {
#endif
};
#ifdef TCC_TARGET_PE
#define PE_IMAGE_FILE_RELOCS_STRIPPED 0x0001
#define PE_IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020
#define PE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA 0x0020
#define PE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040
#define PE_DLLCHARACTERISTICS_NX_COMPAT 0x0100
#define PE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000
#endif
struct TCCState {
unsigned char verbose; /* if true, display some information during compilation */
unsigned char nostdinc; /* if true, no standard headers are added */
@ -897,6 +898,7 @@ struct TCCState {
/* predefined sections */
Section *text_section, *data_section, *rodata_section, *bss_section;
Section *tdata_section, *tbss_section;
Section *common_section;
Section *cur_text_section; /* current section where function code is generated */
#ifdef CONFIG_TCC_BCHECK
@ -938,23 +940,23 @@ struct TCCState {
#define qrel s1->qrel
#ifdef TCC_TARGET_RISCV64
struct pcrel_hi { addr_t addr, val; } last_hi;
struct pcrel_hi *pcrel_hi_entries;
struct pcrel_hi { addr_t addr, val; } **pcrel_hi_entries;
int nb_pcrel_hi_entries;
int alloc_pcrel_hi_entries;
#define last_hi s1->last_hi
#endif
#ifdef TCC_TARGET_PE
/* PE info */
int pe_subsystem;
unsigned pe_characteristics;
unsigned pe_dll_characteristics;
unsigned pe_dll_characteristics_clear;
unsigned pe_file_align;
unsigned pe_stack_size;
addr_t pe_imagebase;
# ifdef TCC_TARGET_X86_64
# if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
Section *uw_pdata;
int uw_sym;
int uw_xsym;
unsigned uw_offs;
# endif
#endif
@ -1073,7 +1075,8 @@ struct filespec {
#define VT_STATIC 0x00002000 /* static variable */
#define VT_TYPEDEF 0x00004000 /* typedef definition */
#define VT_INLINE 0x00008000 /* inline definition */
/* currently unused: 0x000[1248]0000 */
#define VT_TLS 0x00010000 /* thread-local storage */
/* currently unused: 0x000[248]0000 */
#define VT_STRUCT_SHIFT 20 /* shift for bitfield shift values (32 - 2*6) */
#define VT_STRUCT_MASK (((1U << (6+6)) - 1) << VT_STRUCT_SHIFT | VT_BITFIELD)
@ -1091,7 +1094,7 @@ struct filespec {
#define VT_ATOMIC VT_VOLATILE
/* type mask (except storage) */
#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE)
#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE | VT_TLS)
#define VT_TYPE (~(VT_STORAGE|VT_STRUCT_MASK))
/* symbol was created by tccasm.c first */
@ -1314,6 +1317,11 @@ PUB_FUNC void tcc_print_stats(TCCState *s, unsigned total_time);
PUB_FUNC int tcc_parse_args(TCCState *s, int *argc, char ***argv);
#ifdef _WIN32
ST_FUNC char *normalize_slashes(char *path);
PUB_FUNC FILE *tcc_fopen(const char *f, const char *m);
PUB_FUNC int tcc_fclose(FILE *f);
#else
# define tcc_fopen fopen
# define tcc_fclose fclose
#endif
ST_FUNC DLLReference *tcc_add_dllref(TCCState *s1, const char *dllname, int level);
ST_FUNC char *tcc_load_text(int fd);
@ -1438,8 +1446,8 @@ ST_DATA int nocode_wanted; /* true if no code generation wanted for an expressio
ST_DATA int global_expr; /* true if compound literals must be allocated globally (used during initializers parsing */
ST_DATA CType func_vt; /* current function return type (used by return instruction) */
ST_DATA int func_var; /* true if current function is variadic */
ST_DATA int func_vc;
ST_DATA int func_ind;
ST_DATA int func_vc; /* stack address for implicit struct return storage */
ST_DATA int func_ind; /* function start address */
ST_DATA const char *funcname;
ST_FUNC void tccgen_init(TCCState *s1);
@ -1685,7 +1693,6 @@ ST_FUNC void gen_increment_tcov (SValue *sv);
/* ------------ x86_64-gen.c ------------ */
#ifdef TCC_TARGET_X86_64
ST_FUNC void gen_addr64(int r, Sym *sym, int64_t c);
ST_FUNC void gen_opl(int op);
#ifdef TCC_TARGET_PE
ST_FUNC void gen_vla_result(int addr);
@ -1718,11 +1725,12 @@ ST_FUNC void gen_increment_tcov (SValue *sv);
/* ------------ riscv64-gen.c ------------ */
#ifdef TCC_TARGET_RISCV64
ST_FUNC void gen_opl(int op);
//ST_FUNC void gfunc_return(CType *func_type);
ST_FUNC void gen_va_start(void);
ST_FUNC void arch_transfer_ret_regs(int);
ST_FUNC void gen_cvt_sxtw(void);
ST_FUNC void gen_cvt_csti(int t);
ST_FUNC void gen_increment_tcov (SValue *sv);
ST_FUNC void gen_clear_cache(void);
#endif
/* ------------ c67-gen.c ------------ */
@ -1744,11 +1752,11 @@ ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands, const char *n
ST_FUNC Sym* get_asm_sym(int name, Sym *csym);
ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
ST_FUNC int asm_int_expr(TCCState *s1);
/* ------------ i386-asm.c ------------ */
ST_FUNC void gen_expr32(ExprValue *pe);
#ifdef TCC_TARGET_X86_64
#if PTR_SIZE == 8
ST_FUNC void gen_expr64(ExprValue *pe);
#endif
/* ------------ i386-asm.c ------------ */
ST_FUNC void gen_expr32(ExprValue *pe);
ST_FUNC void asm_opcode(TCCState *s1, int opcode);
ST_FUNC int asm_parse_regvar(int t);
ST_FUNC void asm_compute_constraints(ASMOperand *operands, int nb_operands, int nb_outputs, const uint8_t *clobber_regs, int *pout_reg);
@ -1765,7 +1773,7 @@ ST_FUNC int pe_putimport(TCCState *s1, int dllindex, const char *name, addr_t va
ST_FUNC int pe_setsubsy(TCCState *s1, const char *arg);
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
#endif
#ifdef TCC_TARGET_X86_64
#if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack);
#endif
PUB_FUNC int tcc_get_dllexports(const char *filename, char **pp);
@ -1922,10 +1930,15 @@ dwarf_read_sleb128(unsigned char **ln, unsigned char *end)
/********************************************************/
#if CONFIG_TCC_SEMLOCK
#if defined _WIN32
typedef struct { int init; CRITICAL_SECTION cs; } TCCSem;
typedef struct { volatile LONG init; CRITICAL_SECTION cs; } TCCSem;
static inline void wait_sem(TCCSem *p) {
if (!p->init)
InitializeCriticalSection(&p->cs), p->init = 1;
if (InterlockedCompareExchange(&p->init, 1, 0) == 0) {
InitializeCriticalSection(&p->cs);
InterlockedExchange(&p->init, 2);
} else {
while (InterlockedCompareExchange(&p->init, 2, 2) != 2)
Sleep(0);
}
EnterCriticalSection(&p->cs);
}
static inline void post_sem(TCCSem *p) {
@ -1976,6 +1989,8 @@ static inline void post_sem(TCCSem *p) {
#define data_section TCC_STATE_VAR(data_section)
#define rodata_section TCC_STATE_VAR(rodata_section)
#define bss_section TCC_STATE_VAR(bss_section)
#define tdata_section TCC_STATE_VAR(tdata_section)
#define tbss_section TCC_STATE_VAR(tbss_section)
#define common_section TCC_STATE_VAR(common_section)
#define cur_text_section TCC_STATE_VAR(cur_text_section)
#define bounds_section TCC_STATE_VAR(bounds_section)

105
tccasm.c
View File

@ -25,6 +25,26 @@
static Section *last_text_section; /* to handle .previous asm directive */
static int asmgoto_n;
static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global);
static Sym* asm_new_label(TCCState *s1, int label, int is_local);
static Sym* asm_new_label1(TCCState *s1, int label, int is_local, int sh_num, int value);
#if PTR_SIZE == 8
/* output constant with relocation if 'r & VT_SYM' is true */
ST_FUNC void gen_addr64(int r, Sym *sym, int64_t c)
{
if (r & VT_SYM)
greloca(cur_text_section, sym, ind, R_DATA_PTR, c), c=0;
gen_le32(c);
gen_le32(c>>32);
}
ST_FUNC void gen_expr64(ExprValue *pe)
{
gen_addr64(pe->sym ? VT_SYM : 0, pe->sym, pe->v);
}
#endif
static int asm_get_prefix_name(TCCState *s1, const char *prefix, unsigned int n)
{
char buf[64];
@ -37,10 +57,6 @@ ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n)
return asm_get_prefix_name(s1, "L..", n);
}
static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global);
static Sym* asm_new_label(TCCState *s1, int label, int is_local);
static Sym* asm_new_label1(TCCState *s1, int label, int is_local, int sh_num, int value);
/* If a C name has an _ prepended then only asm labels that start
with _ are representable in C, by removing the first _. ASM names
without _ at the beginning don't correspond to C names, but we use
@ -328,12 +344,12 @@ static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
if (esym1 && esym1->st_shndx == esym2->st_shndx
&& esym1->st_shndx != SHN_UNDEF) {
/* we also accept defined symbols in the same section */
pe->v += esym1->st_value - esym2->st_value;
pe->v += (int)(esym1->st_value - esym2->st_value);
pe->sym = NULL;
} else if (esym2->st_shndx == cur_text_section->sh_num) {
/* When subtracting a defined symbol in current section
this actually makes the value PC-relative. */
pe->v += 0 - esym2->st_value;
pe->v += (int)(0 - esym2->st_value);
pe->pcrel = 1;
e2.sym = NULL;
} else {
@ -494,7 +510,7 @@ static void pop_section(TCCState *s1)
static void asm_parse_directive(TCCState *s1, int global)
{
int n, offset, v, size, tok1;
int n, offset, v, size, tok1, c;
Section *sec;
uint8_t *ptr;
@ -524,18 +540,25 @@ static void asm_parse_directive(TCCState *s1, int global)
/* the section must have a compatible alignment */
if (sec->sh_addralign < n)
sec->sh_addralign = n;
c = sec->sh_flags & SHF_EXECINSTR;
} else {
if (n < 0)
n = 0;
size = n;
size = n, c = 0;
}
v = 0;
if (tok == ',') {
next();
v = asm_int_expr(s1);
v = asm_int_expr(s1), c = 0;
}
zero_pad:
if ((uint64_t)ind + size >= 1<<30)
tcc_error("too much data");
if (sec->sh_type != SHT_NOBITS) {
if (c) {
gen_fill_nops(size);
break;
}
sec->data_offset = ind;
ptr = section_ptr_add(sec, size);
memset(ptr, v, size);
@ -543,7 +566,7 @@ static void asm_parse_directive(TCCState *s1, int global)
ind += size;
break;
case TOK_ASMDIR_quad:
#ifdef TCC_TARGET_X86_64
#if PTR_SIZE == 8
size = 8;
goto asm_data;
#else
@ -592,7 +615,7 @@ static void asm_parse_directive(TCCState *s1, int global)
if (sec->sh_type != SHT_NOBITS) {
if (size == 4) {
gen_expr32(&e);
#ifdef TCC_TARGET_X86_64
#if PTR_SIZE == 8
} else if (size == 8) {
gen_expr64(&e);
#endif
@ -690,11 +713,9 @@ static void asm_parse_directive(TCCState *s1, int global)
expect("constant or same-section symbol");
n += esym->st_value;
}
if (n < 0 || n > 0x100000)
tcc_error(".org out of range");
if (n < ind)
tcc_error("attempt to .org backwards");
v = 0;
v = c = 0;
size = n - ind;
goto zero_pad;
}
@ -812,6 +833,7 @@ static void asm_parse_directive(TCCState *s1, int global)
case TOK_ASMDIR_size:
{
Sym *sym;
ElfSym *esym;
next();
if (tok < TOK_IDENT)
@ -823,8 +845,10 @@ static void asm_parse_directive(TCCState *s1, int global)
tcc_warning_c(warn_unsupported)("ignoring .size %s,*", get_tok_str(tok, NULL));
next();
skip(',');
while (tok != TOK_LINEFEED && tok != ';' && tok != CH_EOF) {
next();
n = asm_int_expr(s1);
esym = elfsym(sym);
if (esym) {
esym->st_size = n;
}
}
break;
@ -952,7 +976,7 @@ static void asm_parse_directive(TCCState *s1, int global)
}
break;
#endif
#ifdef TCC_TARGET_X86_64
#if PTR_SIZE == 8
/* added for compatibility with GAS */
case TOK_ASMDIR_code64:
next();
@ -995,17 +1019,41 @@ static void asm_parse_directive(TCCState *s1, int global)
case TOK_ASMDIR_reloc:
{
ExprValue e;
const char *reloc_name;
int reloc_type = -1;
next();
asm_expr(s1, &e);
skip(',');
reloc_name = get_tok_str(tok, NULL);
#if defined(TCC_TARGET_ARM64)
if (strcmp(get_tok_str(tok, NULL), "R_AARCH64_CALL26"))
if (!strcmp(reloc_name, "R_AARCH64_CALL26"))
reloc_type = R_AARCH64_CALL26;
#elif defined(TCC_TARGET_RISCV64)
if (!strcmp(reloc_name, "R_RISCV_CALL") || !strcmp(reloc_name, "R_RISCV_CALL_PLT"))
reloc_type = R_RISCV_CALL;
else if (!strcmp(reloc_name, "R_RISCV_BRANCH"))
reloc_type = R_RISCV_BRANCH;
else if (!strcmp(reloc_name, "R_RISCV_JAL"))
reloc_type = R_RISCV_JAL;
else if (!strcmp(reloc_name, "R_RISCV_PCREL_HI20"))
reloc_type = R_RISCV_PCREL_HI20;
else if (!strcmp(reloc_name, "R_RISCV_PCREL_LO12_I"))
reloc_type = R_RISCV_PCREL_LO12_I;
else if (!strcmp(reloc_name, "R_RISCV_PCREL_LO12_S"))
reloc_type = R_RISCV_PCREL_LO12_S;
else if (!strcmp(reloc_name, "R_RISCV_32_PCREL"))
reloc_type = R_RISCV_32_PCREL;
else if (!strcmp(reloc_name, "R_RISCV_32"))
reloc_type = R_RISCV_32;
else if (!strcmp(reloc_name, "R_RISCV_64"))
reloc_type = R_RISCV_64;
#endif
tcc_error("unimp: reloc '%s' unknown", get_tok_str(tok, NULL));
if (reloc_type < 0)
tcc_error("unimp: reloc '%s' unknown", reloc_name);
next();
skip(',');
greloca(cur_text_section, get_asm_sym(tok, NULL), e.v, R_AARCH64_CALL26, 0);
greloca(cur_text_section, get_asm_sym(tok, NULL), e.v, reloc_type, 0);
next();
}
break;
@ -1032,11 +1080,14 @@ static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global)
tcc_debug_line(s1);
parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
redo:
#if !defined(TCC_TARGET_ARM64)
if (tok == '#') {
/* horrible gas comment */
while (tok != TOK_LINEFEED)
next();
} else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
} else
#endif
if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
asm_parse_directive(s1, global);
} else if (tok == TOK_PPNUM) {
const char *p;
@ -1176,6 +1227,9 @@ static void subst_asm_operands(ASMOperand *operands, int nb_operands,
if (*str == 'c' || *str == 'n' ||
*str == 'b' || *str == 'w' || *str == 'h' || *str == 'k' ||
*str == 'q' || *str == 'l' ||
#ifdef TCC_TARGET_ARM64
*str == 'x' || *str == 's' || *str == 'd' || *str == 'Z' ||
#endif
#ifdef TCC_TARGET_RISCV64
*str == 'z' ||
#endif
@ -1196,7 +1250,7 @@ static void subst_asm_operands(ASMOperand *operands, int nb_operands,
sv = *op->vt;
if (op->reg >= 0) {
sv.r = op->reg;
if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
if (op->is_memory)
sv.r |= VT_LVAL;
}
subst_asm_operand(out_str, &sv, modifier);
@ -1248,7 +1302,12 @@ static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
if ((vtop->r & VT_LVAL) &&
((vtop->r & VT_VALMASK) == VT_LLOCAL ||
(vtop->r & VT_VALMASK) < VT_CONST) &&
!strchr(op->constraint, 'm')) {
!strchr(op->constraint, 'm')
#ifdef TCC_TARGET_ARM64
&& !strchr(op->constraint, 'Q')
&& !strstr(op->constraint, "Ump")
#endif
) {
gv(RC_INT);
}
}

120
tccelf.c
View File

@ -70,6 +70,8 @@ ST_FUNC void tccelf_new(TCCState *s)
/* create ro data section (make ro after relocation done with GNU_RELRO) */
rodata_section = new_section(s, rdata, SHT_PROGBITS, shf_RELRO);
bss_section = new_section(s, ".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE);
tdata_section = new_section(s, ".tdata", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE | SHF_TLS);
tbss_section = new_section(s, ".tbss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE | SHF_TLS);
common_section = new_section(s, ".common", SHT_NOBITS, SHF_PRIVATE);
common_section->sh_num = SHN_COMMON;
@ -110,6 +112,21 @@ ST_FUNC void tccelf_new(TCCState *s)
if (s->elf_entryname)
set_global_sym(s, s->elf_entryname, NULL, 0); /* SHN_UNDEF */
#endif
#ifndef ELF_OBJ_ONLY
if (NULL == s->elfint && s1->output_type != TCC_OUTPUT_OBJ) {
const char *p = CONFIG_TCC_ELFINTERP;
#if defined TCC_TARGET_ARM && defined CONFIG_TCC_ELFINTERP_ARMHF
if (s->float_abi == ARM_HARD_FLOAT)
p = CONFIG_TCC_ELFINTERP_ARMHF;
#endif
#if defined TCC_IS_NATIVE && defined TARGETOS_BSD
/* see commit 55cb2170cd5ce77a7d76dcaf462fad2707281605 */
{ const char *e = getenv("LD_SO"); if (e) p = e; }
#endif
s->elfint = tcc_strdup(p);
}
#endif /* ndef ELF_OBJ_ONLY */
}
ST_FUNC void free_section(Section *s)
@ -145,9 +162,6 @@ ST_FUNC void tccelf_delete(TCCState *s1)
dynarray_reset(&s1->priv_sections, &s1->nb_priv_sections);
tcc_free(s1->sym_attrs);
#ifdef TCC_TARGET_RISCV64
tcc_free(s1->pcrel_hi_entries);
#endif
symtab_section = NULL; /* for tccrun.c:rt_printline() */
}
@ -1109,7 +1123,7 @@ ST_FUNC void relocate_syms(TCCState *s1, Section *symtab, int do_resolve)
if (sym_bind == STB_WEAK)
sym->st_value = 0;
else
tcc_error_noabort("undefined symbol '%s'", name);
tcc_error_noabort("unresolved reference to '%s'", name);
} else if (sh_num < SHN_LORESERVE) {
/* add section base */
@ -1130,10 +1144,6 @@ static void relocate_section(TCCState *s1, Section *s, Section *sr)
addr_t tgt, addr;
int is_dwarf = s->sh_num >= s1->dwlo && s->sh_num < s1->dwhi;
#ifdef TCC_TARGET_RISCV64
s1->nb_pcrel_hi_entries = 0;
#endif
qrel = (ElfW_Rel *)sr->data;
for_each_elem(sr, 0, rel, ElfW_Rel) {
if (s->data == NULL) /* bss */
@ -1155,6 +1165,7 @@ static void relocate_section(TCCState *s1, Section *s, Section *sr)
addr = s->sh_addr + rel->r_offset;
relocate(s1, rel, type, ptr, addr, tgt);
}
#ifndef ELF_OBJ_ONLY
/* if the relocation is allocated, we change its symbol table */
if (sr->sh_flags & SHF_ALLOC) {
@ -1172,6 +1183,10 @@ static void relocate_section(TCCState *s1, Section *s, Section *sr)
}
}
#endif
#ifdef TCC_TARGET_RISCV64
dynarray_reset(&s1->pcrel_hi_entries, &s1->nb_pcrel_hi_entries);
#endif
}
/* relocate all sections */
@ -2076,7 +2091,7 @@ static void bind_exe_dynsyms(TCCState *s1, int is_PIE)
if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK ||
!strcmp(name, "_fp_hw")) {
} else {
tcc_error_noabort("undefined symbol '%s'", name);
tcc_error_noabort("unresolved reference to '%s'", name);
}
}
}
@ -2107,7 +2122,7 @@ static void bind_libs_dynsyms(TCCState *s1)
if (esym->st_shndx == SHN_UNDEF) {
/* weak symbols can stay undefined */
if (ELFW(ST_BIND)(esym->st_info) != STB_WEAK)
tcc_warning("undefined dynamic symbol '%s'", name);
tcc_warning("unresolved dynamic reference to '%s'", name);
}
}
}
@ -2281,7 +2296,7 @@ static int sort_sections(TCCState *s1, int *sec_order, struct dyn_inf *d)
if (k < 0x900)
++d->shnum;
if (k < 0x700) {
f = s->sh_flags & (SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR|SHF_TLS);
f = s->sh_flags & (SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR);
#if TARGETOS_NetBSD
/* NetBSD only supports 2 PT_LOAD sections.
See: https://blog.netbsd.org/tnf/entry/the_first_report_on_lld */
@ -2340,6 +2355,18 @@ static int layout_sections(TCCState *s1, int *sec_order, struct dyn_inf *d)
++phnum;
if (d->roinf)
++phnum;
{
int has_tls = 0;
for (i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
if (s->sh_flags & SHF_TLS) {
has_tls = 1;
break;
}
}
if (has_tls)
++phnum;
}
d->phnum = phnum;
d->phdr = tcc_mallocz(phnum * sizeof(ElfW(Phdr)));
@ -2416,10 +2443,6 @@ static int layout_sections(TCCState *s1, int *sec_order, struct dyn_inf *d)
ph->p_flags |= PF_W;
if (f & SHF_EXECINSTR)
ph->p_flags |= PF_X;
if (f & SHF_TLS) {
ph->p_type = PT_TLS;
ph->p_align = align + 1;
}
ph->p_offset = file_offset;
ph->p_vaddr = addr;
@ -2464,6 +2487,34 @@ static int layout_sections(TCCState *s1, int *sec_order, struct dyn_inf *d)
fill_phdr(++ph, PT_GNU_EH_FRAME, eh_frame_hdr_section);
if (d->roinf)
fill_phdr(++ph, PT_GNU_RELRO, d->roinf)->p_flags |= PF_W;
{
/* Create PT_TLS segment covering all TLS sections */
Section *tls_start_sec = NULL;
addr_t tls_start = 0, tls_end = 0;
for (i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
if (s->sh_flags & SHF_TLS && s->sh_size) {
if (!tls_start_sec) {
tls_start_sec = s;
tls_start = s->sh_addr;
tls_end = s->sh_addr + s->sh_size;
} else {
if (s->sh_addr < tls_start)
tls_start = s->sh_addr;
if (s->sh_addr + s->sh_size > tls_end)
tls_end = s->sh_addr + s->sh_size;
}
}
}
if (tls_start_sec) {
ph = fill_phdr(++ph, PT_TLS, tls_start_sec);
ph->p_vaddr = tls_start;
ph->p_paddr = tls_start;
ph->p_filesz = tls_end - tls_start;
ph->p_memsz = ph->p_filesz;
ph->p_align = tls_start_sec->sh_addralign;
}
}
if (d->interp)
fill_phdr(&d->phdr[1], PT_INTERP, d->interp);
if (phfill) {
@ -2839,6 +2890,28 @@ static void create_arm_attribute_section(TCCState *s1)
}
#endif
#ifdef TCC_TARGET_RISCV64
static void create_riscv_attribute_section(TCCState *s1)
{
static const unsigned char riscv_attr[] = {
0x41, /* 'A' */
0x49, 0x00, 0x00, 0x00, /* total_len = 73 */
'r', 'i', 's', 'c', 'v', 0x00, /* "riscv\0" */
0x3a, 0x00, 0x00, 0x00, /* file_len = 58 */
0x05, /* Tag_RISCV_arch */
0x35, 0x00, 0x00, 0x00, /* isa_len = 53 */
'r','v','6','4','i','2','p','1','_','m','2','p','0','_',
'a','2','p','1','_','f','2','p','2','_','d','2','p','2','_',
'c','2','p','0','_','z','i','c','s','r','2','p','0','_',
'z','i','f','e','n','c','e','i','2','p','0', 0x00,
};
Section *attr = new_section(s1, ".riscv.attributes", SHT_RISCV_ATTRIBUTES, 0);
unsigned char *ptr = section_ptr_add(attr, sizeof(riscv_attr));
attr->sh_addralign = 1;
memcpy(ptr, riscv_attr, sizeof(riscv_attr));
}
#endif
#if TARGETOS_OpenBSD || TARGETOS_NetBSD || TARGETOS_FreeBSD
static void fill_bsd_note(Section *s, int type,
@ -2941,18 +3014,10 @@ static int elf_output_file(TCCState *s1, const char *filename)
if (!s1->static_link) {
if (file_type & TCC_OUTPUT_EXE) {
char *ptr;
/* allow override the dynamic loader */
const char *elfint = s1->elfint;
if (elfint == NULL)
elfint = getenv("LD_SO");
if (elfint == NULL)
elfint = DEFAULT_ELFINTERP(s1);
/* add interpreter section only if executable */
interp = new_section(s1, ".interp", SHT_PROGBITS, SHF_ALLOC);
interp->sh_addralign = 1;
ptr = section_ptr_add(interp, 1 + strlen(elfint));
strcpy(ptr, elfint);
put_elf_str(interp, s1->elfint);
dyninf.interp = interp;
}
@ -3103,10 +3168,13 @@ static void alloc_sec_names(TCCState *s1, int is_obj)
}
/* Output an elf .o file */
LIBTCCAPI int elf_output_obj(TCCState *s1, const char *filename)
static int elf_output_obj(TCCState *s1, const char *filename)
{
Section *s;
int i, ret, file_offset;
#ifdef TCC_TARGET_RISCV64
create_riscv_attribute_section(s1);
#endif
/* Allocate strings for section names */
alloc_sec_names(s1, 1);
file_offset = (sizeof (ElfW(Ehdr)) + 3) & -4;
@ -3303,6 +3371,8 @@ invalid:
continue;
if (sh->sh_type != s->sh_type
&& strcmp (s->name, ".eh_frame")
/* some crt1.o seem to have two ".note.GNU-stack" (SHT_NOTE & SHT_PROGBITS) */
&& strcmp (s->name, ".note.GNU-stack")
) {
tcc_error_noabort("section type conflict: %s %02x <> %02x", s->name, sh->sh_type, s->sh_type);
goto the_end;

241
tccgen.c
View File

@ -70,8 +70,9 @@ ST_DATA int nocode_wanted; /* no code generation wanted */
ST_DATA int global_expr; /* true if compound literals must be allocated globally (used during initializers parsing */
ST_DATA CType func_vt; /* current function return type (used by return instruction) */
ST_DATA int func_var; /* true if current function is variadic (used by return instruction) */
ST_DATA int func_vc;
ST_DATA int func_ind;
ST_DATA int func_vc; /* stack address for implicit struct return storage */
ST_DATA int func_ind; /* function start address */
static int func_old;
ST_DATA const char *funcname;
ST_DATA CType int_type, func_old_type, char_type, char_pointer_type;
static CString initstr;
@ -322,12 +323,6 @@ ST_FUNC int ieee_finite(double d)
return ((unsigned)((p[1] | 0x800fffff) + 1)) >> 31;
}
/* compiling intel long double natively */
#if (defined __i386__ || defined __x86_64__) \
&& (defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64)
# define TCC_IS_NATIVE_387
#endif
ST_FUNC void test_lvalue(void)
{
if (!(vtop->r & VT_LVAL))
@ -531,6 +526,8 @@ ST_FUNC void put_extern_sym2(Sym *sym, int sh_num,
sym_type = STT_NOTYPE;
if (IS_ASM_FUNC(t))
sym_type = STT_FUNC;
} else if (t & VT_TLS) {
sym_type = STT_TLS;
} else {
sym_type = STT_OBJECT;
}
@ -1385,7 +1382,7 @@ static Sym *external_sym(int v, CType *type, int r, AttributeDef *ad)
/* make sure that type->ref is on global stack */
move_ref_to_global(s);
/* put into local scope */
s = sym_copy(s, &local_stack);
sym_copy(s, &local_stack);
}
return s;
}
@ -1906,6 +1903,8 @@ ST_FUNC int gv(int rc)
#endif
bt = vtop->type.t & VT_BTYPE;
if (bt == VT_VOID || bt == VT_STRUCT) /* should not happen */
return vtop->r;
#ifdef TCC_TARGET_RISCV64
/* XXX mega hack */
@ -2521,11 +2520,17 @@ void gen_negf(int op)
operation. We implement this with bit manipulation and have
to do some type reinterpretation for this, which TCC can do
only via memory. */
int align, size, bt;
size = type_size(&vtop->type, &align);
bt = vtop->type.t & VT_BTYPE;
#if defined TCC_TARGET_X86_64 || defined TCC_TARGET_I386
/* sizeof long double is 12 or 16 here, but it's really the 80bit
extended float format. */
if (bt == VT_LDOUBLE)
size = 10;
#endif
if (nocode_wanted) /* save_reg() wont work */
goto gv2;
save_reg(gv(RC_TYPE(bt)));
vdup();
incr_bf_adr(size - 1);
@ -2534,6 +2539,8 @@ void gen_negf(int op)
gen_op('^');
vstore();
vpop();
gv2:
gv(RC_TYPE(bt)); /* -n is not a lvalue */
}
#endif
@ -2919,7 +2926,8 @@ static int combine_types(CType *dest, SValue *op1, SValue *op2, int op)
type.ref = NULL;
if (bt1 == VT_VOID || bt2 == VT_VOID) {
ret = op == '?' ? 1 : 0;
if (op != '?')
tcc_error("operation on void value");
/* NOTE: as an extension, we accept void on only one side */
type.t = VT_VOID;
} else if (bt1 == VT_PTR || bt2 == VT_PTR) {
@ -3097,9 +3105,7 @@ op_err:
#endif
type1 = vtop[-1].type;
vpush_type_size(pointed_type(&vtop[-1].type), &align);
if (!(vtop[-1].type.t & VT_UNSIGNED)) {
gen_cast_s(VT_PTRDIFF_T);
}
vtop->type.t &= ~VT_UNSIGNED;
gen_op('*');
#ifdef CONFIG_TCC_BCHECK
if (tcc_state->do_bounds_check && !CONST_WANTED) {
@ -3168,7 +3174,7 @@ op_err:
}
// Make sure that we have converted to an rvalue:
if (vtop->r & VT_LVAL)
gv(is_float(vtop->type.t & VT_BTYPE) ? RC_FLOAT : RC_INT);
gv(RC_TYPE(vtop->type.t));
}
#if defined TCC_TARGET_ARM64 || defined TCC_TARGET_RISCV64 || defined TCC_TARGET_ARM
@ -3273,20 +3279,15 @@ again:
df = is_float(dbt);
dbt_bt = dbt & VT_BTYPE;
sbt_bt = sbt & VT_BTYPE;
if (dbt_bt == VT_VOID)
if (dbt_bt == VT_VOID) {
goto done;
}
if (sbt_bt == VT_VOID) {
error:
cast_error(&vtop->type, type);
}
c = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
#if !defined TCC_IS_NATIVE && !defined TCC_IS_NATIVE_387
/* don't try to convert to ldouble when cross-compiling
(except when it's '0' which is needed for arm:gen_negf()) */
if (dbt_bt == VT_LDOUBLE && !nocode_wanted && (sf || vtop->c.i != 0))
c = 0;
#endif
if (c) {
/* constant case: we can do it now */
/* XXX: in ISOC, cannot do it if error in convert */
@ -3353,8 +3354,11 @@ error:
}
/* cannot generate code for global or static initializers */
if (nocode_wanted & DATA_ONLY_WANTED)
if (nocode_wanted & DATA_ONLY_WANTED) {
if (df)
vtop->r = get_reg(RC_FLOAT); /* don't confuse backends */
goto done;
}
/* non constant case: generate code */
if (dbt == VT_BOOL) {
@ -3463,11 +3467,11 @@ error:
if (ds >= ss)
goto done;
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 || defined TCC_TARGET_ARM64
if (ss == 4) {
gen_cvt_csti(dbt);
goto done;
}
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 || defined TCC_TARGET_ARM64 || defined TCC_TARGET_RISCV64
if (ss == 4) {
gen_cvt_csti(dbt);
goto done;
}
#endif
bits = (ss - ds) * 8;
/* for unsigned, gen_op will convert SAR to SHR */
@ -4745,6 +4749,8 @@ static int parse_btype(CType *type, AttributeDef *ad, int ignore_label)
} else {
if (bt != -1 || (st != -1 && u != VT_INT))
goto tmbt;
if ((t & VT_DEFSIGN) && (u == VT_VOID || u > VT_LLONG))
goto tmbt;
bt = u;
}
if (u != VT_INT)
@ -4927,7 +4933,12 @@ static int parse_btype(CType *type, AttributeDef *ad, int ignore_label)
}
goto basic_type2;
case TOK_THREAD_LOCAL:
tcc_error("_Thread_local is not implemented");
case TOK___thread:
if (t & VT_TLS)
tcc_error("multiple thread-local storage specifiers");
t |= VT_TLS;
next();
break;
default:
if (typespec_found)
goto the_end;
@ -5627,6 +5638,7 @@ ST_FUNC void unary(void)
case TOK_CLDOUBLE:
#ifdef TCC_USING_DOUBLE_FOR_LDOUBLE
t = VT_DOUBLE | VT_LONG;
tokc.d = tokc.ld;
#else
t = VT_LDOUBLE;
#endif
@ -5941,6 +5953,8 @@ ST_FUNC void unary(void)
vtop->type = type;
break;
}
#endif
#ifdef TCC_TARGET_ARM64
case TOK___arm64_clear_cache: {
parse_builtin_params(0, "ee");
gen_clear_cache();
@ -5949,6 +5963,17 @@ ST_FUNC void unary(void)
break;
}
#endif
#ifdef TCC_TARGET_RISCV64
case TOK___riscv64_clear_cache: {
parse_builtin_params(0, "ee");
vpop();
vpop();
gen_clear_cache();
vpushi(0);
vtop->type.t = VT_VOID;
break;
}
#endif
/* atomic operations */
case TOK___atomic_store:
@ -6104,6 +6129,7 @@ special_math_val:
tcc_error("'%s' undeclared", name);
/* for simple function calls, we tolerate undeclared
external reference to int() function */
if (!func_old)
tcc_warning_c(warn_implicit_function_declaration)(
"implicit declaration of function '%s'", name);
s = external_global_sym(t, &func_old_type);
@ -6158,7 +6184,8 @@ special_math_val:
gen_op('+');
/* change type to field type, and set to lvalue */
vtop->type = s->type;
vtop->type.t |= qualifiers;
if (qualifiers)
parse_btype_qualify(&vtop->type, qualifiers);
/* an array is never an lvalue */
if (!(vtop->type.t & VT_ARRAY)) {
vtop->r |= VT_LVAL;
@ -6652,7 +6679,7 @@ static void expr_cond(void)
/* keep structs lvalue by transforming `(expr ? a : b)` to `*(expr ? &a : &b)` so
that `(expr ? a : b).mem` does not error with "lvalue expected" */
islv = (vtop->r & VT_LVAL) && (sv.r & VT_LVAL) && VT_STRUCT == (type.t & VT_BTYPE);
islv = VT_STRUCT == (type.t & VT_BTYPE);
/* now we convert second operand */
if (c != 1) {
@ -6660,8 +6687,7 @@ static void expr_cond(void)
if (islv) {
mk_pointer(&vtop->type);
gaddrof();
} else if (VT_STRUCT == (vtop->type.t & VT_BTYPE))
gaddrof();
}
}
rc = RC_TYPE(type.t);
@ -6672,7 +6698,8 @@ static void expr_cond(void)
tt = r2 = 0;
if (c < 0) {
r2 = gv(rc);
if (type.t != VT_VOID)
r2 = gv(rc);
tt = gjmp(0);
}
gsym(u);
@ -6687,14 +6714,15 @@ static void expr_cond(void)
if (islv) {
mk_pointer(&vtop->type);
gaddrof();
} else if (VT_STRUCT == (vtop->type.t & VT_BTYPE))
gaddrof();
}
}
if (c < 0) {
r1 = gv(rc);
move_reg(r2, r1, islv ? VT_PTR : type.t);
vtop->r = r2;
if (type.t != VT_VOID) {
r1 = gv(rc);
move_reg(r2, r1, islv ? VT_PTR : type.t);
vtop->r = r2;
}
gsym(tt);
}
@ -6737,7 +6765,8 @@ ST_FUNC void gexpr(void)
/* make builtin_constant_p((1,2)) return 0 (like on gcc) */
if ((vtop->r & VT_VALMASK) == VT_CONST && nocode_wanted && !CONST_WANTED)
gv(RC_TYPE(vtop->type.t));
if (vtop->type.t != VT_VOID && (vtop->type.t & VT_BTYPE) != VT_STRUCT)
gv(RC_TYPE(vtop->type.t));
}
}
@ -6847,11 +6876,10 @@ static void check_func_return(void)
{
if ((func_vt.t & VT_BTYPE) == VT_VOID)
return;
if (!strcmp (funcname, "main")
if ((!strcmp(funcname, "main") || func_old)
&& (func_vt.t & VT_BTYPE) == VT_INT) {
/* main returns 0 by default */
vpushi(0);
gen_assign_cast(&func_vt);
gfunc_return(&func_vt);
} else {
tcc_warning("function might return no value: '%s'", funcname);
@ -7234,6 +7262,8 @@ again:
tcc_warning("void function returns a value");
vtop--;
}
} else if (b && func_old && (func_vt.t & VT_BTYPE) == VT_INT) {
vpushi(0);
} else if (b) {
tcc_warning("'return' with no value");
b = 0;
@ -7758,6 +7788,71 @@ static int decl_designator(init_params *p, CType *type, unsigned long c,
return al;
}
static void write_ldouble(unsigned char *d, void *s)
{
//printf("long double %Lf\n", *(long double*)s);
#ifdef TCC_CROSS_TEST
if (LDOUBLE_SIZE >= 10) {
double b = *(long double*)s;
s = &b;
#else
if (sizeof (long double) == 8 && LDOUBLE_SIZE >= 10) {
#endif
/* our 'long double' is a double really (_WIN32, __APPLE__) */
uint64_t m = *(uint64_t*)s;
int e = m >> 48;
int f = e >> 4 & 0x7FF;
m <<= 11;
if (0 == f) {
if (0 == m)
goto set;
for (f = 1; !(m & 1ULL<<63); --f)
m <<= 1;
}
if (f == 0x7ff)
f = 0x43FF;
e = (e & 0x8000) | (f + 0x3C00);
m |= 1ULL<<63;
set:
#if (defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64)
/* double -> extended */
write64le(d, m);
write16le(d+8, e);
#elif LDOUBLE_SIZE == 16
/* double -> quad */
write64le(d+6, m << 1);
write16le(d+14, e);
#endif
;
} else {
#if LDOUBLE_SIZE == 8
/* long double -> double */
double b = *(long double*)s;
memcpy(d, &b, 8);
#elif (__i386__ || __x86_64__) && (defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64)
/* extended -> extended */
memcpy(d, s, 10);
#elif (__i386__ || __x86_64__) && (defined TCC_TARGET_ARM64 || defined TCC_TARGET_RISCV64)
/* extended -> quad */
uint64_t m = *(uint64_t*)s;
int e = *(uint16_t*)((char*)s + 8);
write64le(d+6, m << 1);
write16le(d+14, e);
#elif (__aarch64__ || __riscv) && (defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64)
/* quad -> extended */
uint64_t m = read64le((char*)s + 6);
int e = read16le((char*)s + 14);
(e & 0x7fff) && (m & 1) && 0 == ++m && ++e;
write64le(d, m >> 1 | ((e & 0x7fff) ? 1ULL<<63 : 0));
write16le(d+8, e);
#else
/* unknown */
if (sizeof (long double) == LDOUBLE_SIZE)
memcpy(d, s, LDOUBLE_SIZE);
#endif
}
}
/* store a value or an expression directly in global data or in local array */
static void init_putv(init_params *p, CType *type, unsigned long c)
{
@ -7798,21 +7893,12 @@ static void init_putv(init_params *p, CType *type, unsigned long c)
ptr = sec->data + c;
val = vtop->c.i;
/* XXX: make code faster ? */
if ((vtop->r & (VT_SYM|VT_CONST)) == (VT_SYM|VT_CONST) &&
vtop->sym->v >= SYM_FIRST_ANOM &&
/* XXX This rejects compound literals like
'(void *){ptr}'. The problem is that '&sym' is
represented the same way, which would be ruled out
by the SYM_FIRST_ANOM check above, but also '"string"'
in 'char *p = "string"' is represented the same
with the type being VT_PTR and the symbol being an
anonymous one. That is, there's no difference in vtop
between '(void *){x}' and '&(void *){x}'. Ignore
pointer typed entities here. Hopefully no real code
will ever use compound literals with scalar type. */
(vtop->type.t & VT_BTYPE) != VT_PTR) {
/* These come from compound literals, memcpy stuff over. */
if ((vtop->r & (VT_SYM|VT_CONST)) == (VT_SYM|VT_CONST)
&& vtop->sym->v >= SYM_FIRST_ANOM
&& ((vtop->r & VT_LVAL) /* compound literal */
|| bt == VT_STRUCT /* designator */
)) {
/* memcpy stuff over. */
Section *ssec;
ElfSym *esym;
ElfW_Rel *rel;
@ -7879,34 +7965,7 @@ static void init_putv(init_params *p, CType *type, unsigned long c)
write64le(ptr, val);
break;
case VT_LDOUBLE:
#if defined TCC_IS_NATIVE_387
/* Host and target platform may be different but both have x87.
On windows, tcc does not use VT_LDOUBLE, except when it is a
cross compiler. In this case a mingw gcc as host compiler
comes here with 10-byte long doubles, while msvc or tcc won't.
tcc itself can still translate by asm.
In any case we avoid possibly random bytes 11 and 12.
*/
if (sizeof (long double) >= 10)
memcpy(ptr, &vtop->c.ld, 10);
#ifdef __TINYC__
else if (sizeof (long double) == sizeof (double))
__asm__("fldl %1\nfstpt %0\n" : "=m" (*ptr) : "m" (vtop->c.ld));
#endif
else
#endif
/* For other platforms it should work natively, but may not work
for cross compilers */
if (sizeof(long double) == LDOUBLE_SIZE)
memcpy(ptr, &vtop->c.ld, LDOUBLE_SIZE);
else if (sizeof(double) == LDOUBLE_SIZE)
*(double*)ptr = (double)vtop->c.ld;
else if (0 == memcmp(ptr, &vtop->c.ld, LDOUBLE_SIZE))
; /* nothing to do for 0.0 */
#ifndef TCC_CROSS_TEST
else
tcc_error("can't cross compile long double constants");
#endif
write_ldouble(ptr, &vtop->c.ld);
break;
#if PTR_SIZE == 8
@ -8064,6 +8123,8 @@ static void decl_initializer(init_params *p, CType *type, unsigned long c, int f
}
}
}
if (tok == ',' && !no_oblock) /* static const char s[] = { "123", }; */
next();
} else {
do_init_array:
@ -8336,7 +8397,12 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
CType *tp = type;
while ((tp->t & (VT_BTYPE|VT_ARRAY)) == (VT_PTR|VT_ARRAY))
tp = &tp->ref->type;
if (tp->t & VT_CONSTANT) {
if (type->t & VT_TLS) {
if (has_init)
sec = tdata_section;
else
sec = tbss_section;
} else if (tp->t & VT_CONSTANT) {
sec = rodata_section;
} else if (has_init) {
sec = data_section;
@ -8520,6 +8586,7 @@ static void gen_function(Sym *sym)
func_ind = ind;
func_vt = sym->type.ref->type;
func_var = sym->type.ref->f.func_type == FUNC_ELLIPSIS;
func_old = sym->type.ref->f.func_type == FUNC_OLD;
/* NOTE: we patch the symbol size later */
put_extern_sym(sym, cur_text_section, ind, 0);

View File

@ -19,6 +19,7 @@ int atoi(const char *nptr);
long int strtol(const char *nptr, char **endptr, int base);
unsigned long int strtoul(const char *nptr, char **endptr, int base);
void exit(int);
void *alloca(size_t);
/* stdio.h */
typedef struct __FILE FILE;
@ -39,6 +40,7 @@ int getchar(void);
char *gets(char *s);
int ungetc(int c, FILE *stream);
int fflush(FILE *stream);
int puts(const char *s);
int putchar (int c);
int printf(const char *format, ...);

View File

@ -702,7 +702,7 @@ static void check_relocs(TCCState *s1, struct macho *mo)
goti = tcc_realloc(goti, (mo->n_got + 1) * sizeof(*goti));
if (ELFW(ST_BIND)(sym->st_info) == STB_LOCAL) {
if (sym->st_shndx == SHN_UNDEF)
tcc_error("undefined local symbo: '%s'",
tcc_error("unresolved local reference to '%s'",
(char *) symtab_section->link->data + sym->st_name);
goti[mo->n_got++] = INDIRECT_SYMBOL_LOCAL;
} else {
@ -859,7 +859,7 @@ static void check_relocs(TCCState *s1, struct macho *mo)
goti = tcc_realloc(goti, (mo->n_got + 1) * sizeof(*goti));
if (ELFW(ST_BIND)(sym->st_info) == STB_LOCAL) {
if (sym->st_shndx == SHN_UNDEF)
tcc_error("undefined local symbo: '%s'",
tcc_error("unresolved local reference to '%s'",
(char *) symtab_section->link->data + sym->st_name);
goti[mo->n_got++] = INDIRECT_SYMBOL_LOCAL;
} else {
@ -1042,7 +1042,7 @@ static int check_symbols(TCCState *s1, struct macho *mo)
sym->st_shndx = SHN_FROMDLL;
continue;
}
tcc_error_noabort("undefined symbol '%s'", name);
tcc_error_noabort("unresolved reference to '%s'", name);
ret = -1;
}
}
@ -1058,6 +1058,7 @@ static void convert_symbol(TCCState *s1, struct macho *mo, struct nlist_64 *pn)
case STT_NOTYPE:
case STT_OBJECT:
case STT_FUNC:
case STT_TLS:
case STT_SECTION:
n.n_type = N_SECT;
break;

247
tccpe.c
View File

@ -27,6 +27,8 @@
#define stricmp strcasecmp
#define strnicmp strncasecmp
#include <sys/stat.h> /* chmod() */
#else
#include <process.h>
#endif
#ifdef TCC_TARGET_X86_64
@ -50,6 +52,16 @@
# define IMAGE_FILE_MACHINE 0x01C0
# define RSRC_RELTYPE 7 /* ??? (not tested) */
#elif defined TCC_TARGET_ARM64
# define ADDR3264 ULONGLONG
# define PE_IMAGE_REL IMAGE_REL_BASED_DIR64
# define REL_TYPE_DIRECT R_AARCH64_ABS64
# define R_XXX_THUNKFIX R_AARCH64_ABS64
# define R_XXX_RELATIVE R_AARCH64_RELATIVE
# define R_XXX_FUNCCALL R_AARCH64_CALL26
# define IMAGE_FILE_MACHINE 0xAA64
# define RSRC_RELTYPE 3
#elif defined TCC_TARGET_I386
# define ADDR3264 DWORD
# define PE_IMAGE_REL IMAGE_REL_BASED_HIGHLOW
@ -126,7 +138,7 @@ typedef struct _IMAGE_OPTIONAL_HEADER {
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
#ifndef TCC_TARGET_X86_64
#if !defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_ARM64)
DWORD BaseOfData;
#endif
/* NT additional fields. */
@ -225,6 +237,19 @@ typedef struct _IMAGE_BASE_RELOCATION {
#define IMAGE_SIZEOF_BASE_RELOCATION 8
#ifndef IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA
#define IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA PE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA
#endif
#ifndef IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE PE_DLLCHARACTERISTICS_DYNAMIC_BASE
#endif
#ifndef IMAGE_DLLCHARACTERISTICS_NX_COMPAT
#define IMAGE_DLLCHARACTERISTICS_NX_COMPAT PE_DLLCHARACTERISTICS_NX_COMPAT
#endif
#ifndef IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE PE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
#endif
#define IMAGE_REL_BASED_ABSOLUTE 0
#define IMAGE_REL_BASED_HIGH 1
#define IMAGE_REL_BASED_LOW 2
@ -250,6 +275,24 @@ typedef struct _IMAGE_BASE_RELOCATION {
#endif /* ndef IMAGE_NT_SIGNATURE */
/* ----------------------------------------------------------- */
static WORD pe_get_dll_characteristics(TCCState *s1)
{
unsigned v = 0;
#ifdef TCC_TARGET_ARM64
v = PE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA |
PE_DLLCHARACTERISTICS_DYNAMIC_BASE |
PE_DLLCHARACTERISTICS_NX_COMPAT |
PE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE;
#endif
v |= s1->pe_dll_characteristics;
v &= ~s1->pe_dll_characteristics_clear;
return v;
}
#ifndef IMAGE_FILE_MACHINE_ARM64
#define IMAGE_FILE_MACHINE_ARM64 0xAA64
#endif
#ifndef IMAGE_REL_BASED_DIR64
# define IMAGE_REL_BASED_DIR64 10
#endif
@ -261,7 +304,7 @@ struct pe_header
BYTE dosstub[0x40];
DWORD nt_sig;
IMAGE_FILE_HEADER filehdr;
#ifdef TCC_TARGET_X86_64
#if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
IMAGE_OPTIONAL_HEADER64 opthdr;
#else
#ifdef _WIN64
@ -537,17 +580,55 @@ static void pe_add_coffsym(struct pe_info *pe)
/* Run cv2pdb, available at https://github.com/rainers/cv2pdb. It reads
and strips the dwarf info and creates a <exename>.pdb file instead */
#ifndef _WIN32
static void pe_shell_quote(CString *cmd, const char *arg)
{
cstr_cat(cmd, "'", 1);
while (*arg) {
if (*arg == '\'')
cstr_cat(cmd, "'\\''", 4);
else
cstr_cat(cmd, arg, 1);
++arg;
}
cstr_cat(cmd, "'", 1);
}
#endif
static intptr_t pe_run_cv2pdb(const char *exename)
{
#ifdef _WIN32
const char *argv[] = { "cv2pdb.exe", exename, NULL };
return _spawnvp(_P_WAIT, "cv2pdb.exe", argv);
#else
CString cmd;
intptr_t ret;
cstr_new(&cmd);
cstr_cat(&cmd, "cv2pdb.exe ", -1);
pe_shell_quote(&cmd, exename);
cstr_ccat(&cmd, 0);
ret = system(cmd.data);
cstr_free(&cmd);
return ret;
#endif
}
static void pe_create_pdb(TCCState *s1, const char *exename)
{
char buf[300]; int r;
snprintf(buf, sizeof buf, "cv2pdb.exe %s", exename);
r = system(buf);
strcpy(tcc_fileextension(strcpy(buf, exename)), ".pdb");
size_t len = strlen(exename);
char *pdbfile = tcc_malloc(len + sizeof(".pdb"));
intptr_t r;
strcpy(pdbfile, exename);
strcpy(tcc_fileextension(pdbfile), ".pdb");
r = pe_run_cv2pdb(exename);
if (r) {
tcc_error_noabort("could not create '%s'\n(need working cv2pdb from https://github.com/rainers/cv2pdb)", buf);
tcc_error_noabort("could not create '%s'\n(need working cv2pdb from https://github.com/rainers/cv2pdb)", pdbfile);
} else if (s1->verbose) {
printf("<- %s\n", buf);
printf("<- %s\n", pdbfile);
}
tcc_free(pdbfile);
}
/*----------------------------------------------------------------------------*/
@ -605,11 +686,15 @@ static int pe_write(struct pe_info *pe)
0x00E0, /*WORD SizeOfOptionalHeader; */
0x010F, /*WORD Characteristics; */
#define CHARACTERISTICS_DLL 0x230F
#elif defined(TCC_TARGET_ARM64)
0x00F0, /*WORD SizeOfOptionalHeader; */
0x0022 /*WORD Characteristics; */
#define CHARACTERISTICS_DLL 0x2022
#endif
},{
/* IMAGE_OPTIONAL_HEADER opthdr */
/* Standard fields. */
#ifdef TCC_TARGET_X86_64
#if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
0x020B, /*WORD Magic; */
#else
0x010B, /*WORD Magic; */
@ -621,23 +706,35 @@ static int pe_write(struct pe_info *pe)
0x00000000, /*DWORD SizeOfUninitializedData; */
0x00000000, /*DWORD AddressOfEntryPoint; */
0x00000000, /*DWORD BaseOfCode; */
#ifndef TCC_TARGET_X86_64
#if !defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_ARM64)
0x00000000, /*DWORD BaseOfData; */
#endif
/* NT additional fields. */
#if defined(TCC_TARGET_ARM)
0x00100000, /*DWORD ImageBase; */
#elif defined(TCC_TARGET_ARM64)
0x140000000ULL, /*ULONGLONG ImageBase; */
#else
0x00400000, /*DWORD ImageBase; */
#endif
0x00001000, /*DWORD SectionAlignment; */
0x00000200, /*DWORD FileAlignment; */
#if defined(TCC_TARGET_ARM64)
0x0006, /*WORD MajorOperatingSystemVersion; */
0x0002, /*WORD MinorOperatingSystemVersion; */
#else
0x0004, /*WORD MajorOperatingSystemVersion; */
0x0000, /*WORD MinorOperatingSystemVersion; */
#endif
0x0000, /*WORD MajorImageVersion; */
0x0000, /*WORD MinorImageVersion; */
#if defined(TCC_TARGET_ARM64)
0x0006, /*WORD MajorSubsystemVersion; */
0x0002, /*WORD MinorSubsystemVersion; */
#else
0x0004, /*WORD MajorSubsystemVersion; */
0x0000, /*WORD MinorSubsystemVersion; */
#endif
0x00000000, /*DWORD Win32VersionValue; */
0x00000000, /*DWORD SizeOfImage; */
0x00000200, /*DWORD SizeOfHeaders; */
@ -702,7 +799,7 @@ static int pe_write(struct pe_info *pe)
break;
case sec_data:
#ifndef TCC_TARGET_X86_64
#if !defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_ARM64)
if (!pe_header.opthdr.BaseOfData)
pe_header.opthdr.BaseOfData = addr;
#endif
@ -764,11 +861,14 @@ static int pe_write(struct pe_info *pe)
pe_header.opthdr.SizeOfHeaders = pe->sizeofheaders;
pe_header.opthdr.ImageBase = pe->imagebase;
pe_header.opthdr.Subsystem = pe->subsystem;
pe_header.opthdr.DllCharacteristics = pe_get_dll_characteristics(s1);
if (s1->pe_stack_size)
pe_header.opthdr.SizeOfStackReserve = s1->pe_stack_size;
if (PE_DLL == pe->type)
pe_header.filehdr.Characteristics = CHARACTERISTICS_DLL;
pe_header.filehdr.Characteristics |= s1->pe_characteristics;
if (pe->reloc)
pe_header.filehdr.Characteristics &= ~PE_IMAGE_FILE_RELOCS_STRIPPED;
if (pe->coffsym) {
pe_add_coffsym(pe);
@ -1191,7 +1291,8 @@ static int pe_assign_addresses (struct pe_info *pe)
Section *s;
TCCState *s1 = pe->s1;
if (PE_DLL == pe->type)
if (PE_DLL == pe->type ||
(pe_get_dll_characteristics(s1) & PE_DLLCHARACTERISTICS_DYNAMIC_BASE))
pe->reloc = new_section(s1, ".reloc", SHT_PROGBITS, 0);
//pe->thunk = new_section(s1, ".iedat", SHT_PROGBITS, SHF_ALLOC);
@ -1380,6 +1481,18 @@ static int pe_check_symbols(struct pe_info *pe)
write32le(p + 4, 0xE59CF000); // arm code ldr pc, [ip]
put_elf_reloc(symtab_section, text_section,
offset + 8, R_XXX_THUNKFIX, is->iat_index); // offset to IAT position
#elif defined(TCC_TARGET_ARM64)
p = section_ptr_add(text_section, 24);
/* ldr x16, [pc, #16] */
write32le(p + 0, 0x58000090);
/* ldr x16, [x16] */
write32le(p + 4, 0xf9400210);
/* br x16 */
write32le(p + 8, 0xd61f0200);
/* nop for 8-byte literal alignment */
write32le(p + 12, 0xd503201f);
put_elf_reloc(symtab_section, text_section,
offset + 16, R_XXX_THUNKFIX, is->iat_index);
#else
p = section_ptr_add(text_section, 8);
write16le(p, 0x25FF);
@ -1611,7 +1724,7 @@ static int get_dllexports(int fd, char **pp)
if (IMAGE_DIRECTORY_ENTRY_EXPORT >= oh.NumberOfRvaAndSizes)
goto the_end_0;
addr = oh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
} else if (ih.Machine == 0x8664) {
} else if (ih.Machine == 0x8664 || ih.Machine == IMAGE_FILE_MACHINE_ARM64) {
IMAGE_OPTIONAL_HEADER64 oh;
sec_hdroffset = opt_hdroffset + sizeof oh;
if (!read_mem(fd, opt_hdroffset, &oh, sizeof oh))
@ -1834,7 +1947,7 @@ PUB_FUNC int tcc_get_dllexports(const char *filename, char **pp)
/* ------------------------------------------------------------- */
#ifdef TCC_TARGET_X86_64
static unsigned pe_add_uwwind_info(TCCState *s1)
static unsigned pe_add_unwind_info(TCCState *s1)
{
if (NULL == s1->uw_pdata) {
s1->uw_pdata = find_section(s1, ".pdata");
@ -1879,7 +1992,7 @@ ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack)
DWORD UnwindData;
} *p;
d = pe_add_uwwind_info(s1);
d = pe_add_unwind_info(s1);
pd = s1->uw_pdata;
o = pd->data_offset;
p = section_ptr_add(pd, sizeof *p);
@ -1893,9 +2006,85 @@ ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack)
for (n = o + sizeof *p; o < n; o += sizeof p->BeginAddress)
put_elf_reloc(symtab_section, pd, o, R_XXX_RELATIVE, s1->uw_sym);
}
#elif defined(TCC_TARGET_ARM64)
/* ARM64 unwind codes:
save_fplr_x: 10iiiiii - stp x29,lr,[sp,#-(i+1)*8]!
set_fp: 11100001 - mov x29,sp
alloc_s: 000iiiii - sub sp,sp,#i*16 (up to 496 bytes)
alloc_m: 11000iii xxxxxxxx - sub sp,sp,#X*16 (up to 32KB)
end: 11100100 - end of unwind codes
*/
static Section *pe_add_unwind_info(TCCState *s1)
{
Section *s;
if (NULL == s1->uw_pdata) {
s1->uw_pdata = find_section(s1, ".pdata");
s1->uw_pdata->sh_addralign = 4;
}
s = find_section(s1, ".xdata");
s->sh_addralign = 4;
if (0 == s1->uw_sym)
s1->uw_sym = put_elf_sym(symtab_section, 0, 0, 0, 0,
text_section->sh_num, ".uw_text_base");
if (0 == s1->uw_xsym)
s1->uw_xsym = put_elf_sym(symtab_section, 0, 0, 0, 0,
s->sh_num, ".uw_base");
return s;
}
ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack)
{
TCCState *s1 = tcc_state;
Section *pd, *xd;
unsigned o, d, code_bytes, func_len;
unsigned char *q;
uint32_t header;
struct /* _RUNTIME_FUNCTION */ {
DWORD BeginAddress;
DWORD UnwindData;
} *p;
int epilog;
xd = pe_add_unwind_info(s1);
pd = s1->uw_pdata;
func_len = (end - start) >> 2;
code_bytes = 0;
epilog = code_bytes;
code_bytes += 3; /* set_fp, save_fplr_x, end */
code_bytes = (code_bytes + 3) & ~3;
section_ptr_add(xd, -xd->data_offset & 3);
d = xd->data_offset;
q = section_ptr_add(xd, 4 + code_bytes);
/* Full ARM64 xdata header: E=1 with one epilog and no exception handler. */
header = (func_len & 0x3ffff)
| 1 << 21
| (epilog & 0x1F) << 22
| (code_bytes >> 2) << 27
;
write32le(q, header);
q += 4;
*q++ = 0xE1; /* set_fp */
*q++ = 0x9B; /* save_fplr_x: stp x29,lr,[sp,#-224]! */
*q++ = 0xE4; /* end */
while ((unsigned)(q - (xd->data + d + 4)) < code_bytes)
*q++ = 0xE3; /* nop padding */
o = pd->data_offset;
p = section_ptr_add(pd, sizeof *p);
p->BeginAddress = start;
p->UnwindData = d;
put_elf_reloc(symtab_section, pd, o, R_XXX_RELATIVE, s1->uw_sym);
put_elf_reloc(symtab_section, pd, o + 4, R_XXX_RELATIVE, s1->uw_xsym);
}
#endif
/* ------------------------------------------------------------- */
#ifdef TCC_TARGET_X86_64
#if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
#define PE_STDSYM(n,s) n
#else
#define PE_STDSYM(n,s) "_" n s
@ -1991,7 +2180,7 @@ static void pe_add_runtime(TCCState *s1, struct pe_info *pe)
ST_FUNC int pe_setsubsy(TCCState *s1, const char *arg)
{
static const struct subsy { const char* p; int v; } x[] = {
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
{ "native", 1 },
{ "gui", 2 },
{ "windows", 2 },
@ -2020,10 +2209,16 @@ static void pe_set_options(TCCState * s1, struct pe_info *pe)
{
if (PE_DLL == pe->type) {
/* XXX: check if is correct for arm-pe target */
#if defined(TCC_TARGET_ARM64)
pe->imagebase = 0x180000000ULL;
#else
pe->imagebase = 0x10000000;
#endif
} else {
#if defined(TCC_TARGET_ARM)
pe->imagebase = 0x00010000;
#elif defined(TCC_TARGET_ARM64)
pe->imagebase = 0x140000000ULL;
#else
pe->imagebase = 0x00400000;
#endif
@ -2080,29 +2275,33 @@ ST_FUNC int pe_output_file(TCCState *s1, const char *filename)
resolve_common_syms(s1);
pe_set_options(s1, &pe);
pe_check_symbols(&pe);
if (s1->nb_errors)
;
else if (filename) {
goto done;
if (filename) {
pe_assign_addresses(&pe);
relocate_syms(s1, s1->symtab, 0);
if (s1->nb_errors)
goto done;
s1->pe_imagebase = pe.imagebase;
relocate_sections(s1);
pe.start_addr = (DWORD)
(get_sym_addr(s1, pe.start_symbol, 1, 1) - pe.imagebase);
if (0 == s1->nb_errors)
pe_write(&pe);
dynarray_reset(&pe.sec_info, &pe.sec_count);
if (s1->nb_errors)
goto done;
pe_write(&pe);
} else {
/* -run */
#ifdef TCC_IS_NATIVE
pe.thunk = data_section;
pe_build_imports(&pe);
s1->run_main = pe.start_symbol;
#ifdef TCC_TARGET_X86_64
#if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64)
s1->uw_pdata = find_section(s1, ".pdata");
#endif
#endif
}
done:
dynarray_reset(&pe.sec_info, &pe.sec_count);
pe_free_imports(&pe);
#if PE_PRINT_SECTIONS
if (g_debug & 8)

95
tccpp.c
View File

@ -942,11 +942,11 @@ redo_start:
else if (parse_flags & PARSE_FLAG_ASM_FILE)
p = parse_line_comment(p - 1);
}
#if !defined(TCC_TARGET_ARM)
#if !defined(TCC_TARGET_ARM) && !defined(TCC_TARGET_ARM64)
else if (parse_flags & PARSE_FLAG_ASM_FILE)
p = parse_line_comment(p - 1);
#else
/* ARM assembly uses '#' for constants */
/* ARM/ARM64 assembly uses '#' for constants */
#endif
break;
_default:
@ -987,11 +987,7 @@ static inline int tok_size(const int *p)
case TOK_CULLONG:
return 1 + 2;
case TOK_CLDOUBLE:
#ifdef TCC_USING_DOUBLE_FOR_LDOUBLE
return 1 + 8 / 4;
#else
return 1 + LDOUBLE_SIZE / 4;
#endif
return 1 + LDOUBLE_WORDS;
default:
return 1 + 0;
}
@ -1130,22 +1126,12 @@ static void tok_str_add2(TokenString *s, int t, CValue *cv)
str[len++] = cv->tab[1];
break;
case TOK_CLDOUBLE:
#if LDOUBLE_SIZE == 8 || defined TCC_USING_DOUBLE_FOR_LDOUBLE
str[len++] = cv->tab[0];
str[len++] = cv->tab[1];
#elif LDOUBLE_SIZE == 12
str[len++] = cv->tab[0];
str[len++] = cv->tab[1];
str[len++] = cv->tab[2];
#elif LDOUBLE_SIZE == 16
str[len++] = cv->tab[0];
str[len++] = cv->tab[1];
if (LDOUBLE_WORDS >= 3)
str[len++] = cv->tab[2];
if (LDOUBLE_WORDS >= 4)
str[len++] = cv->tab[3];
#else
#error add long double size support
#endif
break;
default:
break;
}
@ -1219,15 +1205,7 @@ static inline void tok_get(int *t, const int **pp, CValue *cv)
n = 2;
goto copy;
case TOK_CLDOUBLE:
#if LDOUBLE_SIZE == 8 || defined TCC_USING_DOUBLE_FOR_LDOUBLE
n = 2;
#elif LDOUBLE_SIZE == 12
n = 3;
#elif LDOUBLE_SIZE == 16
n = 4;
#else
# error add long double size support
#endif
n = LDOUBLE_WORDS;
copy:
do
*tab++ = *p++;
@ -2234,13 +2212,8 @@ static void parse_string(const char *s, int len)
}
}
#ifdef TCC_USING_DOUBLE_FOR_LDOUBLE
/* we use 64 bit (52 needed) numbers */
#define BN_SIZE 2
#else
/* we use 128 bit (64/112 needed) numbers */
#define BN_SIZE 4
#endif
/* bn = (bn << shift) | or_val */
static int bn_lshift(unsigned int *bn, int shift, int or_val)
@ -2272,11 +2245,7 @@ static void parse_number(const char *p)
int b, t, shift, frac_bits, s, exp_val, ch;
char *q;
unsigned int bn[BN_SIZE];
#ifdef TCC_USING_DOUBLE_FOR_LDOUBLE
double d;
#else
long double d;
#endif
/* number */
q = token_buf;
@ -2388,19 +2357,14 @@ static void parse_number(const char *p)
ch = *p++;
}
exp_val = exp_val * s;
/* now we can generate the number */
/* XXX: should patch directly float number */
#ifdef TCC_USING_DOUBLE_FOR_LDOUBLE
d = (double)bn[1] * 4294967296.0 + (double)bn[0];
d = ldexp(d, exp_val - frac_bits);
#else
d = (long double)bn[3] * 79228162514264337593543950336.0L +
(long double)bn[2] * 18446744073709551616.0L +
(long double)bn[1] * 4294967296.0L +
(long double)bn[0];
d = ldexpl(d, exp_val - frac_bits);
#endif
t = toup(ch);
if (t == 'F') {
ch = *p++;
@ -2410,11 +2374,7 @@ static void parse_number(const char *p)
} else if (t == 'L') {
ch = *p++;
tok = TOK_CLDOUBLE;
#ifdef TCC_USING_DOUBLE_FOR_LDOUBLE
tokc.d = d;
#else
tokc.ld = d;
#endif
} else {
tok = TOK_CDOUBLE;
tokc.d = (double)d;
@ -2464,18 +2424,14 @@ static void parse_number(const char *p)
} else if (t == 'L') {
ch = *p++;
tok = TOK_CLDOUBLE;
#ifdef TCC_USING_DOUBLE_FOR_LDOUBLE
tokc.d = strtod(token_buf, NULL);
#else
tokc.ld = strtold(token_buf, NULL);
#endif
} else {
tok = TOK_CDOUBLE;
tokc.d = strtod(token_buf, NULL);
}
}
} else {
unsigned long long n = 0, n1 = 0;
unsigned long long n, n1;
int lcount, ucount, ov = 0;
const char *p1;
@ -2486,6 +2442,7 @@ static void parse_number(const char *p)
b = 8;
q++;
}
n = 0;
while(1) {
t = *q++;
/* no need for checks except for base 10 / 8 errors */
@ -2499,14 +2456,11 @@ static void parse_number(const char *p)
t = t - '0';
if (t >= b)
tcc_error("invalid digit");
n1 = n;
n = n * b + t;
if (!ov) {
/* detect overflow */
if (n1 >= 0x1000000000000000ULL && n / b != n1)
ov = 1;
else
n1 = n;
}
/* detect overflow */
if (n1 >= 0x1000000000000000ULL && n / b != n1)
ov = 1;
}
/* Determine the characteristics (unsigned and/or 64bit) the type of
@ -2556,26 +2510,7 @@ static void parse_number(const char *p)
}
if (ov)
/* Give a warning with values in case of an overflow. This helps to
spot the 0 too much in 0x8000'0000'0000'0000'0. It may even be
better to use a 0x8000'0000'0000'0000 from n1 instead of a 0 from
n after the overflow. This is at least undefined behavior.
The C99 to C23 standards state:
"Each constant shall have a type and the value of a constant
shall be in the range of representable values for its type."
"If an integer constant cannot be represented by any type ...,
then the integer constant has no type."
The C++ standards state:
"A program is ill-formed if one of its translation units contains
an integer-literal that cannot be represented by any of the
allowed types." */
tcc_warning(
b == 8
? "integer constant overflow, using %#llo; did you mean %#llo?"
: b == 10
? "integer constant overflow, using %llu, did you mean %llu?"
: "integer constant overflow, using %#llx; did you mean %#llx?",
n, n1);
tcc_warning("integer constant overflow");
tok = TOK_CINT;
if (lcount) {
@ -2699,7 +2634,7 @@ maybe_newline:
p++;
tok = TOK_TWOSHARPS;
} else {
#if !defined(TCC_TARGET_ARM)
#if !defined(TCC_TARGET_ARM) && !defined(TCC_TARGET_ARM64)
if (parse_flags & PARSE_FLAG_ASM_FILE) {
p = parse_line_comment(p - 1);
goto redo_no_start;

View File

@ -130,8 +130,15 @@ static int rt_mem(TCCState *s1, int size)
//printf("map %p %p %p\n", ptr, prw, (void*)ptr_diff);
size *= 2;
#else
# ifdef _WIN32
/* Generated code is page-protected below; avoid changing CRT heap pages. */
ptr = VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
# else
ptr = tcc_malloc(size += PAGESIZE); /* one extra page to align malloc memory */
# endif
#endif
if (!ptr)
return tcc_error_noabort("tccrun: could not allocate memory");
s1->run_ptr = ptr;
s1->run_size = size;
return ptr_diff;
@ -188,12 +195,20 @@ ST_FUNC void tcc_run_free(TCCState *s1)
#ifdef CONFIG_SELINUX
munmap(ptr, size);
#else
# ifdef _WIN32
(void)size;
# ifdef _WIN64
win64_del_function_table(s1->run_function_table);
# endif
VirtualFree(ptr, 0, MEM_RELEASE);
# else
/* unprotect memory to make it usable for malloc again */
protect_pages((void*)PAGEALIGN(ptr), size - PAGESIZE, 2 /*rw*/);
# ifdef _WIN64
win64_del_function_table(s1->run_function_table);
# endif
tcc_free(ptr);
# endif
#endif
}
@ -287,7 +302,7 @@ static void cleanup_sections(TCCState *s1)
do {
for (i = --f; i < p->nb_secs; i++) {
Section *s = p->secs[i];
if (s1->do_debug || s == s1->symtab || s == s1->symtab->link || s == s1->symtab->hash) {
if (s == s1->symtab || s == s1->symtab->link || s == s1->symtab->hash) {
s->data = tcc_realloc(s->data, s->data_allocated = s->data_offset);
} else {
free_section(s), tcc_free(s), p->secs[i] = NULL;
@ -299,11 +314,10 @@ static void cleanup_sections(TCCState *s1)
/* ------------------------------------------------------------- */
/* 0 = .text rwx other rw (memory >= 2 pages a 4096 bytes) */
/* 1 = .text rx other rw (memory >= 3 pages) */
/* 2 = .debug .debug ro (optional) */
/* 3 = .text rx .rdata ro .data/.bss rw (memory >= 4 pages) */
/* 2 = .text rx .rdata ro .data/.bss rw (memory >= 4 pages) */
/* Some targets implement secutiry options that do not allow write in
executable code. These targets need CONFIG_RUNMEM_RO=2.
executable code. These targets need CONFIG_RUNMEM_RO=1.
The disadvantage of this is that it requires a little bit more memory. */
#ifndef CONFIG_RUNMEM_RO
@ -344,13 +358,12 @@ redo:
if (copy == 3)
return 0;
for (k = 0; k < 4; ++k) { /* 0:rx, 1:ro, 2:ro debug , 3:rw sections */
for (k = 0; k < 3; ++k) { /* 0:rx, 1:ro, 2:rw sections */
n = 0; addr = 0;
for(i = 1; i < s1->nb_sections; i++) {
static const char shf[] = {
SHF_ALLOC|SHF_EXECINSTR, SHF_ALLOC, 0, SHF_ALLOC|SHF_WRITE
SHF_ALLOC|SHF_EXECINSTR, SHF_ALLOC, SHF_ALLOC|SHF_WRITE
};
if (k == 2 && s1->do_debug == 0) continue;
s = s1->sections[i];
if (shf[k] != (s->sh_flags & (SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR)))
continue;
@ -414,7 +427,11 @@ redo:
}
if (protect_pages((void*)addr, n, f) < 0)
return tcc_error_noabort(
#ifdef _WIN32
"VirtualProtect failed");
#else
"mprotect failed (did you mean to configure --with-selinux?)");
#endif
}
}
@ -436,6 +453,8 @@ redo:
/* relocate symbols */
relocate_syms(s1, s1->symtab, 1);
if (s1->nb_errors)
goto redo;
/* relocate sections */
#ifdef TCC_TARGET_PE
s1->pe_imagebase = mem;
@ -471,7 +490,7 @@ static int protect_pages(void *ptr, unsigned long length, int mode)
if (mprotect(ptr, length, protect[mode]))
return -1;
/* XXX: BSD sometimes dump core with bad system call */
# if (defined TCC_TARGET_ARM && !TARGETOS_BSD) || defined TCC_TARGET_ARM64
# if (defined TCC_TARGET_ARM && !TARGETOS_BSD) || defined TCC_TARGET_ARM64 || defined TCC_TARGET_RISCV64
if (mode == 0 || mode == 3) {
void __clear_cache(void *beginning, void *end);
__clear_cache(ptr, (char *)ptr + length);
@ -487,11 +506,14 @@ static void *win64_add_function_table(TCCState *s1)
void *p = NULL;
if (s1->uw_pdata) {
p = (void*)s1->uw_pdata->sh_addr;
RtlAddFunctionTable(
if (!RtlAddFunctionTable(
(RUNTIME_FUNCTION*)p,
s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
s1->pe_imagebase
);
)) {
tcc_error_noabort("RtlAddFunctionTable failed");
p = NULL;
}
s1->uw_pdata = NULL;
}
return p;
@ -1201,7 +1223,11 @@ static int rt_error(rt_frame *f, const char *fmt, ...)
/* translate from ucontext_t* to internal rt_context * */
static void rt_getcontext(ucontext_t *uc, rt_frame *rc)
{
#if defined _WIN64
#if defined _WIN64 && defined __aarch64__
rc->ip = uc->Pc; /* Program Counter */
rc->fp = uc->Fp; /* Frame Pointer (X29) */
rc->sp = uc->Sp; /* Stack Pointer (X30 is LR, but SP is separate) */
#elif defined _WIN64
rc->ip = uc->Rip;
rc->fp = uc->Rbp;
rc->sp = uc->Rsp;
@ -1400,7 +1426,11 @@ static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
/* Generate a stack backtrace when a CPU exception occurs. */
static void set_exception_handler(void)
{
#ifdef _WIN64
AddVectoredExceptionHandler(1, cpu_exception_handler);
#else
SetUnhandledExceptionFilter(cpu_exception_handler);
#endif
}
#endif

View File

@ -39,6 +39,7 @@
DEF(TOK_RESTRICT3, "__restrict__")
DEF(TOK_EXTENSION, "__extension__") /* gcc keyword */
DEF(TOK_THREAD_LOCAL, "_Thread_local") /* C11 thread-local storage */
DEF(TOK___thread, "__thread") /* GCC thread-local storage extension */
DEF(TOK_GENERIC, "_Generic")
DEF(TOK_STATIC_ASSERT, "_Static_assert")
@ -306,8 +307,13 @@
#if defined TCC_TARGET_PE
DEF(TOK___chkstk, "__chkstk")
#endif
#if defined TCC_TARGET_ARM64 || defined TCC_TARGET_RISCV64
#ifdef TCC_TARGET_ARM64
DEF(TOK___arm64_clear_cache, "__arm64_clear_cache")
#endif
#ifdef TCC_TARGET_RISCV64
DEF(TOK___riscv64_clear_cache, "__riscv64_clear_cache")
#endif
#if defined TCC_TARGET_ARM64 || defined TCC_TARGET_RISCV64
DEF(TOK___addtf3, "__addtf3")
DEF(TOK___subtf3, "__subtf3")
DEF(TOK___multf3, "__multf3")
@ -402,12 +408,13 @@
DEF_ASMDIR(endr)
DEF_ASMDIR(org)
DEF_ASMDIR(quad)
#if defined(TCC_TARGET_I386)
#if PTR_SIZE == 4
DEF_ASMDIR(code16)
DEF_ASMDIR(code32)
#elif defined(TCC_TARGET_X86_64)
#else
DEF_ASMDIR(code64)
#elif defined(TCC_TARGET_RISCV64)
#endif
#if defined(TCC_TARGET_RISCV64)
DEF_ASMDIR(option)
#endif
DEF_ASMDIR(short)
@ -421,10 +428,14 @@
#include "i386-tok.h"
#endif
#if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
#if defined TCC_TARGET_ARM
#include "arm-tok.h"
#endif
#if defined TCC_TARGET_ARM64
#include "arm64-tok.h"
#endif
#if defined TCC_TARGET_RISCV64
#include "riscv64-tok.h"
#endif

View File

@ -13,7 +13,6 @@ TESTS = \
hello-run \
libtest \
libtest_mt \
libtest_xor_rex \
test3 \
abitest \
asm-c-connect-test \
@ -41,9 +40,6 @@ endif
ifeq (,$(filter i386 x86_64,$(ARCH)))
TESTS := $(filter-out asm-c-connect-test,$(TESTS))
endif
ifeq (,$(filter x86_64,$(ARCH)))
TESTS := $(filter-out libtest_xor_rex,$(TESTS))
endif
ifeq ($(OS),Windows_NT) # for libtcc_test to find libtcc.dll
PATH := $(CURDIR)/$(TOP)$(if $(findstring ;,$(PATH)),;,:)$(PATH)
endif
@ -76,9 +72,9 @@ endif
RUN_TCC = -run $(TOPSRC)/tcc.c $(TCCFLAGS)
DISAS = objdump -d
ifdef CONFIG_OSX
DUMPTCC = (set -x; $(TOP)/tcc -vv; otool -L $(TOP)/tcc; exit 1)
DUMPTCC = (set -x; $(TCC_LOCAL) -vv; otool -L $(TCC_LOCAL); exit 1)
else
DUMPTCC = (set -x; $(TOP)/tcc -vv; ldd $(TOP)/tcc; exit 1)
DUMPTCC = (set -x; $(TCC_LOCAL) -vv; ldd $(TCC_LOCAL); exit 1)
endif
all test :
@ -86,6 +82,7 @@ all test :
@$(TCC_LOCAL) -v
@$(MAKE) --no-print-directory -s clean
@$(MAKE) --no-print-directory -s -r _all
@echo ------- ALL TESTS PASSED --------
_all : $(TESTS)
@ -107,17 +104,14 @@ libtcc_test$(EXESUF): libtcc_test.c
libtcc_test_mt$(EXESUF): libtcc_test_mt.c
$(CC) -o $@ $< $(CFLAGS) $(-LTCC) $(LIBS)
libtcc_test_xor_rex$(EXESUF): libtcc_test_xor_rex.c
$(CC) -o $@ $< $(CFLAGS) $(-LTCC) $(LIBS)
%-dir:
@echo ------------ $@ ------------
$(MAKE) -k -C $*
# test.ref - generate using cc
test.ref: tcctest.c
$(CC) -o tcctest.gcc $< $(CFLAGS) -w -O0 -std=gnu99 -fno-omit-frame-pointer
./tcctest.gcc > $@
$(CC) -o tcctest.gcc$(EXESUF) $< $(CFLAGS) -w -O0 -std=gnu99 -fno-omit-frame-pointer
./tcctest.gcc$(EXESUF) > $@
# auto test
test1 test1b: tcctest.c test.ref
@ -316,6 +310,7 @@ CROSS-TGTS = \
arm-NetBSD \
arm-wince \
arm64 \
arm64-win32 \
arm64-osx \
arm64-FreeBSD \
arm64-NetBSD \

View File

@ -1,58 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libtcc.h"
static const char program[] =
"#include <stdio.h>\n"
"int fib(int n)\n"
"{\n"
" if (n <= 2)\n"
" return 1;\n"
" else\n"
" return fib(n-1) + fib(n-2);\n"
"}\n"
"int tst(void)\n"
"{\n"
" int i;\n"
" for (i = 2; i < 20; i++)\n"
" printf(\"%d \", fib(i));\n"
" printf(\"\\n\");\n"
" return 0;\n"
"}\n";
void handle_error(void *opaque, const char *msg)
{
fprintf(opaque, "%s\n", msg);
}
int
main(void)
{
int (*func)(void);
TCCState *s = tcc_new();
if (!s) {
fprintf(stderr, __FILE__ ": could not create tcc state\n");
return 1;
}
#if 1
/* If -g option is not set the debugging files tst.c en tst.o will
not be created. */
tcc_set_options(s, "-g");
#endif
tcc_set_error_func(s, stdout, handle_error);
tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
if (tcc_compile_string_file(s, program, "tst.c") == -1)
return 1;
if (tcc_relocate(s) < 0)
return 1;
elf_output_obj(s, "tst.o");
/* set breakpoint on next line. and load symbol file with
gdb command add-symbol-file.
Then set breakpoint on tst and continue. */
if ((func = tcc_get_symbol(s, "tst")))
func();
tcc_delete(s);
return 0;
}

View File

@ -162,6 +162,7 @@ void *reloc_state(TCCState *s, const char *entry)
{
void *func;
tcc_add_symbol(s, "add", add);
tcc_add_symbol(s, "printf", printf);
if (tcc_relocate(s) < 0) {
fprintf(stderr, __FILE__ ": could not relocate tcc state.\n");
return NULL;
@ -337,8 +338,16 @@ int main(int argc, char **argv)
#else
#include <tcclib.h>
unsigned int sleep(unsigned int seconds);
#ifdef _WIN32
# ifndef _WIN64
__declspec(stdcall)
# endif
void Sleep(unsigned);
# define sleep_ms Sleep
#else
int usleep(unsigned long);
# define sleep_ms(x) usleep((x)*1000);
#endif
int fib(n)
{
@ -347,7 +356,7 @@ int fib(n)
int main(int argc, char **argv)
{
sleep(1);
sleep_ms(333);
printf(" %d", fib(atoi(argv[1])));
return 0;
}

View File

@ -1,128 +0,0 @@
/*
* Test for x86_64 xor REX prefix bug in load() -- x86_64-gen.c
*
* Bug: when loading a 64-bit zero constant into registers r8-r15,
* load() emits:
*
* o(0xc031 + REG_VALUE(r) * 0x900); // xor r, r
*
* REG_VALUE(r) masks to (r & 7), losing bit 3, and no orex() call
* emits the REX prefix needed for extended registers.
*
* Result: r=TREG_R11(11) -> REG_VALUE=3 -> emits 31 db (xor ebx,ebx)
* Correct: should emit 45 31 db (REX.RB xor r11d,r11d)
*
* Fix:
* orex(0, r, r, 0x31);
* o(0xc0 + REG_VALUE(r) * 9);
*
* Trigger: an indirect call through a compile-time null function pointer,
* e.g. ((void(*)(void))0)(), causes gcall_or_jmp() to fall into the
* "indirect call" path which does load(TREG_R11, <constant 0>).
*
* This test compiles such code via the libtcc API, then inspects the
* generated machine code for the incorrect encoding.
*/
#if !defined __x86_64__
#include <stdio.h>
int main(void) { printf("SKIP (x86_64 only)\n"); return 0; }
#else
#include <stdio.h>
#include "libtcc.h"
static void handle_error(void *opaque, const char *msg)
{
fprintf(opaque, "%s\n", msg);
}
/*
* Compiled via libtcc. The cast-to-null indirect call forces
* gcall_or_jmp() into its "else" branch (no VT_SYM, so the
* condition on line ~650 fails), which does:
*
* r = TREG_R11;
* load(r, vtop); // <-- buggy xor lands here
* o(0x41); o(0xff); // call/jmp *r
* o(0xd0 + REG_VALUE(r)); // r11 -> 0xd3
*
* We never execute the function (it would crash); we only
* inspect the generated bytes.
*/
static const char test_code[] =
"void test(void) {\n"
" ((void(*)(void))0)();\n"
"}\n";
int main(int argc, char **argv)
{
TCCState *s;
unsigned char *code;
int i;
int ret = 0;
s = tcc_new();
if (!s) {
fprintf(stderr, "tcc_new() failed\n");
return 2;
}
tcc_set_error_func(s, stderr, handle_error);
for (i = 1; i < argc; ++i) {
char *a = argv[i];
if (a[0] == '-') {
if (a[1] == 'B')
tcc_set_lib_path(s, a + 2);
else if (a[1] == 'I')
tcc_add_include_path(s, a + 2);
else if (a[1] == 'L')
tcc_add_library_path(s, a + 2);
}
}
tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
if (tcc_compile_string(s, test_code) == -1)
return 2;
if (tcc_relocate(s) < 0)
return 2;
code = (unsigned char *)tcc_get_symbol(s, "test");
if (!code) {
fprintf(stderr, "symbol 'test' not found\n");
return 2;
}
/*
* Scan for the 'call *%r11' instruction: 41 ff d3
* Then inspect the bytes immediately before it.
*
* Correct: 45 31 db 41 ff d3 (xor %r11d,%r11d ; call *%r11)
* Buggy: 31 db 41 ff d3 (xor %ebx,%ebx ; call *%r11)
*/
for (i = 3; i < 128; i++) {
if (code[i] == 0x41 && code[i+1] == 0xff && code[i+2] == 0xd3) {
if (i >= 3 && code[i-3] == 0x45
&& code[i-2] == 0x31
&& code[i-1] == 0xdb) {
printf("xor_rex: OK\n");
} else if (i >= 2 && code[i-2] == 0x31 && code[i-1] == 0xdb) {
printf("xor_rex: FAIL - xor %%ebx,%%ebx (31 db) emitted"
" instead of xor %%r11d,%%r11d (45 31 db)\n");
ret = 1;
} else {
printf("xor_rex: FAIL - unexpected bytes before"
" call *%%r11: %02x %02x %02x %02x\n",
code[i-4], code[i-3], code[i-2], code[i-1]);
ret = 1;
}
goto done;
}
}
printf("xor_rex: SKIP - call *%%r11 not found in generated code\n");
done:
tcc_delete(s);
return ret;
}
#endif

164
tests/msvcrt_start.c Normal file
View File

@ -0,0 +1,164 @@
/* ------------------------------------------------------------- */
/* minimal startup with runtime linker to msvcrt */
#if 0
#define REDIR_ALL \
REDIR(__set_app_type)\
REDIR(__getmainargs)\
REDIR(_controlfp)\
REDIR(_vsnprintf)\
REDIR(exit)\
\
REDIR(puts)\
REDIR(printf)\
REDIR(putchar)\
REDIR(strtod)\
REDIR(memset)\
REDIR(strcpy)\
REDIR(strlen)\
REDIR(malloc)\
REDIR(free)\
#if defined __i386__ && !defined __TINYC__
# define __leading_underscore 1
#endif
#ifdef __leading_underscore
# define _(s) "_"#s
#else
# define _(s) #s
#endif
#define REDIR(s) void *s;
static struct { REDIR_ALL } all_ptrs;
#undef REDIR
#define REDIR(s) #s"\0"
static const char all_names[] = REDIR_ALL;
#undef REDIR
#if __aarch64__
#if defined __TINYC__
# define ALIGN ".align 8"
#else
# define ALIGN ".align 3" /* .align is power of 2 on non-ELF platforms */
#endif
# define REDIR(s) \
__asm__("\n"_(s)":"); \
__asm__(".int 0x58000090"); /* ldr x16, [pc, #16] */ \
__asm__(".int 0xf9400210"); /* ldr x16, [x16] */ \
__asm__(".int 0xd61f0200"); /* br x16 */ \
__asm__(".int 0xd503201f"); /* nop for alignment */ \
__asm__(".quad all_ptrs + (. - all_jmps - 16) / 24 * 8"); \
__asm__(".global "_(s));
__asm__("\t.text\n\t"ALIGN"\nall_jmps:");
REDIR_ALL
#else
# define REDIR(s) \
__asm__("\n"_(s)":");\
__asm__("jmp *%0"::"m"(all_ptrs.s));\
__asm__(".global "_(s));
static void all_jmps() { REDIR_ALL }
#endif
#undef REDIR
#if 0
# include <windows.h>
#else
# if __i386__
# define STDCALL __declspec(stdcall)
# else
# define STDCALL
# endif
# define DWORD long unsigned
# define HMODULE void*
# define HANDLE void*
S TDCALL HMODULE LoadLibraryA(const char *);
S TDCALL HMODULE GetProcAddress(HMODULE , char*);
S TDCALL void ExitProcess(int);
S TDCALL int WriteFile(HANDLE, const void*, DWORD, DWORD*, void*);
S TDCALL HANDLE GetStdHandle(DWORD);
S TDCALL int FlushFileBuffers(HANDLE);
# define STD_ERROR_HANDLE -12
#endif
static void eput(const char *s)
{
DWORD n_out;
int n = 0;
while (s[n])
++n;
WriteFile(GetStdHandle(STD_ERROR_HANDLE), s, n, &n_out, 0);
}
static void rt_reloc()
{
const char *s = all_names;
void **p = (void**)&all_ptrs;
void *dll = LoadLibraryA("msvcrt.dll");
do {
char buf[100], *d = buf;
*p = (void*)GetProcAddress(dll, (char*)s);
*d++ = '_'; do *d++ = *s; while (*s++);
if (0 == *p)
*p = (void*)GetProcAddress(dll, buf);
if (0 == *p) {
eput("MSVCRT_START.C: RUNTIME RELOCATION ERROR: '");
eput(buf+1);
eput("'\n");
ExitProcess(-1);
}
++p;
} while (*s);
}
#else
# define rt_reloc()
#endif
int main(int argc, char **argv, char **env);
void exit(int);
void __set_app_type(int apptype);
typedef struct { int newmode; } _startupinfo;
int __getmainargs(int *pargc, char ***pargv, char ***penv, int globb, _startupinfo*);
void _controlfp(unsigned a, unsigned b);
#define _MCW_PC 0x00030000 // Precision Control
#define _PC_53 0x00010000 // 53 bits
int __argc;
char **__argv;
char **environ;
_startupinfo start_info = {0};
void mainCRTStartup(void)
{
rt_reloc();
#if defined __i386__ || defined __x86_64__
_controlfp(_PC_53, _MCW_PC);
#endif
__set_app_type(1);
__getmainargs(&__argc, &__argv, &environ, 0, &start_info);
exit(main(__argc, __argv, environ));
}
#include <stdarg.h>
#define size_t __SIZE_TYPE__
int printf(const char *, ...);
int _vsnprintf(char *, size_t, const char *, va_list);
/* undefined on windows-11-arm64 */
int vprintf(const char *format, va_list ap)
{
char buf[1000];
_vsnprintf(buf, sizeof buf, format, ap);
return printf("%s", buf);
}
void __main() {} /* for gcc */
void _pei386_runtime_relocator(void) {} /* for gcc */
void __chkstk(unsigned n) {} /* for clang */

View File

@ -1,155 +0,0 @@
#!/usr/local/bin/tcc -run -nostdlib
// Not working on windows and apple because of different API.
#include <unistd.h>
#include <sys/syscall.h>
#if defined __x86_64__
__asm__ ("syscall:\n\t"
"mov %rdi,%rax\n\t"
"mov %rsi,%rdi\n\t"
"mov %rdx,%rsi\n\t"
"mov %rcx,%rdx\n\t"
"mov %r8,%r10\n\t"
"mov %r9,%r8\n\t"
"mov 0x8(%rsp),%r9\n\t"
"syscall\n\t"
"ret");
__asm__ (".global _start\n\t"
"_start:\n\t"
"mov 0(%rsp), %rdi\n\t"
"lea 8(%rsp), %rsi\n\t"
"jmp print");
#elif defined __i386__
__asm__ ("syscall:\n\t"
"push %ebp\n\t"
"push %edi\n\t"
"push %esi\n\t"
"push %ebx\n\t"
"mov 0x2c(%esp),%ebp\n\t"
"mov 0x28(%esp),%edi\n\t"
"mov 0x24(%esp),%esi\n\t"
"mov 0x20(%esp),%edx\n\t"
"mov 0x1c(%esp),%ecx\n\t"
"mov 0x18(%esp),%ebx\n\t"
"mov 0x14(%esp),%eax\n\t"
// "call *%gs:0x10\n\t"
".byte 0x65,0xff,0x15,0x10,0x00,0x00,0x00\n\t"
"pop %ebx\n\t"
"pop %esi\n\t"
"pop %edi\n\t"
"pop %ebp\n\t"
"ret");
__asm__ (".global _start\n\t"
"_start:\n\t"
"pop %esi\n\t"
"mov %esp, %ecx\n\t"
"and $0xfffffff0,%esp\n\t"
"push %ecx\n\t"
"push %esi\n\t"
"call print");
#elif defined __arm__
__asm__ ("syscall:\n\t"
"mov r12, sp\n\t"
"push {r4, r5, r6, r7}\n\t"
"mov r7, r0\n\t"
"mov r0, r1\n\t"
"mov r1, r2\n\t"
"mov r2, r3\n\t"
"ldm r12, {r3, r4, r5, r6}\n\t"
"svc 0x00000000\n\t"
"pop {r4, r5, r6, r7}\n\t"
"mov pc, lr");
__asm__ (".global _start\n\t"
"_start:\n\t"
"pop {r0}\n\t"
"mov r1, sp\n\t"
"bl print");
#elif defined __aarch64__
__asm__ ("syscall:\n\t"
".int 0x2a0003e8\n\t" // mov w8, w0
".int 0xaa0103e0\n\t" // x0, x1
".int 0xaa0203e1\n\t" // mov x1, x2
".int 0xaa0303e2\n\t" // mov x2, x3
".int 0xaa0403e3\n\t" // mov x3, x4
".int 0xaa0503e4\n\t" // mov x4, x5
".int 0xaa0603e5\n\t" // mov x5, x6
".int 0xaa0703e6\n\t" // mov x6, x7
".int 0xd4000001\n\t" // svc #0x0
".int 0xd65f03c0"); // ret
__asm__ (".global _start\n\t"
"_start:\n\t"
".int 0xf94003e0\n\t" // ldr x0, [sp]
".int 0x910023e1\n\t" // add x1, sp, #08
".reloc .,R_AARCH64_CALL26,print\n\t"
".int 0x94000000"); // bl print
#elif defined __riscv
__asm__ ("syscall:\n\t"
"mv t1,a0\n\t"
"mv a0,a1\n\t"
"mv a1,a2\n\t"
"mv a2,a3\n\t"
"mv a3,a4\n\t"
"mv a4,a5\n\t"
"mv a5,a6\n\t"
"mv a6,a7\n\t"
"mv a7,t1\n\t"
"ecall\n\t"
"ret");
__asm__ (".global _start\n\t"
"_start:\n\t"
"ld a0,0(sp)\n\t"
"addi a1,sp,8\n\t"
"jal print");
#endif
unsigned long strlen(const char *s)
{
unsigned long len = 0;
while (*s++)
len++;
return len;
}
static void pr_num(int num)
{
char val[20], *p = &val[20];
*--p = '\0';
do {
int a = num, b = 0;
while (a >= 10) {
a -= 10;
b++;
}
*--p = a + '0';
num = b;
} while (num);
syscall(SYS_write, 1, p, strlen(p));
}
static void pr_str(int n, char *s)
{
pr_num(n);
syscall(SYS_write, 1, ": ", 2);
syscall(SYS_write, 1, s, strlen(s));
syscall(SYS_write, 1, "\n", 1);
}
void print(int argc, char **argv) {
int i;
char **envp = &argv[argc + 1];
syscall(SYS_write, 1, "argc: ", 6);
pr_num(argc);
syscall(SYS_write, 1, "\n", 1);
syscall(SYS_write, 1, "argv[]\n", 7);
for (i = 0; i < argc; i++)
pr_str(i, argv[i]);
syscall(SYS_write, 1, "envp[]\n", 7);
i = 0;
while (*envp)
pr_str(i++, *envp++);
syscall(SYS_exit, 0);
}

View File

@ -46,6 +46,7 @@ clean:
rm -f *.output
02.test : DIFF_OPTS += -w
16.test : DIFF_OPTS += -B
# 15.test : DIFF_OPTS += -I"^XXX:"
# diff options:

View File

@ -32,8 +32,9 @@
#define XLONG_LONG_FORMAT "%Lx"
#endif
// MinGW has 80-bit rather than 64-bit long double which isn't compatible with TCC or MSVC
#if defined(_WIN32) && defined(__GNUC__)
/* MinGW has 80-bit rather than 64-bit long double which isn't
compatible with printf in msvcrt */
#if defined(_WIN32)
#define LONG_DOUBLE double
#define LONG_DOUBLE_LITERAL(x) x
#else
@ -81,9 +82,6 @@ typedef __SIZE_TYPE__ uintptr_t;
#include incname
#include stringify(funnyname)
int puts(const char *s);
void *alloca(size_t size);
int fib(int n);
void num(int n);
void forward_ref(void);
@ -287,6 +285,7 @@ comment
printf("basefromheader %s\n", get_basefile_from_header());
printf("base %s\n", __BASE_FILE__);
#if !(defined _WIN32 && CC_NAME == CC_clang)
{
/* Some compilers (clang) prepend './' to __FILE__ from included
files. */
@ -295,6 +294,8 @@ comment
fn += 2;
printf("filefromheader %s\n", fn);
}
#endif
printf("file %s\n", __FILE__);
/* Check that funnily named include was in fact included */
@ -1095,8 +1096,10 @@ void struct_test()
sizeof(struct aligntest2), __alignof__(struct aligntest2));
printf("aligntest3 sizeof=%d alignof=%d\n",
sizeof(struct aligntest3), __alignof__(struct aligntest3));
#if !(defined _WIN32 && CC_NAME == CC_clang)
printf("aligntest4 sizeof=%d alignof=%d\n",
sizeof(struct aligntest4), __alignof__(struct aligntest4));
#endif
printf("aligntest5 sizeof=%d alignof=%d\n",
sizeof(struct aligntest5), __alignof__(struct aligntest5));
printf("aligntest6 sizeof=%d alignof=%d\n",
@ -1105,8 +1108,10 @@ void struct_test()
sizeof(struct aligntest7), __alignof__(struct aligntest7));
printf("aligntest8 sizeof=%d alignof=%d\n",
sizeof(struct aligntest8), __alignof__(struct aligntest8));
#if !(defined _WIN32 && CC_NAME == CC_clang)
printf("aligntest9 sizeof=%d alignof=%d\n",
sizeof(struct aligntest9), __alignof__(struct aligntest9));
#endif
printf("aligntest10 sizeof=%d alignof=%d\n",
sizeof(struct aligntest10), __alignof__(struct aligntest10));
printf("altest5 sizeof=%d alignof=%d\n",
@ -1117,7 +1122,9 @@ void struct_test()
sizeof(altest7), __alignof__(altest7));
/* empty structures (GCC extension) */
#if !(defined _WIN32 && CC_NAME == CC_clang)
printf("sizeof(struct empty) = %d\n", sizeof(struct empty));
#endif
printf("alignof(struct empty) = %d\n", __alignof__(struct empty));
printf("Large: sizeof=%d\n", sizeof(ls));
@ -1168,7 +1175,7 @@ void char_short_test()
var4 = 0x11223344aa998877ULL;
printf("promote char/short assign VA %d %d\n", var3 = var1 + 1, var3 = var4 + 1);
printf("promote char/short cast VA %d %d\n", (signed char)(var1 + 1), (signed char)(var4 + 1));
#if !defined(__arm__)
#if !defined __arm__ && !defined __riscv
/* We can't really express GCC behaviour of return type promotion in
the presence of undefined behaviour (like __csf is). */
var1 = csf(unsigned char,0x89898989);
@ -1459,17 +1466,37 @@ static int tab_reinit[10];
static int tentative_ar[];
static int tentative_ar[] = {1,2,3};
//int cinit1; /* a global variable can be defined several times without error ! */
int cinit1;
int cinit1; /* a global variable can be defined several times without error ! */
int cinit1;
int cinit1 = 0;
int *cinit2 = (int []){3, 2, 1};
uintptr_t cinit3 = (uintptr_t)"AA";
char const * const cinit8[] = { [0 ... 1] = "BB", [2 ... 4] = "CC" };
void *cinit52 = &(void*){ (void*) 52 };
#if __TINYC__ || __GNUC__ >= 6
int cinit4 = (int){44};
void *cinit51 = (void*){ (void*) 51 };
struct _c6 { int a,b; } cinit6 = (struct _c6){61,62}, *cinit7 = &(struct _c6){71,72};
#else
int cinit4 = 44;
void *cinit51 = (void*)51;
struct _c6 { int a,b; } cinit6 = { 61,62 }, cinit70 = {71,72}, *cinit7 = &cinit70;
#endif
void compound_literal_test(void)
{
int *p, i;
char *q, *q3;
printf("cinit3 : %s\n", cinit3);
printf("cinit4 : %d\n", cinit4);
printf("cinit51 : %d\n", (int)cinit51);
printf("cinit52 : %d\n", *(int*)cinit52);
printf("cinit6 : %d %d\n", cinit6.a, cinit6.b);
printf("cinit7 : %d %d\n", cinit7->a, cinit7->b);
printf("cinit8 : %s %s %s %s %s\n", cinit8[0], cinit8[1], cinit8[2], cinit8[3], cinit8[4]);
p = (int []){1, 2, 3};
for(i=0;i<3;i++)
printf(" %d", p[i]);
@ -2176,15 +2203,6 @@ float strtof(const char *nptr, char **endptr);
LONG_DOUBLE strtold(const char *nptr, char **endptr);
#endif
#if CC_NAME == CC_clang
/* In clang 0.0/0.0 is nan and not -nan.
Also some older clang version do v=-v
as v = -0 - v */
static char enable_nan_test = 0;
#else
static char enable_nan_test = 1;
#endif
#define FTEST(prefix, typename, type, fmt)\
void prefix ## cmp(type a, type b)\
{\
@ -2240,7 +2258,7 @@ void prefix ## fcast(type a)\
b = llia;\
printf("lltof: " fmt "\n", b);\
b = llua;\
printf("ulltof: " fmt "\n", b);\
if (CC_NAME != CC_clang) printf("ulltof: " fmt "\n", b);\
}\
\
float prefix ## retf(type a) { return a; }\
@ -2300,7 +2318,7 @@ void prefix ## test(void)\
prefix ## fcast(-2334.6);\
prefix ## call();\
prefix ## signed_zeros();\
if (enable_nan_test) prefix ## nan();\
if (CC_NAME != CC_clang) prefix ## nan();\
}
FTEST(f, float, float, "%f")
@ -2556,8 +2574,8 @@ void longlong_test(void)
a = ia;
b = ua;
printf(LONG_LONG_FORMAT " " LONG_LONG_FORMAT "\n", a, b);
printf(LONG_LONG_FORMAT " " LONG_LONG_FORMAT " " LONG_LONG_FORMAT " %Lx\n",
(long long)1,
printf(LONG_LONG_FORMAT " " LONG_LONG_FORMAT " " LONG_LONG_FORMAT " "XLONG_LONG_FORMAT"\n",
(long long)1,
(long long)-2,
1LL,
0x1234567812345679);
@ -2867,12 +2885,14 @@ void stdarg_test(void)
stdarg_for_struct(bob, bob2, bob3, bob4, bob, bob, bob.profile);
stdarg_for_libc("stdarg_for_libc: %s %.2f %d\n", "string", 1.23, 456);
stdarg_syntax(1, 17);
#if !(defined _WIN32 && CC_NAME == CC_clang) /* broken clang */
stdarg_double_struct(6,-1,pts[0],pts[1],pts[2],pts[3],pts[4],pts[5]);
stdarg_double_struct(7,1,pts[0],-1.0,pts[1],pts[2],pts[3],pts[4],pts[5]);
stdarg_double_struct(7,2,pts[0],pts[1],-1.0,pts[2],pts[3],pts[4],pts[5]);
stdarg_double_struct(7,3,pts[0],pts[1],pts[2],-1.0,pts[3],pts[4],pts[5]);
stdarg_double_struct(7,4,pts[0],pts[1],pts[2],pts[3],-1.0,pts[4],pts[5]);
stdarg_double_struct(7,5,pts[0],pts[1],pts[2],pts[3],pts[4],-1.0,pts[5]);
#endif
}
int reltab[3] = { 1, 2, 3 };
@ -3289,7 +3309,7 @@ void local_label_test(void)
}
/* inline assembler test */
#if defined(__i386__) || defined(__x86_64__)
#if (defined(__i386__) || defined(__x86_64__)) && !(defined _WIN32 && CC_NAME == CC_clang)
typedef __SIZE_TYPE__ word;
@ -3551,8 +3571,8 @@ void asm_local_label_diff (void)
{
printf ("asm_local_label_diff: %d %d\n", alld_stuff[0], alld_stuff[1]);
}
#endif
#endif
#endif //!__APPLE__
#endif //!_WIN32
/* This checks that static local variables are available from assembler. */
void asm_local_statics (void)

74
tests/test-win32.bat Normal file
View File

@ -0,0 +1,74 @@
@echo off
setlocal
set CC=gcc
set TESTS=
if "%1"=="/?" goto :usage
goto :p0
:usage
echo usage: test-win32.bat [options...] [tests...]
echo options:
echo -c compiler reference compiler (gcc or clang)
echo -p path prepend path to PATH
echo tests tests to run (default all)
echo requires: make, gcc/clang, and sh in the PATH
exit /B 1
:p2
shift
:p1
shift
:p0
if "%1"=="-c" set CC=%2&&goto p2
if "%1"=="-p" set PATH=%2;%PATH%&&goto p2
if not "%1"=="" set TESTS=%TESTS% %1&&goto p1
if "%TESTS%"=="" set TESTS=all -k
set PATH=%CD%\..\win32;%PATH%
for /f "delims=-" %%a in ('tcc.exe -dumpmachine') do set ARCH=%%a
set ARCH=%ARCH:aarch=arm%
set MACH=%ARCH:i386=x86%
set MACH=%MACH:x86_64=amd64%
rem echo ARCH:%ARCH% MACHINE:%MACH%
set CRTLIB=c:/windows/system32/msvcrt.dll
set LIBTCC=libtcc.dll
set LGCC=-lgcc
if %CC%==gcc goto :c2
set CRTLIB=msvcrt.lib
set LIBTCC=libtcc.lib
set LGCC=
if exist %CRTLIB% goto :c3
tcc -impdef msvcrt.dll
lib >nul /def:msvcrt.def /out:%CRTLIB% /machine:%MACH%
:c2
if not exist %CRTLIB% echo test-win32.bat: error: %CRTLIB% not found&&exit /b 1
:c3
set REF_LINK=-nostdlib msvcrt_start.c %LGCC% -lkernel32 %CRTLIB%
set CFG_MAK=..\config.mak
set CFG_H=..\config.h
echo>>%CFG_H% #define CC_NAME CC_%CC%
echo>>%CFG_H% #define GCC_MAJOR 15
if exist %CFG_MAK% del %CFG_MAK%
echo>>%CFG_MAK% CC = %CC%.exe
echo>>%CFG_MAK% CC_NAME = %CC%
echo>>%CFG_MAK% ARCH = %ARCH%
echo>>%CFG_MAK% CFLAGS = -Wall -O0
echo>>%CFG_MAK% LDFLAGS =
echo>>%CFG_MAK% LIBSUF = .lib
echo>>%CFG_MAK% prefix = $(TOP)/bin
echo>>%CFG_MAK% EXESUF = .exe
echo>>%CFG_MAK% DLLSUF = .dll
echo>>%CFG_MAK% TARGETOS = WIN32
echo>>%CFG_MAK% CONFIG_WIN32 = yes
echo>>%CFG_MAK% TOPSRC = $(TOP)
echo>>%CFG_MAK% SHELL = sh
echo>>%CFG_MAK% test.ref: CFLAGS += %REF_LINK%
set GMAKE=make
%GMAKE% TCC_LOCAL=tcc.exe LIBTCC=win32/%LIBTCC% %TESTS%

View File

@ -0,0 +1,280 @@
/* ARM64 instruction encoding verification test.
Exercises shift-by-immediate, load/store addressing modes,
conditional branches, and system register access. */
#include <stdio.h>
#include <stdint.h>
/* ---- shift-by-immediate helpers ---- */
static uint32_t lsl32(uint32_t x, int n) { return x << n; }
static uint32_t lsr32(uint32_t x, int n) { return x >> n; }
static int32_t asr32(int32_t x, int n) { return x >> n; }
static uint64_t lsl64(uint64_t x, int n) { return x << n; }
static uint64_t lsr64(uint64_t x, int n) { return x >> n; }
static int64_t asr64(int64_t x, int n) { return x >> n; }
static void test_shifts(void)
{
printf("shift-imm:\n");
printf("%x\n", lsl32(1, 0));
printf("%x\n", lsl32(1, 15));
printf("%x\n", lsl32(1, 31));
printf("%x\n", lsr32(0x80000000U, 31));
printf("%x\n", lsr32(0x80000000U, 16));
printf("%x\n", asr32(-1, 1));
printf("%x\n", asr32(-256, 4));
printf("%llx\n", (unsigned long long)lsl64(1ULL, 0));
printf("%llx\n", (unsigned long long)lsl64(1ULL, 32));
printf("%llx\n", (unsigned long long)lsl64(1ULL, 63));
printf("%llx\n", (unsigned long long)lsr64(0x8000000000000000ULL, 63));
printf("%llx\n", (unsigned long long)asr64(-1LL, 1));
printf("%llx\n", (unsigned long long)asr64(-256LL, 4));
}
/* ---- load/store with various addressing modes ---- */
static void test_ldr_str(void)
{
int64_t buf[4] = { 0x1122334455667788LL, 0x99AABBCCDDEEFF00LL,
0x0F1E2D3C4B5A6978LL, 0x0000000000000001LL };
int64_t val;
int32_t wval;
int64_t *p;
printf("ldr-str:\n");
/* LDR X, [Xn, #offset] */
val = buf[0];
printf("%llx\n", (unsigned long long)val);
val = buf[1];
printf("%llx\n", (unsigned long long)val);
/* LDR W, [Xn, #offset] (32-bit load) */
wval = *(int32_t*)&buf[0];
printf("%x\n", (unsigned)wval);
/* Pre-indexed: store pointer, modify */
p = &buf[0];
val = *(p + 2);
printf("%llx\n", (unsigned long long)val);
/* Post-indexed simulation via pointer arithmetic */
p = &buf[0];
val = *p;
p++;
printf("%llx %llx\n", (unsigned long long)val,
(unsigned long long)*p);
}
/* ---- STP/LDP (store/load pair) ---- */
static void test_ldp_stp(void)
{
int64_t src[2] = { 0xAAAABBBBCCCCDDDDLL, 0x1111222233334444LL };
int64_t dst[2];
/* The compiler should use stp/ldp for this */
dst[0] = src[0];
dst[1] = src[1];
printf("ldp-stp:\n");
printf("%llx %llx\n",
(unsigned long long)dst[0],
(unsigned long long)dst[1]);
}
/* ---- CBZ / CBNZ via C patterns ---- */
static const char *cbz_test(int x)
{
if (x == 0)
return "zero";
return "nonzero";
}
static const char *cbnz_test(int x)
{
if (x != 0)
return "nonzero";
return "zero";
}
static void test_cond_branches(void)
{
printf("cbz-cbnz:\n");
printf("%s\n", cbz_test(0));
printf("%s\n", cbz_test(42));
printf("%s\n", cbnz_test(0));
printf("%s\n", cbnz_test(42));
}
/* ---- conditional compare patterns (CCMP/CCMN) ---- */
static int cond_select(int a, int b)
{
/* TCC should generate csel or equivalent */
return a > b ? a : b;
}
static void test_cond_select(void)
{
printf("csel:\n");
printf("%d\n", cond_select(5, 10));
printf("%d\n", cond_select(10, 5));
printf("%d\n", cond_select(0, 0));
printf("%d\n", cond_select(-1, 1));
}
/* ---- MRS/MSR system registers (FPCR/FPSR) ---- */
static void test_sysregs(void)
{
unsigned int fpcr, fpsr;
printf("sysregs:\n");
/* Read FPCR */
__asm__ volatile ("mrs %0, fpcr" : "=r"(fpcr));
printf("%u\n", fpcr & 0x0F);
/* Read FPSR */
__asm__ volatile ("mrs %0, fpsr" : "=r"(fpsr));
printf("%u\n", fpsr);
/* Write/restore FPCR (should be same value) */
__asm__ volatile ("msr fpcr, %0" : : "r"(fpcr));
/* Read back and verify */
{
unsigned int check;
__asm__ volatile ("mrs %0, fpcr" : "=r"(check));
printf("%s\n", check == fpcr ? "ok" : "fail");
}
}
/* ---- NOP encoding (should not crash) ---- */
static void test_nop(void)
{
printf("nop:\n");
__asm__ volatile ("nop");
__asm__ volatile ("nop");
__asm__ volatile ("nop");
printf("ok\n");
}
/* ---- barrier instructions ---- */
static void test_barriers(void)
{
printf("barriers:\n");
__asm__ volatile ("dmb sy");
__asm__ volatile ("dsb sy");
__asm__ volatile ("isb");
printf("ok\n");
}
/* ---- MOV (register) patterns ---- */
static int64_t mov_x0_x1(int64_t x)
{
register int64_t r __asm__("x0") = x;
__asm__ volatile ("" : "=r"(r) : "0"(r));
return r;
}
static void test_mov_reg(void)
{
printf("mov-reg:\n");
printf("%lld\n", (long long)mov_x0_x1(42));
printf("%lld\n", (long long)mov_x0_x1(-1));
}
/* ---- large struct passing (reference semantics) ---- */
struct large { int64_t a, b, c, d; };
static int64_t sum_large(struct large s)
{
return s.a + s.b + s.c + s.d;
}
static struct large make_large(int64_t a, int64_t b, int64_t c, int64_t d)
{
struct large s = { a, b, c, d };
return s;
}
static void test_large_structs(void)
{
struct large s = { 1, 2, 3, 4 };
struct large t;
printf("large-struct:\n");
printf("%lld\n", (long long)sum_large(s));
t = make_large(10, 20, 30, 40);
printf("%lld %lld %lld %lld\n",
(long long)t.a, (long long)t.b,
(long long)t.c, (long long)t.d);
}
/* ---- boundary-sized structs ---- */
struct s18 { char x[18]; };
struct s24 { char x[24]; };
struct s32 { char x[32]; };
static void print_s18(struct s18 s) { printf("%.18s\n", s.x); }
static void print_s24(struct s24 s) { printf("%.24s\n", s.x); }
static void print_s32(struct s32 s) { printf("%.32s\n", s.x); }
static struct s18 ret_s18(void)
{
struct s18 s = { "123456789012345678" };
return s;
}
static struct s24 ret_s24(void)
{
struct s24 s = { "123456789012345678901234" };
return s;
}
static struct s32 ret_s32(void)
{
struct s32 s = { "12345678901234567890123456789012" };
return s;
}
static void test_boundary_structs(void)
{
struct s18 a;
struct s24 b;
struct s32 c;
printf("boundary-structs:\n");
a = ret_s18();
printf("%.18s\n", a.x);
b = ret_s24();
printf("%.24s\n", b.x);
c = ret_s32();
printf("%.32s\n", c.x);
}
int main(void)
{
test_shifts();
test_ldr_str();
test_ldp_stp();
test_cond_branches();
test_cond_select();
test_sysregs();
test_nop();
test_barriers();
test_mov_reg();
test_large_structs();
test_boundary_structs();
return 0;
}

View File

@ -0,0 +1,50 @@
shift-imm:
1
8000
80000000
1
8000
ffffffff
fffffff0
1
100000000
8000000000000000
1
ffffffffffffffff
fffffffffffffff0
ldr-str:
1122334455667788
99aabbccddeeff00
55667788
f1e2d3c4b5a6978
1122334455667788 99aabbccddeeff00
ldp-stp:
aaaabbbbccccdddd 1111222233334444
cbz-cbnz:
zero
nonzero
zero
nonzero
csel:
10
10
0
1
sysregs:
0
0
ok
nop:
ok
barriers:
ok
mov-reg:
42
-1
large-struct:
10
10 20 30 40
boundary-structs:
123456789012345678
123456789012345678901234
12345678901234567890123456789012

View File

@ -0,0 +1,91 @@
/* ARM64 inline assembly error tests.
Verify that invalid assembly produces proper error messages.
Run with -dt; skipped on non-arm64 architectures. */
#include <stdio.h>
#if defined test_unknown_instruction
int main(void)
{
__asm__("fubar x0, x1, x2");
return 0;
}
#elif defined test_shift_imm_range_32
int main(void)
{
/* LSL by 32 is out of range for 32-bit operand */
__asm__("lsl w0, w1, #32");
return 0;
}
#elif defined test_shift_imm_range_64
int main(void)
{
/* LSL by 64 is out of range for 64-bit operand */
__asm__("lsl x0, x1, #64");
return 0;
}
#elif defined test_invalid_sysreg
int main(void)
{
/* Bogus system register name */
__asm__("mrs x0, bogusreg");
return 0;
}
#elif defined test_invalid_barrier_option
int main(void)
{
/* Invalid barrier scope name */
__asm__("dmb xyz");
return 0;
}
#elif defined test_missing_third_operand
int main(void)
{
__asm__("add x0, x1");
return 0;
}
#elif defined test_movz_imm_range
int main(void)
{
__asm__("movz x0, #0x10000");
return 0;
}
#elif defined test_movz_shift_range
int main(void)
{
__asm__("movz x0, #1, lsl #8");
return 0;
}
#elif defined test_invalid_muls
int main(void)
{
__asm__("muls x0, x1, x2");
return 0;
}
#elif defined test_extended_inline_asm
int main(void)
{
int x = 1;
/* Invalid operand reference in extended inline asm */
__asm__("add %0, %1, #1" : "=r"(x) : "2"(x));
return 0;
}
#elif defined test_extended_inline_clobber
int main(void)
{
/* Invalid clobber register name */
__asm__ volatile ("nop" : : : "bogus");
return 0;
}
#endif

View File

@ -0,0 +1,32 @@
[test_unknown_instruction]
139_arm64_errors.c:10: error: ARM64 instruction 'fubar' not implemented
[test_shift_imm_range_32]
139_arm64_errors.c:17: error: shift immediate out of range
[test_shift_imm_range_64]
139_arm64_errors.c:25: error: shift immediate out of range
[test_invalid_sysreg]
139_arm64_errors.c:34: error: unsupported system register
[test_invalid_barrier_option]
139_arm64_errors.c:42: error: invalid operand 'xyz'
[test_missing_third_operand]
139_arm64_errors.c:48: error: missing third operand
[test_movz_imm_range]
139_arm64_errors.c:55: error: move wide immediate out of range
[test_movz_shift_range]
139_arm64_errors.c:62: error: move wide shift out of range
[test_invalid_muls]
139_arm64_errors.c:70: error: ARM64 instruction 'muls' not implemented
[test_extended_inline_asm]
139_arm64_errors.c:79: error: invalid reference in constraint 1 ('2')
[test_extended_inline_clobber]
139_arm64_errors.c:87: error: invalid clobber register 'bogus'

View File

@ -0,0 +1,451 @@
/*
* ARM64 Extended Inline Assembly Tests
* Tests for GCC-style extended inline assembly with operands, constraints, and clobbers
*/
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
struct pair64 {
uint64_t a;
uint64_t b;
};
static int arm64_symbol_target(void)
{
return 7;
}
static void test_symbolic_address_constraint_compile_only(void)
{
asm volatile("" : : "S"(arm64_symbol_target));
}
/* Test 1: Basic output operand */
void test_basic_output(void)
{
int x;
asm("mov %0, #42" : "=r"(x));
assert(x == 42);
printf("Test 1 (basic output): PASSED\n");
}
/* Test 2: Input operand */
void test_input_operand(void)
{
int x = 10;
int y;
asm("add %0, %1, #5" : "=r"(y) : "r"(x));
assert(y == 15);
printf("Test 2 (input operand): PASSED\n");
}
/* Test 3: Read-write operand */
void test_read_write_operand(void)
{
int x = 10;
asm("add %0, %0, #1" : "+r"(x));
assert(x == 11);
printf("Test 3 (read-write operand): PASSED\n");
}
/* Test 4: Memory operand - load */
void test_memory_load(void)
{
int x = 42;
int y;
asm("ldr %w0, [%1]" : "=r"(y) : "r"(&x));
assert(y == 42);
printf("Test 4 (memory load): PASSED\n");
}
/* Test 5: Memory operand - store */
void test_memory_store(void)
{
int x;
int val = 123;
asm("str %w1, [%0]" : : "r"(&x), "r"(val));
assert(x == 123);
printf("Test 5 (memory store): PASSED\n");
}
/* Test 5a: Stack memory operand with frame-relative offset */
void test_stack_memory_operand(void)
{
long x = 0;
long val = 321;
asm volatile("str %0, %1" : : "r"(val), "m"(x));
assert(x == 321);
printf("Test 5a (stack memory operand): PASSED\n");
}
/* Test 5b: Symbol memory operand */
static long arm64_symbol_mem;
void test_symbol_memory_operand(void)
{
long val = 654;
arm64_symbol_mem = 0;
asm volatile("str %0, %1" : : "r"(val), "m"(arm64_symbol_mem));
assert(arm64_symbol_mem == 654);
printf("Test 5b (symbol memory operand): PASSED\n");
}
/* Test 6: Clobber list */
void test_clobber_list(void)
{
int x = 10;
int y = 20;
int result;
asm("add %0, %1, %2"
: "=r"(result)
: "r"(x), "r"(y)
: "cc");
assert(result == 30);
printf("Test 6 (clobber list): PASSED\n");
}
/* Test 7: Multiple outputs */
void test_multiple_outputs(void)
{
int a, b;
asm("mov %0, #1; mov %1, #2"
: "=r"(a), "=r"(b));
assert(a == 1 && b == 2);
printf("Test 7 (multiple outputs): PASSED\n");
}
/* Test 8: Constraint reference */
void test_constraint_reference(void)
{
int x = 10;
int y;
asm("add %0, %1, #5" : "=r"(y) : "0"(x));
assert(y == 15);
printf("Test 8 (constraint reference): PASSED\n");
}
/* Test 9: Early clobber */
void test_early_clobber(void)
{
int x = 10;
int y;
asm("mov %0, #42" : "=&r"(y) : "r"(x));
assert(y == 42);
printf("Test 9 (early clobber): PASSED\n");
}
/* Test 10: 32-bit register modifier */
void test_w_register(void)
{
uint32_t x = 100;
uint32_t y;
asm("add %w0, %w1, #50" : "=r"(y) : "r"(x));
assert(y == 150);
printf("Test 10 (w register modifier): PASSED\n");
}
/* Test 11: Immediate constraint 'I' (12-bit immediate) */
void test_immediate_i_constraint(void)
{
int x = 100;
int y;
asm("add %0, %1, #200" : "=r"(y) : "r"(x), "I"(200));
assert(y == 300);
printf("Test 11 (immediate I constraint): PASSED\n");
}
/* Test 12: Register constraint */
void test_general_operand_constraint(void)
{
int x = 50;
int y;
asm("add %0, %1, #10" : "=r"(y) : "r"(x));
assert(y == 60);
printf("Test 12 (general operand constraint): PASSED\n");
}
/* Test 13: Multiple inputs and outputs */
void test_multiple_io(void)
{
int a = 10, b = 20;
int sum, diff;
asm("add %0, %1, %2" : "=r"(sum) : "r"(a), "r"(b));
asm("sub %0, %1, %2" : "=r"(diff) : "r"(a), "r"(b));
assert(sum == 30 && diff == -10);
printf("Test 13 (multiple IO): PASSED\n");
}
/* Test 14: Register variable preservation */
void test_regvar_preservation(void)
{
register uint64_t keep asm("x19") = 0x123456789abcdef0ULL;
uint64_t out;
asm volatile("mov x19, #7; add %0, x19, #1"
: "=r"(out)
:
: "x19");
assert(keep == 0x123456789abcdef0ULL);
assert(out == 8);
printf("Test 14 (regvar preservation): PASSED\n");
}
/* Test 15: Complex arithmetic */
void test_complex_arithmetic(void)
{
int a = 100, b = 50, c = 25;
int result;
asm("add %0, %1, %2; sub %0, %0, %3"
: "=&r"(result)
: "r"(a), "r"(b), "r"(c));
assert(result == 125);
printf("Test 15 (complex arithmetic): PASSED\n");
}
/* Test 16: Named operand (GCC extension) */
void test_named_operand(void)
{
int input = 10;
int output;
asm("add %[out], %[in], #5"
: [out] "=r"(output)
: [in] "r"(input));
assert(output == 15);
printf("Test 16 (named operand): PASSED\n");
}
/* Test 17: Memory clobber */
void test_memory_clobber(void)
{
int x = 10;
asm volatile("" : : : "memory");
assert(x == 10);
printf("Test 17 (memory clobber): PASSED\n");
}
/* Test 18: Condition flags clobber */
void test_cc_clobber(void)
{
int x = 100;
int y;
asm("adds %0, %1, #0" : "=r"(y) : "r"(x) : "cc");
assert(y == 100);
printf("Test 18 (cc clobber): PASSED\n");
}
/* Test 19: Large immediate with movz/movk */
void test_large_immediate(void)
{
uint64_t val;
asm("movz %0, #0x1234, lsl #16; movk %0, #0x5678" : "=r"(val));
assert(val == 0x12345678ULL);
printf("Test 19 (large immediate): PASSED\n");
}
/* Test 20: Bitwise operations */
void test_bitwise_ops(void)
{
uint64_t a = 0xf0f0f0f00f0f0f0fULL;
uint64_t b = 0x3333ffff0000ccccULL;
uint64_t andv;
uint64_t orv;
uint64_t xorv;
uint64_t imm_and;
asm("and %0, %1, %2" : "=r"(andv) : "r"(a), "r"(b));
asm("orr %0, %1, %2" : "=r"(orv) : "r"(a), "r"(b));
asm("eor %0, %1, %2" : "=r"(xorv) : "r"(a), "r"(b));
asm("and %0, %1, %2" : "=r"(imm_and)
: "r"(~0ULL), "L"(0xff00ff00ff00ff00ULL));
assert(andv == (a & b));
assert(orv == (a | b));
assert(xorv == (a ^ b));
assert(imm_and == 0xff00ff00ff00ff00ULL);
printf("Test 20 (bitwise ops): PASSED\n");
}
/* Test 21: Register shift operands */
void test_register_shift_operands(void)
{
uint64_t val = 3;
uint64_t amount = 4;
uint64_t shifted;
uint64_t rotated;
asm("lsl %0, %1, %2" : "=r"(shifted) : "r"(val), "r"(amount));
asm("ror %0, %1, %2" : "=r"(rotated)
: "r"(0x0123456789abcdefULL), "r"(8ULL));
assert(shifted == 48);
assert(rotated == 0xef0123456789abcdULL);
printf("Test 21 (register shifts): PASSED\n");
}
/* Test 22: ROR immediate alias of EXTR */
void test_ror_immediate(void)
{
uint64_t rotated;
asm("ror %0, %1, #8" : "=r"(rotated) : "r"(0x0123456789abcdefULL));
assert(rotated == 0xef0123456789abcdULL);
printf("Test 22 (ror immediate): PASSED\n");
}
/* Test 23: FP/SIMD register constraint and modifier */
void test_fp_register_operand(void)
{
double x = 3.5;
double y;
asm("ldr %d0, [%1]" : "=w"(y) : "r"(&x));
assert(y == x);
printf("Test 23 (fp register operand): PASSED\n");
}
/* Test 24: Zero constraint */
void test_zero_constraint(void)
{
uint64_t x = 41;
uint64_t y;
asm("add %0, %1, %x2" : "=r"(y) : "r"(x), "Z"(0));
assert(y == x);
printf("Test 24 (Z constraint): PASSED\n");
}
/* Test 25: 32-bit logical immediate constraint */
void test_logical_imm32_constraint(void)
{
uint32_t y;
asm("orr %w0, wzr, %1" : "=r"(y) : "K"(0xff));
assert(y == 0xff);
printf("Test 25 (K constraint): PASSED\n");
}
/* Test 26: 64-bit logical immediate constraint */
void test_logical_imm64_constraint(void)
{
uint64_t y;
asm("orr %0, xzr, %1" : "=r"(y) : "L"(0xff00ff00ff00ff00ULL));
assert(y == 0xff00ff00ff00ff00ULL);
printf("Test 26 (L constraint): PASSED\n");
}
/* Test 27: 32-bit MOV pseudo immediate constraint */
void test_mov_imm32_constraint(void)
{
uint32_t y;
asm("mov %w0, %1" : "=r"(y) : "M"(0x1234));
assert(y == 0x1234);
printf("Test 27 (M constraint): PASSED\n");
}
/* Test 28: 64-bit MOV pseudo immediate constraint */
void test_mov_imm64_constraint(void)
{
uint64_t y;
asm("mov %0, %1" : "=r"(y) : "N"(0x12340000ULL));
assert(y == 0x12340000ULL);
printf("Test 28 (N constraint): PASSED\n");
}
/* Test 29: x FP/SIMD register constraint */
void test_x_constraint_fp(void)
{
double x = 6.25;
double y;
asm("ldr %d0, [%1]" : "=x"(y) : "r"(&x));
assert(y == x);
printf("Test 29 (x constraint): PASSED\n");
}
/* Test 30: y FP/SIMD register constraint */
void test_y_constraint_fp(void)
{
double x = 7.25;
double y;
asm("ldr %d0, [%1]" : "=y"(y) : "r"(&x));
assert(y == x);
printf("Test 30 (y constraint): PASSED\n");
}
/* Test 31: Q memory constraint */
static int arm64_q_load(int *ptr)
{
int y;
asm("ldr %w0, %1" : "=r"(y) : "Q"(*ptr));
return y;
}
void test_q_memory_constraint(void)
{
int x = 91;
assert(arm64_q_load(&x) == 91);
printf("Test 31 (Q constraint): PASSED\n");
}
/* Test 32: Ump pair-memory constraint */
void test_ump_memory_constraint(void)
{
struct pair64 pair = { 0x1122334455667788ULL, 0x99aabbccddeeff00ULL };
uint64_t a;
uint64_t b;
asm("ldp %0, %1, %2" : "=r"(a), "=r"(b) : "Ump"(pair));
assert(a == pair.a);
assert(b == pair.b);
printf("Test 32 (Ump constraint): PASSED\n");
}
int main(void)
{
printf("ARM64 Extended Inline Assembly Tests\n");
test_basic_output();
test_input_operand();
test_read_write_operand();
test_memory_load();
test_memory_store();
test_stack_memory_operand();
test_symbol_memory_operand();
test_clobber_list();
test_multiple_outputs();
test_constraint_reference();
test_early_clobber();
test_w_register();
test_immediate_i_constraint();
test_general_operand_constraint();
test_multiple_io();
test_regvar_preservation();
test_complex_arithmetic();
test_named_operand();
test_memory_clobber();
test_cc_clobber();
test_large_immediate();
test_bitwise_ops();
test_register_shift_operands();
test_ror_immediate();
test_fp_register_operand();
test_zero_constraint();
test_logical_imm32_constraint();
test_logical_imm64_constraint();
test_mov_imm32_constraint();
test_mov_imm64_constraint();
test_x_constraint_fp();
test_y_constraint_fp();
test_q_memory_constraint();
test_ump_memory_constraint();
test_symbolic_address_constraint_compile_only();
printf("ARM64 Extended Inline Assembly Tests PASSED!\n");
return 0;
}

View File

@ -0,0 +1,36 @@
ARM64 Extended Inline Assembly Tests
Test 1 (basic output): PASSED
Test 2 (input operand): PASSED
Test 3 (read-write operand): PASSED
Test 4 (memory load): PASSED
Test 5 (memory store): PASSED
Test 5a (stack memory operand): PASSED
Test 5b (symbol memory operand): PASSED
Test 6 (clobber list): PASSED
Test 7 (multiple outputs): PASSED
Test 8 (constraint reference): PASSED
Test 9 (early clobber): PASSED
Test 10 (w register modifier): PASSED
Test 11 (immediate I constraint): PASSED
Test 12 (general operand constraint): PASSED
Test 13 (multiple IO): PASSED
Test 14 (regvar preservation): PASSED
Test 15 (complex arithmetic): PASSED
Test 16 (named operand): PASSED
Test 17 (memory clobber): PASSED
Test 18 (cc clobber): PASSED
Test 19 (large immediate): PASSED
Test 20 (bitwise ops): PASSED
Test 21 (register shifts): PASSED
Test 22 (ror immediate): PASSED
Test 23 (fp register operand): PASSED
Test 24 (Z constraint): PASSED
Test 25 (K constraint): PASSED
Test 26 (L constraint): PASSED
Test 27 (M constraint): PASSED
Test 28 (N constraint): PASSED
Test 29 (x constraint): PASSED
Test 30 (y constraint): PASSED
Test 31 (Q constraint): PASSED
Test 32 (Ump constraint): PASSED
ARM64 Extended Inline Assembly Tests PASSED!

View File

@ -0,0 +1,356 @@
#include <stdio.h>
#ifdef __riscv
/* P0.4 + P1.4: riscv64 asm pseudo-instructions test.
Exercises neg/negw, sext.w, fmv.s/d, fneg.s/d. */
int test_neg(int x)
{
int r;
asm("neg %0, %1" : "=r"(r) : "r"(x));
return r;
}
long long test_negw(long long x)
{
int r;
asm("negw %0, %1" : "=r"(r) : "r"((int)x));
return (long long)r;
}
long long test_sextw(int x)
{
long long r;
asm("sext.w %0, %1" : "=r"(r) : "r"(x));
return r;
}
float test_fmv_s(float a)
{
float r;
asm("fmv.s %0, %1" : "=f"(r) : "f"(a));
return r;
}
float test_fneg_s(float a)
{
float r;
asm("fneg.s %0, %1" : "=f"(r) : "f"(a));
return r;
}
double test_fmv_d(double a)
{
double r;
asm("fmv.d %0, %1" : "=f"(r) : "f"(a));
return r;
}
double test_fneg_d(double a)
{
double r;
asm("fneg.d %0, %1" : "=f"(r) : "f"(a));
return r;
}
int test_pseudo(void)
{
int ok = 1;
if (test_neg(42) != -42) {
printf("FAIL: neg\n");
ok = 0;
}
if (test_negw(100) != -100) {
printf("FAIL: negw\n");
ok = 0;
}
if (test_sextw(0x80000000) != 0xffffffff80000000LL) {
printf("FAIL: sext.w\n");
ok = 0;
}
if (test_fmv_s(3.14f) != 3.14f) {
printf("FAIL: fmv.s\n");
ok = 0;
}
if (test_fneg_s(3.14f) != -3.14f) {
printf("FAIL: fneg.s\n");
ok = 0;
}
if (test_fmv_d(2.718281828) != 2.718281828) {
printf("FAIL: fmv.d\n");
ok = 0;
}
if (test_fneg_d(2.718281828) != -2.718281828) {
printf("FAIL: fneg.d\n");
ok = 0;
}
return ok;
}
/* P1.1: riscv64 inline asm with 64-bit immediate (li).
Tests that long long immediates assemble correctly,
including the lui+addi sequence for large constants. */
long long test_li_small(void)
{
long long r;
asm("li %0, 42" : "=r"(r));
return r;
}
long long test_li_large(void)
{
long long r;
asm("li %0, 0x123456789ABCDEF0" : "=r"(r));
return r;
}
long long test_li_negative(void)
{
long long r;
asm("li %0, -1" : "=r"(r));
return r;
}
int test_ll(void)
{
int ok = 1;
if (test_li_small() != 42) {
printf("FAIL: li small\n");
ok = 0;
}
if (test_li_large() != 0x123456789ABCDEF0LL) {
printf("FAIL: li large\n");
ok = 0;
}
if (test_li_negative() != -1) {
printf("FAIL: li negative\n");
ok = 0;
}
return ok;
}
/* P1.3: riscv64 F/D extension arithmetic instructions.
Tests fadd/fsub/fmul/fdiv for both single and double precision. */
float test_fadd_s(float a, float b)
{
float r;
asm("fadd.s %0, %1, %2" : "=f"(r) : "f"(a), "f"(b));
return r;
}
float test_fsub_s(float a, float b)
{
float r;
asm("fsub.s %0, %1, %2" : "=f"(r) : "f"(a), "f"(b));
return r;
}
float test_fmul_s(float a, float b)
{
float r;
asm("fmul.s %0, %1, %2" : "=f"(r) : "f"(a), "f"(b));
return r;
}
float test_fdiv_s(float a, float b)
{
float r;
asm("fdiv.s %0, %1, %2" : "=f"(r) : "f"(a), "f"(b));
return r;
}
double test_fadd_d(double a, double b)
{
double r;
asm("fadd.d %0, %1, %2" : "=f"(r) : "f"(a), "f"(b));
return r;
}
double test_fsub_d(double a, double b)
{
double r;
asm("fsub.d %0, %1, %2" : "=f"(r) : "f"(a), "f"(b));
return r;
}
double test_fmul_d(double a, double b)
{
double r;
asm("fmul.d %0, %1, %2" : "=f"(r) : "f"(a), "f"(b));
return r;
}
double test_fdiv_d(double a, double b)
{
double r;
asm("fdiv.d %0, %1, %2" : "=f"(r) : "f"(a), "f"(b));
return r;
}
int test_farith(void)
{
int ok = 1;
if (test_fadd_s(1.5f, 2.5f) != 4.0f) {
printf("FAIL: fadd.s\n");
ok = 0;
}
if (test_fsub_s(5.0f, 2.0f) != 3.0f) {
printf("FAIL: fsub.s\n");
ok = 0;
}
if (test_fmul_s(3.0f, 4.0f) != 12.0f) {
printf("FAIL: fmul.s\n");
ok = 0;
}
if (test_fdiv_s(12.0f, 4.0f) != 3.0f) {
printf("FAIL: fdiv.s\n");
ok = 0;
}
if (test_fadd_d(1.5, 2.5) != 4.0) {
printf("FAIL: fadd.d\n");
ok = 0;
}
if (test_fsub_d(5.0, 2.0) != 3.0) {
printf("FAIL: fsub.d\n");
ok = 0;
}
if (test_fmul_d(3.0, 4.0) != 12.0) {
printf("FAIL: fmul.d\n");
ok = 0;
}
if (test_fdiv_d(12.0, 4.0) != 3.0) {
printf("FAIL: fdiv.d\n");
ok = 0;
}
return ok;
}
int csr_pseudo_main(void)
{
int ok = 1;
int old, tmp;
asm volatile("csrr %0, 0x003" : "=r"(old));
asm volatile("csrr %0, 0x003" : "=r"(tmp));
printf("csrr fcsr=%x\n", (unsigned)tmp);
asm volatile("csrw 0x003, %0" : : "r"(0xE0));
asm volatile("csrr %0, 0x003" : "=r"(tmp));
printf("csrw: wrote e0 got %x\n", (unsigned)tmp);
if (tmp != 0xE0) { printf("FAIL: csrw\n"); ok = 0; }
asm volatile("csrw 0x003, %0" : : "r"(old));
asm volatile("csrwi 0x003, 0x10");
asm volatile("csrr %0, 0x003" : "=r"(tmp));
printf("csrwi: wrote 0x10 got %x\n", (unsigned)tmp);
if (tmp != 0x10) { printf("FAIL: csrwi\n"); ok = 0; }
asm volatile("csrw 0x003, %0" : : "r"(old));
asm volatile("csrsi 0x003, 0x03");
asm volatile("csrr %0, 0x003" : "=r"(tmp));
printf("csrsi: old|3=%x\n", (unsigned)tmp);
if ((old | 0x03) != tmp) { printf("FAIL: csrsi\n"); ok = 0; }
asm volatile("csrw 0x003, %0" : : "r"(old));
asm volatile("csrci 0x003, 0x03");
asm volatile("csrr %0, 0x003" : "=r"(tmp));
printf("csrci: old&~3=%x\n", (unsigned)tmp);
if ((old & ~0x03) != tmp) { printf("FAIL: csrci\n"); ok = 0; }
asm volatile("csrw 0x003, %0" : : "r"(old));
return ok;
}
int fp_cmp_cvt_main(void)
{
/* F/D comparison (use raw regs to avoid inline asm float→int bug) */
asm volatile("feq.s a0, fa0, fa1");
asm volatile("feq.d a0, fa0, fa1");
asm volatile("flt.s a0, fa0, fa1");
asm volatile("flt.d a0, fa0, fa1");
asm volatile("fle.s a0, fa0, fa1");
asm volatile("fle.d a0, fa0, fa1");
/* fcvt conversions */
asm volatile("fcvt.w.s a0, fa0");
asm volatile("fcvt.wu.s a0, fa0");
asm volatile("fcvt.s.w fa0, a0");
asm volatile("fcvt.w.d a0, fa0");
asm volatile("fcvt.d.w fa0, a0");
asm volatile("fcvt.d.s fa0, fa0");
asm volatile("fcvt.s.d fa0, fa0");
/* fclass */
asm volatile("fclass.s a0, fa0");
asm volatile("fclass.d a0, fa0");
return 1;
}
int amo_main(void)
{
int x = 0, r;
long long xd = 0xCAFEBABECAFEBABELL, rd, val = 0xDEADBEEFDEADBEEFLL;
asm("amoadd.w %0, %2, (%1)" : "=r"(r) : "r"(&x), "r"(val));
asm("amoswap.w %0, %2, (%1)" : "=r"(r) : "r"(&x), "r"(val));
asm("amoand.w %0, %2, (%1)" : "=r"(r) : "r"(&x), "r"(val));
asm("amoor.d %0, %2, (%1)" : "=r"(rd) : "r"(&xd), "r"(val));
asm("amoxor.w %0, %2, (%1)" : "=r"(r) : "r"(&x), "r"(val));
asm("amomax.w %0, %2, (%1)" : "=r"(r) : "r"(&x), "r"(val));
asm("amomaxu.d %0, %2, (%1)" : "=r"(rd) : "r"(&xd), "r"(val));
asm("amomin.w %0, %2, (%1)" : "=r"(r) : "r"(&x), "r"(val));
asm("amominu.d %0, %2, (%1)" : "=r"(rd) : "r"(&xd), "r"(val));
asm("amoadd.w.aq %0, %2, (%1)" : "=r"(r) : "r"(&x), "r"(val));
asm("amoadd.w.rl %0, %2, (%1)" : "=r"(r) : "r"(&x), "r"(val));
asm("amoadd.d.aqrl %0, %2, (%1)" : "=r"(rd) : "r"(&xd), "r"(val));
asm("amoswap.w.aq %0, %2, (%1)" : "=r"(r) : "r"(&x), "r"(val));
asm("amoswap.d.rl %0, %2, (%1)" : "=r"(rd) : "r"(&xd), "r"(val));
asm("amoand.w.aqrl %0, %2, (%1)" : "=r"(r) : "r"(&x), "r"(val));
asm("amoor.w.rl %0, %2, (%1)" : "=r"(r) : "r"(&x), "r"(val));
asm("amoor.d.aq %0, %2, (%1)" : "=r"(rd) : "r"(&xd), "r"(val));
asm("amoxor.w.aq %0, %2, (%1)" : "=r"(r) : "r"(&x), "r"(val));
asm("amomax.d.rl %0, %2, (%1)" : "=r"(rd) : "r"(&xd), "r"(val));
asm("amomaxu.w.aqrl %0, %2, (%1)" : "=r"(r) : "r"(&x), "r"(val));
asm("amomin.w.rl %0, %2, (%1)" : "=r"(r) : "r"(&x), "r"(val));
asm("amominu.d.aq %0, %2, (%1)" : "=r"(rd) : "r"(&xd), "r"(val));
return 1;
}
int fcvt_round_main(void)
{
asm volatile("fcvt.w.s a0, fa0, rne");
asm volatile("fcvt.w.s a0, fa0, rtz");
asm volatile("fcvt.w.s a0, fa0, rup");
asm volatile("fcvt.w.d a0, fa0, rne");
asm volatile("fcvt.w.d a0, fa0, rtz");
return 1;
}
int main()
{
int ok = 1;
ok &= test_pseudo();
ok &= test_ll();
ok &= test_farith();
ok &= csr_pseudo_main();
ok &= fp_cmp_cvt_main();
ok &= amo_main();
ok &= fcvt_round_main();
printf("%s\n", ok ? "PASS" : "FAIL");
return !ok;
}
#else
int main()
{
printf("SKIP\n");
}
#endif

View File

@ -0,0 +1,6 @@
csrr fcsr=1
csrw: wrote e0 got e0
csrwi: wrote 0x10 got 10
csrsi: old|3=3
csrci: old&~3=0
PASS

View File

@ -0,0 +1,143 @@
#include <stdio.h>
/* PROMOTE_RET test: verify char/short return values are properly
zero/sign-extended to 64-bit per the RISC-V integer calling convention.
Without PROMOTE_RET, upper bits of the return register may contain
garbage, causing incorrect results when assigned to a wider type. */
unsigned char get_uc(void) { return 0x80; }
signed char get_sc(void) { return 0x80; }
unsigned short get_us(void) { return 0x8000; }
signed short get_ss(void) { return 0x8000; }
/* Prevent inlining to force ABI-compliant calling. */
unsigned char (* volatile fp_uc)(void) = get_uc;
signed char (* volatile fp_sc)(void) = get_sc;
unsigned short (* volatile fp_us)(void) = get_us;
signed short (* volatile fp_ss)(void) = get_ss;
int promote_main(void)
{
int ok = 1;
unsigned long long uc = fp_uc();
signed long long sc = fp_sc();
unsigned long long us = fp_us();
signed long long ss = fp_ss();
printf("uc=%llx sc=%llx us=%llx ss=%llx\n",
(unsigned long long)uc,
(unsigned long long)sc,
(unsigned long long)us,
(unsigned long long)ss);
if (uc != 0x80) {
printf("FAIL: uc not zero-extended\n");
ok = 0;
}
if (sc != 0xffffffffffffff80LL) {
printf("FAIL: sc not sign-extended\n");
ok = 0;
}
if (us != 0x8000) {
printf("FAIL: us not zero-extended\n");
ok = 0;
}
if (ss != 0xffffffffffff8000LL) {
printf("FAIL: ss not sign-extended\n");
ok = 0;
}
printf("%s\n", ok ? "PASS" : "FAIL");
return ok ? 0 : 1;
}
/* gen_cvt_csti test: verify narrow-type conversions in expressions.
Without the fix, TCC's riscv64 backend could miss the conversion
step when promoting a narrow result back to int, producing wrong
values (e.g., treating a char as still 32-bit). */
int cast_main(void)
{
int ok = 1;
int x = 0x12345678;
/* Cast to char then add 1 — result must be 8-bit. */
char c = (char)x + 1;
unsigned char uc = (unsigned char)x + 1;
short s = (short)x + 1;
unsigned short us = (unsigned short)x + 1;
printf("c=%x uc=%x s=%x us=%x\n",
(unsigned char)c, (unsigned)uc,
(unsigned short)s, (unsigned)us);
if (c != (char)0x78 + 1) {
printf("FAIL: char conversion\n");
ok = 0;
}
if (uc != (unsigned char)0x78 + 1) {
printf("FAIL: unsigned char conversion\n");
ok = 0;
}
if (s != (short)0x5678 + 1) {
printf("FAIL: short conversion\n");
ok = 0;
}
if (us != (unsigned short)0x5678 + 1) {
printf("FAIL: unsigned short conversion\n");
ok = 0;
}
printf("%s\n", ok ? "PASS" : "FAIL");
return ok ? 0 : 1;
}
/* gen_cvt_sxtw test: verify sign-extension from 32-bit int to 64-bit long long.
Without the fix, the riscv64 backend had an empty stub for gen_cvt_sxtw,
leaving upper 32 bits unmodified (containing whatever was in the register
before), so (long long)(int)x produced wrong results for negative values. */
int sign_main(void)
{
int ok = 1;
int x = 0x80000000;
long long y = (long long)x;
printf("y=%llx\n", (unsigned long long)y);
if (y != 0xffffffff80000000LL) {
printf("FAIL: int→long long sign-extension\n");
ok = 0;
}
/* Also test positive value. */
x = 0x40000000;
y = (long long)x;
printf("y=%llx\n", (unsigned long long)y);
if (y != 0x40000000LL) {
printf("FAIL: int→long long positive value\n");
ok = 0;
}
/* Test via unsigned int to catch zero-extension vs sign-extension. */
unsigned int ux = 0x80000000;
long long uy = (long long)(int)ux;
printf("uy=%llx\n", (unsigned long long)uy);
if (uy != 0xffffffff80000000LL) {
printf("FAIL: unsigned→int→long long sign-extension\n");
ok = 0;
}
printf("%s\n", ok ? "PASS" : "FAIL");
return ok ? 0 : 1;
}
int main()
{
return
promote_main()
| cast_main()
| sign_main();
}

View File

@ -0,0 +1,8 @@
uc=80 sc=ffffffffffffff80 us=8000 ss=ffffffffffff8000
PASS
c=79 uc=79 s=5679 us=5679
PASS
y=ffffffff80000000
y=40000000
uy=ffffffff80000000
PASS

View File

@ -0,0 +1,17 @@
#include <stdio.h>
static void f(int x)
{
printf("f(%d)\n", x);
}
int main(void)
{
int count = 0, i = 0;
for (; i < 3; ++i) {
printf("%d\n", i);
(void)(i || (f(i), ++count));
}
printf("count %d\n", count);
return count == 1 ? 0 : 1;
}

View File

@ -0,0 +1,5 @@
0
f(0)
1
2
count 1

50
tests/tests2/144_tls.c Normal file
View File

@ -0,0 +1,50 @@
#include <stdio.h>
#include <pthread.h>
__thread int tls_init = 42;
__thread int tls_zero;
static void *thread_func(void *arg)
{
(void)arg;
printf("%d\n", tls_init);
if (tls_init != 42) return (void *)1;
printf("%d\n", tls_zero);
if (tls_zero != 0) return (void *)1;
tls_init = 100;
tls_zero = 200;
printf("%d\n", tls_init);
printf("%d\n", tls_zero);
return (void *)0;
}
int main()
{
pthread_t t;
void *ret;
int errors = 0;
printf("%d\n", tls_init);
if (tls_init != 42) errors = 1;
printf("%d\n", tls_zero);
if (tls_zero != 0) errors = 1;
pthread_create(&t, NULL, thread_func, NULL);
pthread_join(t, &ret);
if (ret) errors = 1;
printf("%d\n", tls_init);
if (tls_init != 42) errors = 1;
printf("%d\n", tls_zero);
if (tls_zero != 0) errors = 1;
return errors;
}

View File

@ -0,0 +1,8 @@
42
0
42
0
100
200
42
0

View File

@ -0,0 +1,35 @@
#include <stdio.h>
#include <windows.h>
#define PTR(x) ((PVOID)(ULONG_PTR)(x))
#define CHECK(name, expr) printf("%s: %s\n", name, (expr) ? "yes" : "no")
int main(void)
{
PVOID volatile slot = PTR(0x1111222233334444ULL);
PVOID old;
old = InterlockedExchangePointer(&slot, PTR(0x5555666677778888ULL));
CHECK("exchange old", old == PTR(0x1111222233334444ULL));
CHECK("exchange stored", slot == PTR(0x5555666677778888ULL));
old = InterlockedCompareExchangePointer(&slot,
PTR(0x9999aaaabbbbccccULL),
PTR(0x5555666677778888ULL));
CHECK("compare old", old == PTR(0x5555666677778888ULL));
CHECK("compare stored", slot == PTR(0x9999aaaabbbbccccULL));
old = InterlockedCompareExchangePointerAcquire(&slot,
PTR(0xdddd111122223333ULL),
PTR(0x0123456789abcdefULL));
CHECK("acquire old", old == PTR(0x9999aaaabbbbccccULL));
CHECK("acquire stored", slot == PTR(0x9999aaaabbbbccccULL));
old = InterlockedCompareExchangePointerRelease(&slot,
PTR(0xdddd111122223333ULL),
PTR(0x9999aaaabbbbccccULL));
CHECK("release old", old == PTR(0x9999aaaabbbbccccULL));
CHECK("release stored", slot == PTR(0xdddd111122223333ULL));
return 0;
}

View File

@ -0,0 +1,8 @@
exchange old: yes
exchange stored: yes
compare old: yes
compare stored: yes
acquire old: yes
acquire stored: yes
release old: yes
release stored: yes

View File

@ -421,7 +421,7 @@ int f() { return v(); }
#elif defined test_switch_W4
#pragma comment(option, "-Wunsupported -Wno-error=implicit-function-declaration -Werror")
#endif
void func()
void func(void)
{
char *ccp = "123";
fink();
@ -551,4 +551,114 @@ int main(int argc, char **argv)
}
#elif defined test_const_array_member_addr_ok
typedef int int_array[1];
typedef struct { int_array arr; } my_struct;
int_array const *get_array(const my_struct *s)
{
return &(s->arr);
}
#elif defined test_const_array_member_decay_ok
typedef int int_array[1];
typedef struct { int_array arr; } my_struct;
const int *good_decay(const my_struct *s)
{
return s->arr;
}
#elif defined test_const_array_member_addr_bad
typedef int int_array[1];
typedef struct { int_array arr; } my_struct;
int_array *bad_array_addr(const my_struct *s)
{
return &(s->arr);
}
#elif defined test_const_array_member_decay_bad
typedef int int_array[1];
typedef struct { int_array arr; } my_struct;
int *bad_decay(const my_struct *s)
{
return s->arr;
}
#elif defined test_const_array_member_write_bad
typedef int int_array[1];
typedef struct { int_array arr; } my_struct;
void bad_write(const my_struct *s)
{
s->arr[0] = 1;
}
#elif defined test_const_struct_array_member_write_bad
typedef struct { int x; } inner;
typedef struct { inner arr[1]; } outer;
void bad_inner_write(const outer *s)
{
s->arr[0].x = 1;
}
#elif defined test_volatile_array_member_addr_ok
typedef int int_array[1];
typedef struct { int_array arr; } my_struct;
int_array volatile *get_volatile_array(volatile my_struct *s)
{
return &(s->arr);
}
#elif defined test_volatile_array_member_decay_ok
typedef int int_array[1];
typedef struct { int_array arr; } my_struct;
volatile int *good_volatile_decay(volatile my_struct *s)
{
return s->arr;
}
#elif defined test_volatile_array_member_addr_bad
typedef int int_array[1];
typedef struct { int_array arr; } my_struct;
int_array *bad_volatile_array_addr(volatile my_struct *s)
{
return &(s->arr);
}
#elif defined test_volatile_array_member_decay_bad
typedef int int_array[1];
typedef struct { int_array arr; } my_struct;
int *bad_volatile_decay(volatile my_struct *s)
{
return s->arr;
}
#elif defined test_volatile_array_member_write_ok
typedef int int_array[1];
typedef struct { int_array arr; } my_struct;
void good_volatile_write(volatile my_struct *s)
{
s->arr[0] = 1;
}
#endif

View File

@ -262,3 +262,31 @@ bar 15 12 34
[test_scope_3]
60_errors_and_warnings.c:548: warning: assignment from incompatible pointer type
60_errors_and_warnings.c:549: error: initialization of incomplete type
[test_const_array_member_addr_ok]
[test_const_array_member_decay_ok]
[test_const_array_member_addr_bad]
60_errors_and_warnings.c:581: warning: assignment discards qualifiers from pointer target type
[test_const_array_member_decay_bad]
60_errors_and_warnings.c:591: warning: assignment discards qualifiers from pointer target type
[test_const_array_member_write_bad]
60_errors_and_warnings.c:601: warning: assignment of read-only location
[test_const_struct_array_member_write_bad]
60_errors_and_warnings.c:611: warning: assignment of read-only location
[test_volatile_array_member_addr_ok]
[test_volatile_array_member_decay_ok]
[test_volatile_array_member_addr_bad]
60_errors_and_warnings.c:641: warning: assignment discards qualifiers from pointer target type
[test_volatile_array_member_decay_bad]
60_errors_and_warnings.c:651: warning: assignment discards qualifiers from pointer target type
[test_volatile_array_member_write_ok]

View File

@ -38,6 +38,11 @@ struct hfa32 { long double a, b; } hfa32 = { 32.1, 32.2 };
struct hfa33 { long double a, b, c; } hfa33 = { 33.1, 33.2, 33.3 };
struct hfa34 { long double a, b, c, d; } hfa34 = { 34.1, 34.2, 34.3, 34.4 };
struct empty { };
/* Keep the top-level offsets at zero without changing the type into a union. */
struct hfae12 { struct empty e; struct hfa12 h; } hfae12 = { { }, { 112.1, 112.2 } };
struct hfae22 { struct empty e; struct hfa22 h; } hfae22 = { { }, { 122.1, 122.2 } };
void fa_s1(struct s1 a) { printf("%.1s\n", a.x); }
void fa_s2(struct s2 a) { printf("%.2s\n", a.x); }
void fa_s3(struct s3 a) { printf("%.3s\n", a.x); }
@ -82,6 +87,10 @@ void fa_hfa33(struct hfa33 a)
{ printf("%.1Lf %.1Lf %.1Lf\n", a.a, a.b, a.c); }
void fa_hfa34(struct hfa34 a)
{ printf("%.1Lf %.1Lf %.1Lf %.1Lf\n", a.a, a.b, a.c, a.d); }
void fa_hfae12(struct hfae12 a)
{ printf("%.1f %.1f\n", a.h.a, a.h.b); }
void fa_hfae22(struct hfae22 a)
{ printf("%.1f %.1f\n", a.h.a, a.h.b); }
void fa1(struct s8 a, struct s9 b, struct s10 c, struct s11 d,
struct s12 e, struct s13 f)
@ -140,6 +149,8 @@ void arg(void)
fa_hfa32(hfa32);
fa_hfa33(hfa33);
fa_hfa34(hfa34);
fa_hfae12(hfae12);
fa_hfae22(hfae22);
fa1(s8, s9, s10, s11, s12, s13);
fa2(s9, s10, s11, s12, s13, s14);
fa3(hfa14, hfa23, hfa32);
@ -178,6 +189,8 @@ struct hfa31 fr_hfa31(void) { return hfa31; }
struct hfa32 fr_hfa32(void) { return hfa32; }
struct hfa33 fr_hfa33(void) { return hfa33; }
struct hfa34 fr_hfa34(void) { return hfa34; }
struct hfae12 fr_hfae12(void) { return hfae12; }
struct hfae22 fr_hfae22(void) { return hfae22; }
void ret(void)
{
@ -228,6 +241,8 @@ void ret(void)
printf("%.1Lf %.1Lf\n", fr_hfa32().a, fr_hfa32().b);
printf("%.1Lf %.1Lf\n", fr_hfa33().a, fr_hfa33().c);
printf("%.1Lf %.1Lf\n", fr_hfa34().a, fr_hfa34().d);
printf("%.1f %.1f\n", fr_hfae12().h.a, fr_hfae12().h.b);
printf("%.1f %.1f\n", fr_hfae22().h.a, fr_hfae22().h.b);
}
void*

View File

@ -28,6 +28,8 @@ cdefghijklmnopqrs
32.1 32.1
33.1 33.2 33.3
34.1 34.2 34.3 34.4
112.1 112.2
122.1 122.2
stu ABC JKL TUV 456 ghi
ABC JKL TUV 456 ghi tuv
14.1 14.4 23.1 23.3 32.1 32.2
@ -62,6 +64,8 @@ cdefghijklmnopqrs
32.1 32.2
33.1 33.3
34.1 34.4
112.1 112.2
122.1 122.2
stdarg:
ABCDEFGHI ABCDEFGHI ABCDEFGHI ABCDEFGHI ABCDEFGHI ABCDEFGHI
lmnopqr ABCDEFGHI ABCDEFGHI ABCDEFGHI ABCDEFGHI ABCDEFGHI

View File

@ -19,6 +19,9 @@ ifeq (,$(filter i386 x86_64,$(ARCH)))
SKIP += 85_asm-outside-function.test # x86 asm
SKIP += 127_asm_goto.test # hardcodes x86 asm
endif
ifeq (,$(filter x86_64 riscv64 arm64 i386,$(ARCH)))
SKIP += 144_tls.test # TLS only implemented on these architectures so far
endif
ifeq ($(CONFIG_backtrace),no)
SKIP += 113_btdll.test
CONFIG_bcheck = no
@ -47,12 +50,29 @@ ifeq (-$(CONFIG_WIN32)-,-yes-)
SKIP += 114_bound_signal.test # No pthread support
SKIP += 117_builtins.test # win32 port doesn't define __builtins
SKIP += 124_atomic_counter.test # No pthread support
SKIP += 144_tls.test # No pthread support
endif
ifneq (,$(filter OpenBSD FreeBSD NetBSD,$(TARGETOS)))
SKIP += 106_versym.test # no pthread_condattr_setpshared
SKIP += 114_bound_signal.test # libc problem signal/fork
SKIP += 116_bound_setjmp2.test # No TLS_FUNC/TLS_VAR in bcheck.c
endif
ifdef CONFIG_OSX
SKIP += 144_tls.test # TLS runtime not supported on Mach-O
endif
ifeq (,$(filter arm64 aarch64,$(ARCH)))
SKIP += 138_arm64_encoding.test
SKIP += 139_arm64_errors.test
SKIP += 140_arm64_extasm.test
endif
ifneq ($(CONFIG_WIN32),yes)
SKIP += 145_winarm64_interlocked.test
else ifeq (,$(filter arm64 aarch64,$(ARCH)))
SKIP += 145_winarm64_interlocked.test
endif
ifeq (,$(filter riscv64,$(ARCH)))
SKIP += 141_riscv_asm.test # riscv64 asm
endif
# Some tests might need arguments
ARGS =
@ -62,6 +82,7 @@ ARGS =
# And some tests don't test the right thing with -run
NORUN =
42_function_pointer.test : NORUN = true
# riscv64 asm tests validate encoding, raw regs may crash at runtime
# Some tests might need different flags
FLAGS =
@ -74,8 +95,8 @@ endif
# These tests run several snippets from the same file one by one
60_errors_and_warnings.test : FLAGS += -dt
96_nodata_wanted.test : FLAGS += -dt
139_arm64_errors.test : FLAGS += -dt
# Always generate certain .expects (don't put these in the GIT),
GEN-ALWAYS =
# GEN-ALWAYS += 95_bitfields.expect # does not work
@ -98,6 +119,10 @@ GEN-ALWAYS =
# constructor/destructor
108_constructor.test: NORUN = true
# TLS needs executable and pthread, not -run
144_tls.test: FLAGS += -pthread
144_tls.test: NORUN = true
112_backtrace.test: FLAGS += -dt -b
112_backtrace.test 113_btdll.test 126_bound_global.test: FILTER += \
-e 's;[0-9A-Fa-fx]\{5,\};........;g' \
@ -125,6 +150,8 @@ endif
126_bound_global.test: NORUN = true
128_run_atexit.test: FLAGS += -dt
132_bound_test.test: FLAGS += -b
140_arm64_extasm.test: GEN = $(GEN-TCC)
141_riscv_asm.test: GEN = $(GEN-TCC)
# Filter source directory in warnings/errors (out-of-tree builds)
FILTER = 2>&1 | sed -e 's,$(SRC)/,,g'

View File

@ -5,13 +5,15 @@
@echo off
setlocal
if (%1)==(-clean) goto :cleanup
set CC=gcc
set CC=gcc -O2 -Wall
set /p VERSION= < ..\VERSION
set TCCDIR=
set BINDIR=
set DOC=no
set XCC=no
set TX=
set SELF=%~nx0
goto :a0
:a2
shift
:a3
@ -27,20 +29,22 @@ if (%1)==(-v) set VERSION=%~2&& goto :a2
if (%1)==(-i) set TCCDIR=%2&& goto :a2
if (%1)==(-b) set BINDIR=%2&& goto :a2
if (%1)==(-d) set DOC=yes&& goto :a3
if (%1)==(-x) set XCC=yes&& goto :a3
if (%1)==(-x) set TX=%2&& goto :a2
if (%1)==() goto :p1
:usage
echo usage: build-tcc.bat [ options ... ]
echo options:
echo -c prog use prog (gcc/tcc/cl) to compile tcc
echo -c "prog options" use prog with options to compile tcc
echo -t 32/64 force 32/64 bit default target
echo -t target set target
echo -x target build tcc cross-compiler for target
echo -v "version" set tcc version
echo -i tccdir install tcc into tccdir
echo -b bindir but install tcc.exe and libtcc.dll into bindir
echo -d create tcc-doc.html too (needs makeinfo)
echo -x build the cross compiler too
echo -clean delete all previously produced files and directories
echo supported targets i386 x86_64 arm64
exit /B 1
@rem ------------------------------------------------------
@ -65,6 +69,7 @@ exit /B 0
if exist %1 rmdir /Q/S %1 && %LOG% %1
exit /B 0
@rem ------------------------------------------------------
:cl
@echo off
set CMD=cl
@ -84,36 +89,19 @@ echo on
@rem main program
:p1
if not %T%_==_ goto :p2
set T=32
if %PROCESSOR_ARCHITECTURE%_==AMD64_ set T=64
if %PROCESSOR_ARCHITEW6432%_==AMD64_ set T=64
:p2
if "%CC:~-3%"=="gcc" set CC=%CC% -O2 -s -static
if (%BINDIR%)==() set BINDIR=%TCCDIR%
if not _%TX%_==__ set T=%TX%&&set TX=%TX%-win32-
if _%T%_%PROCESSOR_ARCHITECTURE%_==__x86_ set T=i386
if _%T%_%PROCESSOR_ARCHITECTURE%_==__ARM64_ set T=arm64
if _%T%_==__ set T=x86_64
if %T%==i386 set D=-DTCC_TARGET_PE -DTCC_TARGET_I386
if %T%==x86_64 set D=-DTCC_TARGET_PE -DTCC_TARGET_X86_64
if %T%==arm64 set D=-DTCC_TARGET_PE -DTCC_TARGET_ARM64
if "%D%"=="" echo %SELF%: error: unknown target '%T%'&&exit /B 1
set D32=-DTCC_TARGET_PE -DTCC_TARGET_I386
set D64=-DTCC_TARGET_PE -DTCC_TARGET_X86_64
set P32=i386-win32
set P64=x86_64-win32
@if (%CC:~0,3%)==(gcc) set CC=%CC% -s -static
@if (%BINDIR%)==() set BINDIR=%TCCDIR%
if %T%==64 goto :t64
set D=%D32%
set P=%P32%
set DX=%D64%
set PX=%P64%
set TX=64
goto :p3
:t64
set D=%D64%
set P=%P64%
set DX=%D32%
set PX=%P32%
set TX=32
goto :p3
:p3
:git_hash
git.exe --version 2>nul
if not %ERRORLEVEL%==0 goto :git_done
for /f %%b in ('git.exe rev-parse --abbrev-ref HEAD') do set GITHASH=%%b
@ -125,34 +113,37 @@ if %ERRORLEVEL%==1 set GITHASH=%GITHASH%*
:config.h
echo>..\config.h #define TCC_VERSION "%VERSION%"
if not (%GITHASH%)==() echo>> ..\config.h #define TCC_GITHASH "%GITHASH%"
@if not (%BINDIR%)==(%TCCDIR%) echo>> ..\config.h #define CONFIG_TCCDIR "%TCCDIR:\=/%"
if %TX%==64 echo>> ..\config.h #ifdef TCC_TARGET_X86_64
if %TX%==32 echo>> ..\config.h #ifdef TCC_TARGET_I386
echo>> ..\config.h #define CONFIG_TCC_CROSSPREFIX "%PX%-"
echo>> ..\config.h #endif
@if not "%GITHASH%"=="" echo>>..\config.h #define TCC_GITHASH "%GITHASH%"
@if not _%BINDIR%_==_%TCCDIR%_ echo>>..\config.h #define CONFIG_TCCDIR "%TCCDIR:\=/%"
@if not _%TX%_==__ @echo>>..\config.h #define CONFIG_TCC_CROSSPREFIX "%TX%"
@rem echo>> ..\config.h #define CONFIG_TCC_PREDEFS 1
@rem %CC% -DC2STR ..\conftest.c -o c2str.exe
@rem .\c2str.exe ../include/tccdefs.h ../tccdefs_.h
for %%f in (*tcc.exe *tcc.dll) do @del %%f
@if not _%TX%_==__ goto :tcc_cross
@if not _%TCC_C%_==__ goto :tcc_only
@if _%TCC_C%_==__ goto compiler_2parts
@rem if TCC_C was defined then build only tcc.exe
@if _%LIBTCC_C%_==__ set LIBTCC_C=..\libtcc.c
@set IMPLIB=libtcc.dll
@if "%CC:~0,5%"=="clang" set IMPLIB=libtcc.lib
%CC% -o libtcc.dll -shared %LIBTCC_C% %D% -DLIBTCC_AS_DLL
@if errorlevel 1 goto :the_end
%CC% -o tcc.exe ..\tcc.c %IMPLIB% %D% -DONE_SOURCE"=0"
@if errorlevel 1 goto :the_end
@goto :compiler_done
:tcc_only
%CC% -o tcc.exe %TCC_C% %D%
@if errorlevel 1 goto :the_end
@goto :compiler_done
:compiler_2parts
@if _%LIBTCC_C%_==__ set LIBTCC_C=..\libtcc.c
%CC% -o libtcc.dll -shared %LIBTCC_C% %D% -DLIBTCC_AS_DLL
@if errorlevel 1 goto :the_end
%CC% -o tcc.exe ..\tcc.c libtcc.dll %D% -DONE_SOURCE"=0"
@if errorlevel 1 goto :the_end
if not _%XCC%_==_yes_ goto :compiler_done
%CC% -o %PX%-tcc.exe ..\tcc.c %DX%
:tcc_cross
%CC% -o %TX%tcc.exe ..\tcc.c %D%
@if errorlevel 1 goto :the_end
@goto :compiler_done
:compiler_done
@if (%EXES_ONLY%)==(yes) goto :files_done
@ -168,8 +159,7 @@ if exist libtcc.dll .\tcc -impdef libtcc.dll -o libtcc\libtcc.def
@if errorlevel 1 goto :the_end
:lib
call :make_lib %T% || goto :the_end
@if exist %PX%-tcc.exe call :make_lib %TX% %PX%- || goto :the_end
@call :make_lib %TX% || goto :the_end
:tcc-doc.html
@if not (%DOC%)==(yes) goto :doc-done
@ -193,23 +183,26 @@ for %%f in (include examples libtcc doc) do @xcopy>nul /s/i/q/y %%f %TCCDIR%\%%f
exit /B %ERRORLEVEL%
:make_lib
.\tcc -B. -m%1 -c ../lib/libtcc1.c
.\tcc -B. -m%1 -c lib/crt1.c
.\tcc -B. -m%1 -c lib/crt1w.c
.\tcc -B. -m%1 -c lib/wincrt1.c
.\tcc -B. -m%1 -c lib/wincrt1w.c
.\tcc -B. -m%1 -c lib/dllcrt1.c
.\tcc -B. -m%1 -c lib/dllmain.c
.\tcc -B. -m%1 -c lib/chkstk.S
.\tcc -B. -m%1 -c ../lib/alloca.S
.\tcc -B. -m%1 -c ../lib/alloca-bt.S
.\tcc -B. -m%1 -c ../lib/stdatomic.c
.\tcc -B. -m%1 -c ../lib/atomic.S
.\tcc -B. -m%1 -c ../lib/builtin.c
.\tcc -B. -m%1 -ar lib/%2libtcc1.a libtcc1.o crt1.o crt1w.o wincrt1.o wincrt1w.o dllcrt1.o dllmain.o chkstk.o alloca.o alloca-bt.o stdatomic.o atomic.o builtin.o
.\tcc -B. -m%1 -c ../lib/bcheck.c -o lib/%2bcheck.o -bt -I..
.\tcc -B. -m%1 -c ../lib/bt-exe.c -o lib/%2bt-exe.o
.\tcc -B. -m%1 -c ../lib/bt-log.c -o lib/%2bt-log.o
.\tcc -B. -m%1 -c ../lib/bt-dll.c -o lib/%2bt-dll.o
.\tcc -B. -m%1 -c ../lib/runmain.c -o lib/%2runmain.o
@set LIBTCC1=libtcc1
@if _%1_==_arm64_ set LIBTCC1=lib-arm64
.\%1tcc -B. -c ../lib/%LIBTCC1%.c
.\%1tcc -B. -c lib/crt1.c
.\%1tcc -B. -c lib/crt1w.c
.\%1tcc -B. -c lib/wincrt1.c
.\%1tcc -B. -c lib/wincrt1w.c
.\%1tcc -B. -c lib/dllcrt1.c
.\%1tcc -B. -c lib/dllmain.c
.\%1tcc -B. -c lib/winex.c
.\%1tcc -B. -c lib/chkstk.S
.\%1tcc -B. -c ../lib/alloca.S
.\%1tcc -B. -c ../lib/alloca-bt.S
.\%1tcc -B. -c ../lib/stdatomic.c
.\%1tcc -B. -c ../lib/atomic.S
.\%1tcc -B. -c ../lib/builtin.c
.\%1tcc -ar lib/%1libtcc1.a %LIBTCC1%.o crt1.o crt1w.o wincrt1.o wincrt1w.o dllcrt1.o dllmain.o winex.o chkstk.o alloca.o alloca-bt.o stdatomic.o atomic.o builtin.o
.\%1tcc -B. -c ../lib/bcheck.c -o lib/%1bcheck.o -bt -I..
.\%1tcc -B. -c ../lib/bt-exe.c -o lib/%1bt-exe.o
.\%1tcc -B. -c ../lib/bt-log.c -o lib/%1bt-log.o
.\%1tcc -B. -c ../lib/bt-dll.c -o lib/%1bt-dll.o
.\%1tcc -B. -c ../lib/runmain.c -o lib/%1runmain.o
exit /B %ERRORLEVEL%

View File

@ -70,12 +70,15 @@
#ifdef _WIN64
#define __stdcall
#if defined(__aarch64__)
#define _M_ARM64 1
#define _ARM64_ 1
#else
#define _AMD64_ 1
#define __x86_64 1
#define _M_X64 100 /* Visual Studio */
#define _M_AMD64 100 /* Visual Studio */
#define USE_MINGW_SETJMP_TWO_ARGS
#define mingw_getsp tinyc_getbp
#endif
#else
#define __stdcall __attribute__((__stdcall__))
#define _X86_ 1

View File

@ -124,37 +124,76 @@ extern "C" {
SETJMP_FLOAT128 Xmm14;
SETJMP_FLOAT128 Xmm15;
} _JUMP_BUFFER;
#elif defined(_ARM_)
#define _JBLEN 28
#define _JBTYPE int
typedef struct __JUMP_BUFFER {
unsigned long Frame;
unsigned long R4;
unsigned long R5;
unsigned long R6;
unsigned long R7;
unsigned long R8;
unsigned long R9;
unsigned long R10;
unsigned long R11;
unsigned long Sp;
unsigned long Pc;
unsigned long Fpscr;
unsigned long long D[8];
} _JUMP_BUFFER;
#elif defined(_ARM64_)
#define _JBLEN 24
#define _JBTYPE unsigned __int64
typedef struct __JUMP_BUFFER {
unsigned __int64 Frame;
unsigned __int64 Reserved;
unsigned __int64 X19;
unsigned __int64 X20;
unsigned __int64 X21;
unsigned __int64 X22;
unsigned __int64 X23;
unsigned __int64 X24;
unsigned __int64 X25;
unsigned __int64 X26;
unsigned __int64 X27;
unsigned __int64 X28;
unsigned __int64 Fp;
unsigned __int64 Lr;
unsigned __int64 Sp;
unsigned long Fpcr;
unsigned long Fpsr;
double D[8];
} _JUMP_BUFFER;
#else
#define _JBLEN 1
#define _JBTYPE int
#endif
#ifndef _JMP_BUF_DEFINED
typedef _JBTYPE jmp_buf[_JBLEN];
#define _JMP_BUF_DEFINED
#endif
void * __cdecl __attribute__ ((__nothrow__)) mingw_getsp(void);
#pragma pack(pop)
#ifdef USE_MINGW_SETJMP_TWO_ARGS
#ifndef _INC_SETJMPEX
#define setjmp(BUF) _setjmp((BUF),mingw_getsp())
int __cdecl __attribute__ ((__nothrow__)) _setjmp(jmp_buf _Buf,void *_Ctx);
#else
#undef setjmp
#define setjmp(BUF) _setjmpex((BUF),mingw_getsp())
#define setjmpex(BUF) _setjmpex((BUF),mingw_getsp())
int __cdecl __attribute__ ((__nothrow__)) _setjmpex(jmp_buf _Buf,void *_Ctx);
#endif
#else
#ifndef _INC_SETJMPEX
#define setjmp _setjmp
#endif
int __cdecl __attribute__ ((__nothrow__)) setjmp(jmp_buf _Buf);
#if defined __aarch64__
int _setjmpex(jmp_buf _Buf, void *frame);
#define setjmp(BUF) _setjmpex((BUF), (char*)__builtin_frame_address(0) + 224)
#elif defined __x86_64__
int _setjmp(jmp_buf _Buf, void *frame);
#define setjmp(BUF) _setjmp((BUF), __builtin_frame_address(0))
#else /* __i386__ */
int _setjmp(jmp_buf _Buf);
#define setjmp _setjmp
#endif
__declspec(noreturn) __attribute__ ((__nothrow__)) void __cdecl ms_longjmp(jmp_buf _Buf,int _Value)/* throw(...)*/;
__declspec(noreturn) __attribute__ ((__nothrow__)) void __cdecl longjmp(jmp_buf _Buf,int _Value);
__declspec(noreturn) void longjmp(jmp_buf _Buf,int _Value);
#ifdef __cplusplus
}
#endif
#pragma pack(pop)
#endif

View File

@ -207,6 +207,21 @@ extern "C" {
#endif
#endif
#endif
#if defined __aarch64__
/* something does not work using those from msvcrt.dll */
# undef __argc
# undef __argv
# undef __wargv
# undef _wenviron
# undef _environ
extern int __argc;
extern char **__argv;
extern wchar_t **__wargv;
extern char **_environ;
extern wchar_t **_wenviron;
#endif
#ifndef _pgmptr
#ifdef _MSVCRT_
extern char *_pgmptr;

View File

@ -972,7 +972,9 @@ extern "C" {
LONG WINAPI InterlockedDecrement(LONG volatile *lpAddend);
LONG WINAPI InterlockedExchange(LONG volatile *Target,LONG Value);
#ifndef InterlockedExchangePointer
#define InterlockedExchangePointer(Target,Value) (PVOID)InterlockedExchange((PLONG)(Target),(LONG)(Value))
#endif
LONG WINAPI InterlockedExchangeAdd(LONG volatile *Addend,LONG Value);
LONG WINAPI InterlockedCompareExchange(LONG volatile *Destination,LONG Exchange,LONG Comperand);
@ -1035,6 +1037,7 @@ extern "C" {
return Old;
}
#ifndef InterlockedCompareExchangePointer
#ifdef __cplusplus
__CRT_INLINE PVOID __cdecl __InlineInterlockedCompareExchangePointer(PVOID volatile *Destination,PVOID ExChange,PVOID Comperand) {
return((PVOID)(LONG_PTR)InterlockedCompareExchange((LONG volatile *)Destination,(LONG)(LONG_PTR)ExChange,(LONG)(LONG_PTR)Comperand));
@ -1043,6 +1046,7 @@ extern "C" {
#else
#define InterlockedCompareExchangePointer(Destination,ExChange,Comperand)(PVOID)(LONG_PTR)InterlockedCompareExchange((LONG volatile *)(Destination),(LONG)(LONG_PTR)(ExChange),(LONG)(LONG_PTR)(Comperand))
#endif
#endif
#define InterlockedIncrementAcquire InterlockedIncrement
#define InterlockedIncrementRelease InterlockedIncrement
@ -1054,9 +1058,13 @@ extern "C" {
#define InterlockedCompareExchangeRelease InterlockedCompareExchange
#define InterlockedCompareExchangeAcquire64 InterlockedCompareExchange64
#define InterlockedCompareExchangeRelease64 InterlockedCompareExchange64
#ifndef InterlockedCompareExchangePointerAcquire
#define InterlockedCompareExchangePointerAcquire InterlockedCompareExchangePointer
#endif
#ifndef InterlockedCompareExchangePointerRelease
#define InterlockedCompareExchangePointerRelease InterlockedCompareExchangePointer
#endif
#endif
#if defined(_SLIST_HEADER_) && !defined(_NTOSP_)
WINBASEAPI VOID WINAPI InitializeSListHead(PSLIST_HEADER ListHead);

View File

@ -21,7 +21,7 @@ extern "C" {
#define __CRT_UNALIGNED
#endif
#if defined(__ia64__) || defined(__x86_64)
#if defined(__ia64__) || defined(__x86_64) || defined(__aarch64__)
#define UNALIGNED __CRT_UNALIGNED
#ifdef _WIN64
#define UNALIGNED64 __CRT_UNALIGNED
@ -65,7 +65,7 @@ extern "C" {
#ifdef _WIN64
#ifdef _AMD64_
#define PROBE_ALIGNMENT(_s) TYPE_ALIGNMENT(DWORD)
#elif defined(_IA64_)
#elif defined(_IA64_) || defined(_ARM64_)
#define PROBE_ALIGNMENT(_s) (TYPE_ALIGNMENT(_s) > TYPE_ALIGNMENT(DWORD) ? TYPE_ALIGNMENT(_s) : TYPE_ALIGNMENT(DWORD))
#else
#error No Target Architecture
@ -79,7 +79,7 @@ extern "C" {
#include <basetsd.h>
#if defined(_X86_) || defined(__ia64__) || defined(__x86_64)
#if defined(_X86_) || defined(__ia64__) || defined(__x86_64) || defined(__aarch64__)
#define DECLSPEC_IMPORT __declspec(dllimport)
#else
#define DECLSPEC_IMPORT
@ -321,7 +321,7 @@ typedef DWORD LCID;
#define Int32x32To64(a,b) (LONGLONG)((LONGLONG)(LONG)(a) *(LONG)(b))
#define UInt32x32To64(a,b) (ULONGLONG)((ULONGLONG)(DWORD)(a) *(DWORD)(b))
#define Int64ShrlMod32(a,b) ((DWORDLONG)(a)>>(b))
#elif defined(__ia64__) || defined(__x86_64)
#elif defined(__ia64__) || defined(__x86_64) || defined(__aarch64__)
#define Int32x32To64(a,b) ((LONGLONG)((LONG)(a)) *(LONGLONG)((LONG)(b)))
#define UInt32x32To64(a,b) ((ULONGLONG)((DWORD)(a)) *(ULONGLONG)((DWORD)(b)))
#define Int64ShrlMod32(a,b) ((ULONGLONG)(a) >> (b))
@ -829,8 +829,6 @@ typedef DWORD LCID;
typedef ULONG_PTR KSPIN_LOCK;
typedef KSPIN_LOCK *PKSPIN_LOCK;
#ifdef _AMD64_
#if defined(__x86_64) && !defined(RC_INVOKED)
#ifdef __cplusplus
@ -1282,7 +1280,6 @@ typedef DWORD LCID;
#ifdef __cplusplus
}
#endif
#endif
#define EXCEPTION_READ_FAULT 0
#define EXCEPTION_WRITE_FAULT 1
@ -1420,7 +1417,160 @@ typedef DWORD LCID;
typedef DWORD (*POUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK)(HANDLE Process,PVOID TableAddress,PDWORD Entries,PRUNTIME_FUNCTION *Functions);
#define OUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK_EXPORT_NAME "OutOfProcessFunctionTableCallback"
#endif /* defined(__x86_64) && !defined(RC_INVOKED) */
#if defined(__TINYC__) && (defined(__aarch64__) || defined(__arm64__)) && !defined(RC_INVOKED)
#define __TCC_WINNT_ATOMIC_SEQ_CST 5
/* TCC lowers __atomic_compare_exchange's fourth argument as weak, but the
ARM64 helper reads it as success_memorder. Strong CAS passes weak == 0,
so keep Interlocked's barriers explicit here. */
#define __TCC_WINNT_MEMORY_BARRIER() __asm__ __volatile__("dmb ish" : : : "memory")
/* This covers the LONG operations needed by TCC's semaphore code and the
pointer helpers that winbase.h exposes through Interlocked pointer macros.
Add local helpers before relying on increment/decrement, add/exchange-add,
bitwise, or 64-bit Interlocked forms on Windows ARM64 with TCC. */
__CRT_INLINE LONG InterlockedExchange(LONG volatile *Target,LONG Value) {
LONG Old;
__atomic_load(Target,&Old,__TCC_WINNT_ATOMIC_SEQ_CST);
__TCC_WINNT_MEMORY_BARRIER();
while (!__atomic_compare_exchange(Target,&Old,&Value,0,
__TCC_WINNT_ATOMIC_SEQ_CST,__TCC_WINNT_ATOMIC_SEQ_CST))
;
__TCC_WINNT_MEMORY_BARRIER();
return Old;
}
__CRT_INLINE LONG InterlockedCompareExchange(LONG volatile *Destination,LONG ExChange,LONG Comperand) {
LONG Old = Comperand;
__TCC_WINNT_MEMORY_BARRIER();
__atomic_compare_exchange(Destination,&Old,&ExChange,0,
__TCC_WINNT_ATOMIC_SEQ_CST,__TCC_WINNT_ATOMIC_SEQ_CST);
__TCC_WINNT_MEMORY_BARRIER();
return Old;
}
__CRT_INLINE PVOID __TCC_WINNT_InterlockedExchangePointer(PVOID volatile *Target,PVOID Value) {
PVOID Old;
__atomic_load(Target,&Old,__TCC_WINNT_ATOMIC_SEQ_CST);
__TCC_WINNT_MEMORY_BARRIER();
while (!__atomic_compare_exchange(Target,&Old,&Value,0,
__TCC_WINNT_ATOMIC_SEQ_CST,__TCC_WINNT_ATOMIC_SEQ_CST))
;
__TCC_WINNT_MEMORY_BARRIER();
return Old;
}
__CRT_INLINE PVOID __TCC_WINNT_InterlockedCompareExchangePointer(PVOID volatile *Destination,PVOID ExChange,PVOID Comperand) {
PVOID Old = Comperand;
__TCC_WINNT_MEMORY_BARRIER();
__atomic_compare_exchange(Destination,&Old,&ExChange,0,
__TCC_WINNT_ATOMIC_SEQ_CST,__TCC_WINNT_ATOMIC_SEQ_CST);
__TCC_WINNT_MEMORY_BARRIER();
return Old;
}
#define InterlockedExchangePointer __TCC_WINNT_InterlockedExchangePointer
#define InterlockedCompareExchangePointer __TCC_WINNT_InterlockedCompareExchangePointer
#define InterlockedCompareExchangePointerAcquire InterlockedCompareExchangePointer
#define InterlockedCompareExchangePointerRelease InterlockedCompareExchangePointer
#define InterlockedCompareExchangeAcquire InterlockedCompareExchange
#define InterlockedCompareExchangeRelease InterlockedCompareExchange
#undef __TCC_WINNT_MEMORY_BARRIER
#undef __TCC_WINNT_ATOMIC_SEQ_CST
#endif /* defined(__TINYC__) && (defined(__aarch64__) || defined(__arm64__)) && !defined(RC_INVOKED) */
#if defined(_ARM64_)
/* ARM64 Context Definition */
#define CONTEXT_ARM64 0x00400000
#define CONTEXT_CONTROL (CONTEXT_ARM64 | 0x00000001L)
#define CONTEXT_INTEGER (CONTEXT_ARM64 | 0x00000002L)
#define CONTEXT_FLOATING_POINT (CONTEXT_ARM64 | 0x00000004L)
#define CONTEXT_DEBUG (CONTEXT_ARM64 | 0x00000008L)
#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT)
#define CONTEXT_ALL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG)
#define ARM64_MAX_BREAKPOINTS 8
#define ARM64_MAX_WATCHPOINTS 2
typedef union _ARM64_NT_NEON128 {
struct {
ULONGLONG Low;
LONGLONG High;
} DUMMYSTRUCTNAME;
double D[2];
float S[4];
WORD H[8];
BYTE B[16];
} ARM64_NT_NEON128,*PARM64_NT_NEON128;
typedef struct DECLSPEC_ALIGN(16) _ARM64_NT_CONTEXT {
ULONG ContextFlags;
ULONG Cpsr;
union {
struct {
DWORD64 X0;
DWORD64 X1;
DWORD64 X2;
DWORD64 X3;
DWORD64 X4;
DWORD64 X5;
DWORD64 X6;
DWORD64 X7;
DWORD64 X8;
DWORD64 X9;
DWORD64 X10;
DWORD64 X11;
DWORD64 X12;
DWORD64 X13;
DWORD64 X14;
DWORD64 X15;
DWORD64 X16;
DWORD64 X17;
DWORD64 X18;
DWORD64 X19;
DWORD64 X20;
DWORD64 X21;
DWORD64 X22;
DWORD64 X23;
DWORD64 X24;
DWORD64 X25;
DWORD64 X26;
DWORD64 X27;
DWORD64 X28;
DWORD64 Fp;
DWORD64 Lr;
} DUMMYSTRUCTNAME;
DWORD64 X[31];
} DUMMYUNIONNAME;
DWORD64 Sp;
DWORD64 Pc;
ARM64_NT_NEON128 V[32];
DWORD Fpcr;
DWORD Fpsr;
DWORD Bcr[ARM64_MAX_BREAKPOINTS];
DWORD64 Bvr[ARM64_MAX_BREAKPOINTS];
DWORD Wcr[ARM64_MAX_WATCHPOINTS];
DWORD64 Wvr[ARM64_MAX_WATCHPOINTS];
} ARM64_NT_CONTEXT,*PARM64_NT_CONTEXT;
C_ASSERT(sizeof(ARM64_NT_CONTEXT) == 0x390);
typedef ARM64_NT_CONTEXT CONTEXT,*PCONTEXT;
typedef struct _RUNTIME_FUNCTION {
DWORD BeginAddress;
DWORD UnwindData;
} RUNTIME_FUNCTION,*PRUNTIME_FUNCTION;
typedef PRUNTIME_FUNCTION (*PGET_RUNTIME_FUNCTION_CALLBACK)(DWORD64 ControlPc,PVOID Context);
typedef DWORD (*POUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK)(HANDLE Process,PVOID TableAddress,PDWORD Entries,PRUNTIME_FUNCTION *Functions);
#endif /* _ARM64_ */
#if (defined _ARM64_ || defined _AMD64_) && !defined RC_INVOKED
NTSYSAPI VOID __cdecl RtlRestoreContext (PCONTEXT ContextRecord,struct _EXCEPTION_RECORD *ExceptionRecord);
NTSYSAPI BOOLEAN __cdecl RtlAddFunctionTable(PRUNTIME_FUNCTION FunctionTable,DWORD EntryCount,DWORD64 BaseAddress);
NTSYSAPI BOOLEAN __cdecl RtlInstallFunctionTableCallback(DWORD64 TableIdentifier,DWORD64 BaseAddress,DWORD Length,PGET_RUNTIME_FUNCTION_CALLBACK Callback,PVOID Context,PCWSTR OutOfProcessCallbackDll);
@ -3701,6 +3851,7 @@ typedef DWORD LCID;
#define IMAGE_FILE_MACHINE_CEF 0x0CEF
#define IMAGE_FILE_MACHINE_EBC 0x0EBC
#define IMAGE_FILE_MACHINE_AMD64 0x8664
#define IMAGE_FILE_MACHINE_ARM64 0xAA64
#define IMAGE_FILE_MACHINE_M32R 0x9041
#define IMAGE_FILE_MACHINE_CEE 0xC0EE
@ -3857,10 +4008,16 @@ typedef DWORD LCID;
#define IMAGE_SUBSYSTEM_EFI_ROM 13
#define IMAGE_SUBSYSTEM_XBOX 14
#define IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA 0x0020
#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040
#define IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY 0x0080
#define IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100
#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200
#define IMAGE_DLLCHARACTERISTICS_NO_SEH 0x0400
#define IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800
#define IMAGE_DLLCHARACTERISTICS_APPCONTAINER 0x1000
#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000
#define IMAGE_DLLCHARACTERISTICS_GUARD_CF 0x4000
#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0

View File

@ -8,8 +8,32 @@
#endif
/* ---------------------------------------------- */
#ifndef __x86_64__
#if defined(__aarch64__)
.globl __chkstk
__chkstk:
/* Windows ARM64 stack probing helper.
arm64-gen.c passes the requested frame size in x15, scaled in 16-byte
units. Probe one 4 KiB page at a time and leave SP unchanged; the caller
subtracts SP after the probe returns. */
mov x16, sp
lsl x17, x15, 4
cbz x17, L_chkstk_done
L_chkstk_loop:
subs x0, x17, 4096
bls L_chkstk_tail
sub x16, x16, 4096
ldr xzr, [x16]
sub x17, x17, 4096
b L_chkstk_loop
L_chkstk_tail:
sub x16, x16, x17
ldr xzr, [x16]
L_chkstk_done:
ret
/* ---------------------------------------------- */
#elif defined(__i386__)
.globl _(__chkstk)
_(__chkstk):
@ -33,8 +57,7 @@ P0:
jmp *4(%eax)
/* ---------------------------------------------- */
#else
/* ---------------------------------------------- */
#else /* __x86_64__ */
.globl _(__chkstk)
_(__chkstk):
@ -58,16 +81,6 @@ P0:
mov (%rax),%rcx /* restore ecx */
jmp *8(%rax)
/* ---------------------------------------------- */
/* setjmp/longjmp support */
.globl _(tinyc_getbp)
_(tinyc_getbp):
mov %rbp,%rax
ret
/* ---------------------------------------------- */
#endif
/* ---------------------------------------------- */

View File

@ -75,10 +75,11 @@ __attribute__((weak)) extern int __run_on_exit();
int _runtmain(int argc, /* as tcc passed in */ char **argv)
{
int ret;
#ifdef UNICODE
#if defined UNICODE || defined __aarch64__
_startupinfo start_info = {0};
__tgetmainargs(&__argc, &__targv, &_tenviron, _dowildcard, &start_info);
__tgetmainargs(&__argc, &__targv, &_tenviron, 0, &start_info);
#endif
#ifdef UNICODE
/* may be wrong when tcc has received wildcards (*.c) */
if (argc < __argc) {
__targv += __argc - argc;
@ -93,6 +94,8 @@ int _runtmain(int argc, /* as tcc passed in */ char **argv)
#endif
run_ctors(__argc, __targv, _tenviron);
ret = _tmain(__argc, __targv, _tenviron);
fflush(stdout);
fflush(stderr);
run_dtors();
__run_on_exit(ret);
return ret;

View File

@ -9,6 +9,7 @@ AllocConsole
AllocLSCallback
AllocSLCallback
AreFileApisANSI
AttachConsole
BackupRead
BackupSeek
BackupWrite

27
win32/lib/winex.c Normal file
View File

@ -0,0 +1,27 @@
/* ------------------------------------------------------------------------- */
/* winex.c : extra stuff */
#if __aarch64__
#include <stdlib.h>
/* replaces environ from arm64-msvcrt.dll which does not exist */
char **_environ;
wchar_t **_wenviron;
/* those do exist but have problems */
int __argc;
char **__argv;
wchar_t **__wargv;
#endif
#if __aarch64__ || __x86_64__
/* MSVC x64 intrinsic */
void __faststorefence(void)
{
#if __aarch64__
/* ARM64: Data Memory Barrier (Inner Shareable) */
__asm__("dmb ish");
#elif __x86_64__
/* x86-64: lock prefix to flush store buffer */
__asm__("lock; orl $0,(%%rsp)" ::: "memory");
#endif
}
#endif

View File

@ -266,14 +266,6 @@ ST_FUNC void gen_addr32(int r, Sym *sym, int c)
gen_le32(c);
}
/* output constant with relocation if 'r & VT_SYM' is true */
ST_FUNC void gen_addr64(int r, Sym *sym, int64_t c)
{
if (r & VT_SYM)
greloca(cur_text_section, sym, ind, R_X86_64_64, c), c=0;
gen_le64(c);
}
/* output constant with relocation if 'r & VT_SYM' is true */
ST_FUNC void gen_addrpc32(int r, Sym *sym, int c)
{
@ -322,7 +314,7 @@ static void gen_modrm_impl(int op_reg, int r, Sym *sym, int c, int is_got)
}
} else if ((r & VT_VALMASK) == VT_LOCAL) {
/* currently, we use only ebp as base */
if (c == (char)c) {
if (c == (signed char)c) {
/* short reference */
o(0x45 | op_reg);
g(c);
@ -376,7 +368,8 @@ void load(int r, SValue *sv)
#ifndef TCC_TARGET_PE
/* we use indirect access via got */
if ((fr & VT_VALMASK) == VT_CONST && (fr & VT_SYM) &&
(fr & VT_LVAL) && !(sv->sym->type.t & VT_STATIC)) {
(fr & VT_LVAL) && !(sv->sym->type.t & VT_STATIC)
&& !(sv->sym->type.t & VT_TLS)) {
/* use the result register as a temporal register */
int tr = r | TREG_MEM;
if (is_float(ft)) {
@ -393,6 +386,19 @@ void load(int r, SValue *sv)
v = fr & VT_VALMASK;
if (fr & VT_LVAL) {
int b, ll;
if ((fr & VT_SYM) && sv->sym->type.t & VT_TLS) {
int dst_reg = REG_VALUE(r);
int is64 = is64_type(ft);
o(0x64); /* fs segment prefix */
if (is64 || REX_BASE(r))
o(0x40 | (REX_BASE(r) << 0) | (is64 << 3)); /* rex.w/rex.r */
o(0x8b); /* mov r/m, r */
o(0x04 | (dst_reg << 3)); /* modrm: [sib] | destreg */
o(0x25); /* sib: disp32 */
greloca(cur_text_section, sv->sym, ind, R_X86_64_TPOFF32, fc);
gen_le32(0);
return;
}
if (v == VT_LLOCAL) {
v1.type.t = VT_PTR;
v1.r = VT_LOCAL | VT_LVAL;
@ -444,8 +450,7 @@ void load(int r, SValue *sv)
b = 0xdb, r = 5; /* fldt */
} else if ((ft & VT_TYPE) == VT_BYTE || (ft & VT_TYPE) == VT_BOOL) {
b = 0xbe0f; /* movsbl */
} else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED) ||
(ft & VT_TYPE) == (VT_BOOL | VT_UNSIGNED)) {
} else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) {
b = 0xb60f; /* movzbl */
} else if ((ft & VT_TYPE) == VT_SHORT) {
b = 0xbf0f; /* movswl */
@ -540,8 +545,7 @@ void load(int r, SValue *sv)
o(0x44 + REG_VALUE(r)*8); /* %xmmN */
o(0xf024);
} else {
if (!nocode_wanted)
assert((v >= TREG_XMM0) && (v <= TREG_XMM7));
assert((v >= TREG_XMM0) && (v <= TREG_XMM7));
if ((ft & VT_BTYPE) == VT_FLOAT) {
o(0x100ff3);
} else {
@ -551,8 +555,7 @@ void load(int r, SValue *sv)
o(0xc0 + REG_VALUE(v) + REG_VALUE(r)*8);
}
} else if (r == TREG_ST0) {
if (!nocode_wanted)
assert((v >= TREG_XMM0) && (v <= TREG_XMM7));
assert((v >= TREG_XMM0) && (v <= TREG_XMM7));
/* gen_cvt_ftof(VT_LDOUBLE); */
/* movsd %xmmN,-0x10(%rsp) */
o(0x110ff2);
@ -583,6 +586,20 @@ void store(int r, SValue *v)
ft &= ~(VT_VOLATILE | VT_CONSTANT);
bt = ft & VT_BTYPE;
if ((v->r & VT_SYM) && v->sym->type.t & VT_TLS) {
int src_reg = REG_VALUE(r);
int is64 = is64_type(bt);
o(0x64);
if (is64 || REX_BASE(r))
o(0x40 | (REX_BASE(r) << 0) | (is64 << 3));
o(0x89);
o(0x04 | (src_reg << 3));
o(0x25);
greloca(cur_text_section, v->sym, ind, R_X86_64_TPOFF32, fc);
gen_le32(0);
return;
}
#ifndef TCC_TARGET_PE
/* we need to access the variable via got */
if (fr == VT_CONST
@ -757,7 +774,7 @@ static int arg_prepare_reg(int idx) {
static void gen_offs_sp(int b, int r, int d)
{
orex(1,0,r & 0x100 ? 0 : r, b);
if (d == (char)d) {
if (d == (signed char)d) {
o(0x2444 | (REG_VALUE(r) << 3));
g(d);
} else {
@ -1060,7 +1077,7 @@ void gfunc_epilog(void)
static void gadd_sp(int val)
{
if (val == (char)val) {
if (val == (signed char)val) {
o(0xc48348);
g(val);
} else {
@ -1643,7 +1660,7 @@ void gjmp_addr(int a)
{
int r;
r = a - ind - 2;
if (r == (char)r) {
if (r == (signed char)r) {
g(0xeb);
g(r);
} else {
@ -1712,7 +1729,7 @@ void gen_opi(int op)
r = gv(RC_INT);
vswap();
c = vtop->c.i;
if (c == (char)c) {
if (c == (signed char)c) {
/* XXX: generate inc and dec for smaller code ? */
orex(ll, r, 0, 0x83);
o(0xc0 | (opc << 3) | REG_VALUE(r));
@ -1840,6 +1857,7 @@ void gen_opf(int op)
o(0x80); /* xor $0x80, $n(rbp) */
gen_modrm(6, vtop->r, NULL, vtop->c.i + (bt == VT_DOUBLE ? 7 : 3));
o(0x80);
gv(float_type); /* -n is not a lvalue */
}
return;
}

View File

@ -13,7 +13,7 @@
#define R_NUM R_X86_64_NUM
#define ELF_START_ADDR 0x400000
#define ELF_PAGE_SIZE 0x200000
#define ELF_PAGE_SIZE 0x1000
#define PCRELATIVE_DLLPLT 1
#define RELOCATE_DLLPLT 1
@ -96,13 +96,15 @@ ST_FUNC int gotplt_entry_type (int reloc_type)
case R_X86_64_TLSGD:
case R_X86_64_TLSLD:
case R_X86_64_DTPOFF32:
case R_X86_64_TPOFF32:
case R_X86_64_DTPOFF64:
case R_X86_64_TPOFF64:
case R_X86_64_REX_GOTPCRELX:
case R_X86_64_PLT32:
case R_X86_64_PLTOFF64:
return ALWAYS_GOTPLT_ENTRY;
case R_X86_64_TPOFF32:
case R_X86_64_TPOFF64:
return NO_GOTPLT_ENTRY;
}
return -1;
@ -372,10 +374,30 @@ ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr,
ElfW(Sym) *sym;
Section *sec;
int32_t x;
addr_t tls_start = 0, tls_end = 0, tls_align = 1;
int i;
sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
sec = s1->sections[sym->st_shndx];
x = val - sec->sh_addr - sec->data_offset;
for (i = 1; i < s1->nb_sections; i++) {
Section *s = s1->sections[i];
if (s->sh_flags & SHF_TLS && s->sh_size) {
if (!tls_start || s->sh_addr < tls_start)
tls_start = s->sh_addr;
if (s->sh_addr + s->sh_size > tls_end)
tls_end = s->sh_addr + s->sh_size;
if (s->sh_addralign > tls_align)
tls_align = s->sh_addralign;
}
}
if (tls_end > tls_start) {
addr_t tls_size = tls_end - tls_start;
addr_t aligned_size = (tls_size + tls_align - 1) & ~(tls_align - 1);
x = val - (tls_start + aligned_size);
} else {
x = val - sec->sh_addr - sec->data_offset;
}
add32le(ptr, x);
}
break;
@ -385,10 +407,30 @@ ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr,
ElfW(Sym) *sym;
Section *sec;
int32_t x;
addr_t tls_start = 0, tls_end = 0, tls_align = 1;
int i;
sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
sec = s1->sections[sym->st_shndx];
x = val - sec->sh_addr - sec->data_offset;
for (i = 1; i < s1->nb_sections; i++) {
Section *s = s1->sections[i];
if (s->sh_flags & SHF_TLS && s->sh_size) {
if (!tls_start || s->sh_addr < tls_start)
tls_start = s->sh_addr;
if (s->sh_addr + s->sh_size > tls_end)
tls_end = s->sh_addr + s->sh_size;
if (s->sh_addralign > tls_align)
tls_align = s->sh_addralign;
}
}
if (tls_end > tls_start) {
addr_t tls_size = tls_end - tls_start;
addr_t aligned_size = (tls_size + tls_align - 1) & ~(tls_align - 1);
x = val - (tls_start + aligned_size);
} else {
x = val - sec->sh_addr - sec->data_offset;
}
add64le(ptr, x);
}
break;