diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 506b6b7c45..2b87a3a717 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,11 @@ jobs: steps: - uses: actions/checkout@v4 - name: make & test tcc (x86_64-linux) - run: ./configure && make && make test -k + run: | + ./configure + make + make test -k + make -C tests/tests2 tests2.112 tests2.128 test-x86_64-osx: runs-on: macos-15-intel @@ -27,7 +31,11 @@ jobs: steps: - uses: actions/checkout@v4 - name: make & test tcc (aarch64-osx) - run: ./configure && make && make test -k + run: | + ./configure + make + make test -k + make -C tests/tests2 tests2.133 test-x86_64-win32: runs-on: windows-2025 @@ -54,9 +62,44 @@ jobs: cd win32 call build-tcc.bat -t 64 -c cl echo ::endgroup:: + set TCC_RUN_ENV=ok + .\tcc -B. -run ..\win32\test_run_argv.c alpha "two words" beta > test_run_argv.out + fc /n test_run_argv.out ..\win32\test_run_argv.ref + .\tcc -B. -run ..\win32\test_run_env.c + > test_run_argv_wc_a.tmp echo a + > test_run_argv_wc_b.tmp echo b + .\tcc -B. -run ..\win32\test_run_argv.c test_run_argv_wc_*.tmp > test_run_argv_wc.out + findstr /c:"argc=3" test_run_argv_wc.out + findstr /c:"arg1=" test_run_argv_wc.out + findstr /c:"arg2=" test_run_argv_wc.out + .\tcc -B. -run ..\win32\test_run_wargv.c alpha "two words" beta > test_run_wargv.out + fc /n test_run_wargv.out ..\win32\test_run_wargv.ref + .\tcc -B. -run ..\win32\test_run_wenv.c + .\tcc -B. -run ..\win32\test_run_wargv.c test_run_wargv_wc_*.tmp > test_run_wargv_wc.out + findstr /c:"argc=3" test_run_wargv_wc.out + findstr /c:"arg1=" test_run_wargv_wc.out + findstr /c:"arg2=" test_run_wargv_wc.out + .\tcc -B. ..\win32\test_pe_field_alignment.c -o test_pe_field_alignment.exe && .\test_pe_field_alignment.exe > test_pe_field_alignment.out + fc /n test_pe_field_alignment.out ..\win32\test_pe_field_alignment.ref .\tcc -I.. libtcc.dll -v ../tests/libtcc_test.c -o libtest.exe && .\libtest.exe .\tcc -I.. libtcc.dll -run ../tests/libtcc_test.c + test-x86_64-win32-clang64: + runs-on: windows-2025 + timeout-minutes: 6 + steps: + - uses: actions/checkout@v4 + - name: make & test tcc (x86_64-win32-clang64) + shell: cmd + run: | + echo ::group:: setup msys clang64 + set MSYS2_PATH_TYPE=inherit + set MSYSTEM=CLANG64 + set CHERE_INVOKING=yes + C:\msys64\usr\bin\bash -l -c "pacman -S --noconfirm mingw-w64-clang-x86_64-clang" + echo ::endgroup:: + C:\msys64\usr\bin\bash -l -c "./configure --cc=clang && make && make -C tests test3" + test-i386-win32: runs-on: windows-2025 timeout-minutes: 6 @@ -100,7 +143,66 @@ jobs: cd win32 call build-tcc.bat -t arm64 -c clang echo ::endgroup:: - .\tcc -v + if not exist libtcc.dll exit /b 1 + set TCC_RUN_ENV=ok + .\tcc -B. -v + .\tcc -B. -run ..\win32\test_run_argv.c alpha "two words" beta > test_run_argv.out + fc /n test_run_argv.out ..\win32\test_run_argv.ref + .\tcc -B. -run ..\win32\test_run_env.c + .\tcc -B. -run ..\win32\test_run_wargv.c alpha "two words" beta > test_run_wargv.out + fc /n test_run_wargv.out ..\win32\test_run_wargv.ref + .\tcc -B. -run ..\win32\test_run_wenv.c + > test_run_argv_wc_a.tmp echo a + > test_run_argv_wc_b.tmp echo b + .\tcc -B. -run ..\win32\test_run_argv.c test_run_argv_wc_*.tmp > test_run_argv_wc.out + findstr /c:"argc=3" test_run_argv_wc.out + findstr /c:"arg1=" test_run_argv_wc.out + findstr /c:"arg2=" test_run_argv_wc.out + > test_run_wargv_wc_a.tmp echo a + > test_run_wargv_wc_b.tmp echo b + .\tcc -B. -run ..\win32\test_run_wargv.c test_run_wargv_wc_*.tmp > test_run_wargv_wc.out + findstr /c:"argc=3" test_run_wargv_wc.out + findstr /c:"arg1=" test_run_wargv_wc.out + findstr /c:"arg2=" test_run_wargv_wc.out + type ..\win32\test_run_stdin.ref | .\tcc -B. -run ..\win32\test_run_stdin.c > test_run_stdin.out + fc /n test_run_stdin.out ..\win32\test_run_stdin.ref + powershell -NoProfile -Command "$before = @(Get-ChildItem -Path $env:TEMP -Filter 'tcc*.tmp' -Name -ErrorAction SilentlyContinue); & .\tcc -B. -run ..\win32\test_run_exit.c; if ($LASTEXITCODE -ne 27) { exit 1 }; $after = @(Get-ChildItem -Path $env:TEMP -Filter 'tcc*.tmp' -Name -ErrorAction SilentlyContinue); if (Compare-Object $before $after) { Write-Host 'Temporary -run file cleanup mismatch'; Compare-Object $before $after; exit 1 }" + .\tcc -B. ..\win32\test_arm64.c -o test_arm64.exe && .\test_arm64.exe + .\tcc -B. -run ..\examples\ex1.c + .\tcc -B. -run ..\tcc.c -B. -run ..\tcc.c -B. -run ..\examples\ex1.c > test_nested_run.out + findstr /x /c:"Hello World" test_nested_run.out + clang -O0 -I.. ..\win32\test_arm64_libtcc_context.c ..\win32\test_arm64_libtcc_context.S -o test_libtcc_context.exe + .\test_libtcc_context.exe + .\tcc -B. ..\win32\test_arm64_inline_asm.c -o test_inline_asm.exe && .\test_inline_asm.exe > test_inline_asm.out + > test_inline_asm.expect echo inline asm ok + fc /n test_inline_asm.out test_inline_asm.expect + .\tcc -B. -DTEST_GOTO ..\win32\test_arm64_inline_asm.c -o test_inline_goto.exe && .\test_inline_goto.exe > test_inline_goto.out + > test_inline_goto.expect echo asm goto ok + fc /n test_inline_goto.out test_inline_goto.expect + .\tcc -B. -DTEST_OPERANDS ..\win32\test_arm64_inline_asm.c -o test_inline_operands.exe && .\test_inline_operands.exe > test_inline_operands.out + findstr /x /c:"2" test_inline_operands.out + .\tcc -B. -DTEST_CLOBBERS ..\win32\test_arm64_inline_asm.c -o test_inline_clobbers.exe && .\test_inline_clobbers.exe + > test_rstdin.txt echo arm64 stdin + .\tcc -B. -rstdin test_rstdin.txt -run ..\win32\test_rstdin.c > test_rstdin.out + type test_rstdin.out + fc /n test_rstdin.out test_rstdin.txt + .\tcc -B. ..\tests\tests2\49_bracket_evaluation.c -o test49.exe && .\test49.exe > test49.out + fc /n test49.out ..\tests\tests2\49_bracket_evaluation.expect + .\tcc -B. ..\tests\tests2\133_old_func.c -o test133.exe && .\test133.exe > test133.out + fc /n test133.out ..\tests\tests2\133_old_func.expect + .\tcc -B. -b ..\tests\tests2\115_bound_setjmp.c -o test115.exe && .\test115.exe > test115.out + for %%I in (test115.out) do if not %%~zI==0 ( type test115.out & exit /b 1 ) + .\tcc -B. -b ..\tests\tests2\116_bound_setjmp2.c -o test116.exe && .\test116.exe > test116.out + for %%I in (test116.out) do if not %%~zI==0 ( type test116.out & exit /b 1 ) + .\tcc -B. -b ..\win32\test_arm64_backtrace.c -o test_bt.exe + .\test_bt.exe > test_bt.out 2>&1 + if not errorlevel 1 exit /b 1 + type test_bt.out + findstr crash4 test_bt.out + findstr crash3 test_bt.out + findstr crash2 test_bt.out + findstr crash1 test_bt.out + findstr main test_bt.out test-armv7-linux: runs-on: ubuntu-22.04 @@ -136,7 +238,10 @@ jobs: apt-get install -q -y gcc make run: | echo "::endgroup::" # flatten 'run container' - ./configure && make && make test -k + ./configure + make + make test -k + make -C tests/tests2 tests2.73 tests2.109 tests2.121 tests2.133 test-riscv64-linux: runs-on: ubuntu-22.04 diff --git a/Makefile b/Makefile index ad518beca7..c48d084741 100644 --- a/Makefile +++ b/Makefile @@ -33,8 +33,9 @@ ifdef CONFIG_WIN32 ifneq ($(CONFIG_debug),yes) LDFLAGS += -s 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 ifneq ($(CONFIG_ldl),no) @@ -213,7 +214,7 @@ 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 diff --git a/arm64-asm.c b/arm64-asm.c index a97fd6420e..6bee988451 100644 --- a/arm64-asm.c +++ b/arm64-asm.c @@ -1,13 +1,17 @@ /*************************************************************/ /* - * ARM64 dummy assembler for TCC + * ARM64 (AArch64) assembler for TCC * + * Based on ARM64 Architecture Reference Manual + * Supports AArch64 assembler parsing and extended inline asm + * with operands, constraints, and clobbers. */ #ifdef TARGET_DEFS_ONLY #define CONFIG_TCC_ASM -#define NB_ASM_REGS 16 +/* 32 general purpose + 32 SIMD/FP registers */ +#define NB_ASM_REGS 64 ST_FUNC void g(int c); ST_FUNC void gen_le16(int c); @@ -16,13 +20,98 @@ ST_FUNC void gen_le32(int c); /*************************************************************/ #else /*************************************************************/ + #define USING_GLOBALS #include "tcc.h" -static void asm_error(void) -{ - tcc_error("ARM asm not implemented."); -} +/* Register type flags */ +#define REG_X 0x01 /* 64-bit general purpose */ +#define REG_W 0x02 /* 32-bit general purpose */ +#define REG_V 0x04 /* 128-bit SIMD */ +#define REG_D 0x08 /* 64-bit FP */ +#define REG_S 0x10 /* 32-bit FP */ +#define REG_H 0x20 /* 16-bit FP */ +#define REG_B 0x40 /* 8-bit SIMD */ + +/* Operand types */ +enum { + OPT_REG, + OPT_IM, + OPT_ADDR, + OPT_COND, +}; + +#define OP_REG (1 << OPT_REG) +#define OP_IM (1 << OPT_IM) +#define OP_ADDR (1 << OPT_ADDR) +#define OP_COND (1 << OPT_COND) + +/* Register allocation masks */ +#define REG_OUT_MASK 0x01 +#define REG_IN_MASK 0x02 + +#define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask) + +/* ARM64 register constants */ +#define TREG_X0 0 +#define TREG_X1 1 +#define TREG_X2 2 +#define TREG_X3 3 +#define TREG_X4 4 +#define TREG_X5 5 +#define TREG_X6 6 +#define TREG_X7 7 +#define TREG_X8 8 +#define TREG_X9 9 +#define TREG_X10 10 +#define TREG_X11 11 +#define TREG_X12 12 +#define TREG_X13 13 +#define TREG_X14 14 +#define TREG_X15 15 +#define TREG_X16 16 +#define TREG_X17 17 +#define TREG_X18 18 +#define TREG_X19 19 +#define TREG_X20 20 +#define TREG_X21 21 +#define TREG_X22 22 +#define TREG_X23 23 +#define TREG_X24 24 +#define TREG_X25 25 +#define TREG_X26 26 +#define TREG_X27 27 +#define TREG_X28 28 +#define TREG_X29 29 +#define TREG_X30 30 +#define TREG_SP 31 + +#ifdef TCC_TARGET_PE +#define ARM64_FREG_BASE 19 +#else +#define ARM64_FREG_BASE 20 +#endif +#define ARM64_FREG_LAST (ARM64_FREG_BASE + 7) + +typedef struct Operand { + uint32_t type; + int8_t reg; + int8_t reg2; + uint8_t reg_type; + uint8_t shift; + uint8_t addr_mode; + int reg_tok; + ExprValue e; +} Operand; + +enum { + ADDR_OFF, + ADDR_PRE, + ADDR_POST, +}; + +/* Forward declaration */ +static void parse_addr_operand(TCCState *s1, Operand *op); /* XXX: make it faster ? */ ST_FUNC void g(int c) @@ -37,16 +126,16 @@ ST_FUNC void g(int c) ind = ind1; } -ST_FUNC void gen_le16 (int i) +ST_FUNC void gen_le16(int c) { - g(i); - g(i>>8); + g(c); + g(c >> 8); } -ST_FUNC void gen_le32 (int i) +ST_FUNC void gen_le32(int c) { - gen_le16(i); - gen_le16(i>>16); + gen_le16(c); + gen_le16(c >> 16); } ST_FUNC void gen_expr32(ExprValue *pe) @@ -54,40 +143,2138 @@ ST_FUNC void gen_expr32(ExprValue *pe) gen_le32(pe->v); } +/* Emit 32-bit instruction */ +static void emit_instr32(uint32_t val) +{ + if (nocode_wanted) + return; + if (ind + 4 > cur_text_section->data_allocated) + section_realloc(cur_text_section, ind + 4); + write32le(cur_text_section->data + ind, val); + ind += 4; +} + +/* Parse ARM64 register token for assembler syntax. */ +static int arm64_parse_asm_reg(int t) +{ + /* X registers (64-bit) */ + if (t >= TOK_ASM_x0 && t <= TOK_ASM_x30) + return t - TOK_ASM_x0; + /* W registers (32-bit) */ + if (t >= TOK_ASM_w0 && t <= TOK_ASM_w30) + return t - TOK_ASM_w0; + /* V registers (128-bit SIMD) */ + if (t >= TOK_ASM_v0 && t <= TOK_ASM_v31) + return (t - TOK_ASM_v0) + 32; + /* D registers (64-bit FP) */ + if (t >= TOK_ASM_d0 && t <= TOK_ASM_d31) + return (t - TOK_ASM_d0) + 32; + /* S registers (32-bit FP) */ + if (t >= TOK_ASM_s0 && t <= TOK_ASM_s31) + return (t - TOK_ASM_s0) + 32; + /* H registers (16-bit FP) */ + if (t >= TOK_ASM_h0 && t <= TOK_ASM_h31) + return (t - TOK_ASM_h0) + 32; + /* B registers (8-bit SIMD) */ + if (t >= TOK_ASM_b0 && t <= TOK_ASM_b31) + return (t - TOK_ASM_b0) + 32; + /* Special registers */ + if (t == TOK_ASM_sp || t == TOK_ASM_xzr || t == TOK_ASM_wzr) + return 31; /* SP/ZR encoded as 31 */ + return -1; +} + +/* Get register type from token */ +static uint8_t get_reg_type(int t) +{ + if (t >= TOK_ASM_x0 && t <= TOK_ASM_x30) + return REG_X; + if (t >= TOK_ASM_w0 && t <= TOK_ASM_w30) + return REG_W; + if (t >= TOK_ASM_v0 && t <= TOK_ASM_v31) + return REG_V; + if (t >= TOK_ASM_d0 && t <= TOK_ASM_d31) + return REG_D; + if (t >= TOK_ASM_s0 && t <= TOK_ASM_s31) + return REG_S; + if (t >= TOK_ASM_h0 && t <= TOK_ASM_h31) + return REG_H; + if (t >= TOK_ASM_b0 && t <= TOK_ASM_b31) + return REG_B; + /* Special registers - sp is 64-bit, xzr is 64-bit, wzr is 32-bit */ + if (t == TOK_ASM_sp || t == TOK_ASM_xzr) + return REG_X; + if (t == TOK_ASM_wzr) + return REG_W; + return REG_X; +} + +/* Parse condition code */ +static int parse_condition(int t) +{ + switch (t) { + case TOK_ASM_eq: return 0; + case TOK_ASM_ne: return 1; + case TOK_ASM_cs: + case TOK_ASM_hs: return 2; + case TOK_ASM_cc: + case TOK_ASM_lo: return 3; + case TOK_ASM_mi: return 4; + case TOK_ASM_pl: return 5; + case TOK_ASM_vs: return 6; + case TOK_ASM_vc: return 7; + case TOK_ASM_hi: return 8; + case TOK_ASM_ls: return 9; + case TOK_ASM_ge: return 10; + case TOK_ASM_lt: return 11; + case TOK_ASM_gt: return 12; + case TOK_ASM_le: return 13; + case TOK_ASM_al: return 14; + default: return -1; + } +} + +static int parse_barrier_option_name(int t) +{ + const char *name; + + if (t < TOK_IDENT) + return -1; + name = get_tok_str(t, NULL); + if (!strcmp(name, "oshld")) return 0x1; + if (!strcmp(name, "oshst")) return 0x2; + if (!strcmp(name, "osh")) return 0x3; + if (!strcmp(name, "nshld")) return 0x5; + if (!strcmp(name, "nshst")) return 0x6; + if (!strcmp(name, "nsh")) return 0x7; + if (!strcmp(name, "ishld")) return 0x9; + if (!strcmp(name, "ishst")) return 0xA; + if (!strcmp(name, "ish")) return 0xB; + if (!strcmp(name, "ld")) return 0xD; + if (!strcmp(name, "st")) return 0xE; + if (!strcmp(name, "sy")) return 0xF; + return -1; +} + +static int arm64_parse_regvar(int t) +{ + if (t >= TOK_ASM_x0 && t <= TOK_ASM_x30) + return t - TOK_ASM_x0; + if (t >= TOK_ASM_v0 && t <= TOK_ASM_v7) + return ARM64_FREG_BASE + (t - TOK_ASM_v0); + if (t >= TOK_ASM_d0 && t <= TOK_ASM_d7) + return ARM64_FREG_BASE + (t - TOK_ASM_d0); + if (t >= TOK_ASM_s0 && t <= TOK_ASM_s7) + return ARM64_FREG_BASE + (t - TOK_ASM_s0); + if (t >= TOK_ASM_h0 && t <= TOK_ASM_h7) + return ARM64_FREG_BASE + (t - TOK_ASM_h0); + if (t >= TOK_ASM_b0 && t <= TOK_ASM_b7) + return ARM64_FREG_BASE + (t - TOK_ASM_b0); + return -1; +} + +/* Parse a single operand */ +static void parse_operand(TCCState *s1, Operand *op) +{ + int reg; + + op->type = 0; + op->reg = -1; + op->reg2 = -1; + op->reg_type = 0; + op->shift = 0; + op->addr_mode = ADDR_OFF; + op->reg_tok = 0; + + /* Address operand in brackets [xn, ...] */ + if (tok == '[') { + parse_addr_operand(s1, op); + return; + } + + /* Register */ + reg = arm64_parse_asm_reg(tok); + if (reg >= 0) { + op->type = OP_REG; + op->reg = reg; + op->reg_type = get_reg_type(tok); + op->reg_tok = tok; + next(); + return; + } + + /* Condition code */ + reg = parse_condition(tok); + if (reg >= 0) { + op->type = OP_COND; + op->reg = reg; + next(); + return; + } + + /* Immediate or address expression */ + if (tok == '#' || tok == ':' || tok == '@' || tok == '$') { + next(); + asm_expr(s1, &op->e); + op->type = OP_IM; + } else if (tok >= TOK_IDENT) { + tcc_error("invalid operand '%s'", get_tok_str(tok, &tokc)); + op->type = OP_IM; + } else { + asm_expr(s1, &op->e); + op->type = OP_IM; + } +} + +/* Parse a symbolic/immediate expression operand used by branch instructions. */ +static void parse_expr_operand(TCCState *s1, Operand *op) +{ + op->type = OP_IM; + op->reg = -1; + op->reg2 = -1; + op->reg_type = 0; + op->shift = 0; + op->addr_mode = ADDR_OFF; + op->reg_tok = 0; + + if (tok == '#' || tok == ':' || tok == '@' || tok == '$') + next(); + asm_expr(s1, &op->e); +} + +/* Parse address operand in brackets [xn, ...] */ +static void parse_addr_operand(TCCState *s1, Operand *op) +{ + int reg; + + op->type = OP_ADDR; + op->reg = -1; + op->reg2 = -1; + op->e.v = 0; + op->e.sym = NULL; + op->addr_mode = ADDR_OFF; + op->reg_tok = 0; + + skip('['); + reg = arm64_parse_asm_reg(tok); + if (reg < 0 || reg >= 32) { + tcc_error("invalid register in address operand"); + return; + } + op->reg = reg; + op->reg_tok = tok; + next(); + /* Check for offset */ + if (tok == ',') { + next(); + if (tok == '#' || tok == '@' || tok == '$') + next(); + asm_expr(s1, &op->e); + } + skip(']'); + if (tok == '!') { + op->addr_mode = ADDR_PRE; + next(); + } else if (tok == ',') { + op->addr_mode = ADDR_POST; + next(); + if (tok == '#' || tok == '@' || tok == '$') + next(); + asm_expr(s1, &op->e); + } +} + +/* Generate MOVZ instruction */ +/* Generate MOVZ/MOVN/MOVK with base opcode */ +static void gen_mov_with_base(int rd, uint16_t imm, int shift, + int is_64bit, uint32_t base_opcode) +{ + uint32_t instr = base_opcode; + if (is_64bit) instr |= ARM64_SF(1); + /* shift is halfword index (0-3), encode as LSL #0/16/32/48 */ + instr |= ARM64_IMM_HW(imm, shift); + instr |= ARM64_RD(rd); + emit_instr32(instr); +} + +static void gen_movz(int rd, uint16_t imm, int shift, int is_64bit) +{ + gen_mov_with_base(rd, imm, shift, is_64bit, ARM64_MOVZ); +} + +/* Generate MOVN instruction */ +static void gen_movn(int rd, uint16_t imm, int shift, int is_64bit) +{ + gen_mov_with_base(rd, imm, shift, is_64bit, ARM64_MOVN); +} + +/* Generate MOVK instruction */ +static void gen_movk(int rd, uint16_t imm, int shift, int is_64bit) +{ + gen_mov_with_base(rd, imm, shift, is_64bit, ARM64_MOVK); +} + +/* Generate ADD (immediate) */ +static void gen_add_imm(int rd, int rn, uint32_t imm, int is_64bit, int setflags) +{ + uint32_t instr = ARM64_ADD_IMM; + uint32_t imm12; + + if (is_64bit) instr |= ARM64_SF(1); + if (setflags) instr |= ARM64_S(1); + + if (imm <= 0xFFF) { + imm12 = imm; + } else if (!(imm & 0xFFF) && (imm >> 12) <= 0xFFF) { + instr |= ARM64_SH(1); + imm12 = imm >> 12; + } else { + tcc_error("add immediate out of range"); + return; + } + + instr |= ARM64_IMM12(imm12); + instr |= ARM64_RN(rn); + instr |= ARM64_RD(rd); + emit_instr32(instr); +} + +/* Generate SUB (immediate) */ +static void gen_sub_imm(int rd, int rn, uint32_t imm, int is_64bit, int setflags) +{ + uint32_t instr = ARM64_SUB_IMM; + uint32_t imm12; + + if (is_64bit) instr |= ARM64_SF(1); + if (setflags) instr |= ARM64_S(1); + + if (imm <= 0xFFF) { + imm12 = imm; + } else if (!(imm & 0xFFF) && (imm >> 12) <= 0xFFF) { + instr |= ARM64_SH(1); + imm12 = imm >> 12; + } else { + tcc_error("sub immediate out of range"); + return; + } + + instr |= ARM64_IMM12(imm12); + instr |= ARM64_RN(rn); + instr |= ARM64_RD(rd); + emit_instr32(instr); +} + +/* Generate data processing register instruction */ +static void gen_dp_reg(uint32_t opcode, int rd, int rn, int rm, int is_64bit) +{ + uint32_t instr = opcode; + if (is_64bit) instr |= ARM64_SF(1); + instr |= ARM64_RM(rm); + instr |= ARM64_RN(rn); + instr |= ARM64_RD(rd); + emit_instr32(instr); +} + +/* Generate LDR/STR (unsigned immediate) */ +static void gen_ldst_imm(uint32_t base_opcode, int rt, int rn, + int32_t offset, int size_log2) +{ + uint32_t instr = base_opcode; + uint32_t unscaled_opcode = 0; + uint32_t imm12; + + if (offset >= 0 && !(offset & ((1 << size_log2) - 1))) { + imm12 = offset >> size_log2; + if (imm12 <= 0xFFF) { + instr |= ARM64_IMM12(imm12); + instr |= ARM64_RN(rn); + instr |= ARM64_RT(rt); + emit_instr32(instr); + return; + } + } + + switch (base_opcode) { + case ARM64_LDR_X: unscaled_opcode = ARM64_LDUR_X; break; + case ARM64_LDR_W: unscaled_opcode = ARM64_LDUR_W; break; + case ARM64_LDR_B: unscaled_opcode = ARM64_LDUR_B; break; + case ARM64_LDR_H: unscaled_opcode = ARM64_LDUR_H; break; + case ARM64_LDR_D: unscaled_opcode = ARM64_LDUR_D_SIMD; break; + case ARM64_STR_X: unscaled_opcode = ARM64_STUR_X; break; + case ARM64_STR_W: unscaled_opcode = ARM64_STUR_W; break; + case ARM64_STR_B: unscaled_opcode = ARM64_STUR_B; break; + case ARM64_STR_H: unscaled_opcode = ARM64_STUR_H; break; + case ARM64_STR_D: unscaled_opcode = ARM64_STUR_D_SIMD; break; + } + + if (unscaled_opcode && offset >= -256 && offset <= 255) { + instr = unscaled_opcode; + instr |= ((uint32_t)offset & 0x1FFU) << 12; + instr |= ARM64_RN(rn); + instr |= ARM64_RT(rt); + emit_instr32(instr); + return; + } + + if (offset & ((1 << size_log2) - 1)) + tcc_error("invalid load/store offset"); + tcc_error("load/store offset out of range"); +} + +/* Generate STP/LDP (signed immediate) */ +static void gen_ldst_pair(uint32_t base_opcode, int rt, int rt2, int rn, + int32_t offset, int size_log2) +{ + int32_t imm7; + uint32_t instr = base_opcode; + + if (offset & ((1 << size_log2) - 1)) + tcc_error("invalid pair load/store offset"); + imm7 = offset >> size_log2; + if (imm7 < -64 || imm7 > 63) + tcc_error("pair load/store offset out of range"); + + instr |= ARM64_IMM7(imm7); + instr |= ARM64_RT2(rt2); + instr |= ARM64_RN(rn); + instr |= ARM64_RT(rt); + emit_instr32(instr); +} + +/* Generate B (branch) */ +/* Generate B/BL with base opcode */ +static void gen_b_or_bl(int32_t offset, uint32_t base_opcode) +{ + uint32_t instr = base_opcode; + instr |= ARM64_OFFSET26(offset); + emit_instr32(instr); +} + +static void gen_b(int32_t offset) +{ + gen_b_or_bl(offset, ARM64_B); +} + +/* Generate BL (branch with link) */ +static void gen_bl(int32_t offset) +{ + gen_b_or_bl(offset, ARM64_BL); +} + +/* Generate BR (branch to register) */ +static void gen_br(int rn) +{ + uint32_t instr = ARM64_BR; + instr |= ARM64_RN(rn); + emit_instr32(instr); +} + +/* Generate BLR (branch with link to register) */ +static void gen_blr(int rn) +{ + uint32_t instr = ARM64_BLR; + instr |= ARM64_RN(rn); + emit_instr32(instr); +} + +/* Generate RET */ +static void gen_ret(int rn) +{ + uint32_t instr = ARM64_RET; + instr |= ARM64_RN(rn); + emit_instr32(instr); +} + +/* Generate conditional branch */ +static void gen_b_cond(int cond, int32_t offset) +{ + uint32_t instr = ARM64_B_COND; + instr |= ARM64_OFFSET19(offset); + instr |= ARM64_COND(cond); + emit_instr32(instr); +} + +/* Generate CBZ */ +/* Generate CBZ/CBNZ with base opcode */ +static void gen_cbz_or_cbnz(int rt, int32_t offset, int is_64bit, uint32_t base_opcode) +{ + uint32_t instr = base_opcode; + if (is_64bit) instr |= ARM64_SF(1); + instr |= ARM64_OFFSET19(offset); + instr |= ARM64_RT(rt); + emit_instr32(instr); +} + +static void gen_cbz(int rt, int32_t offset, int is_64bit) +{ + gen_cbz_or_cbnz(rt, offset, is_64bit, ARM64_CBZ); +} + +/* Generate CBNZ */ +static void gen_cbnz(int rt, int32_t offset, int is_64bit) +{ + gen_cbz_or_cbnz(rt, offset, is_64bit, ARM64_CBNZ); +} + +/* Generate MOV (register) - ORR with zero register */ +static void gen_mov_reg(int rd, int rm, int is_64bit) +{ + uint32_t instr = ARM64_MOV_REG; + if (is_64bit) instr |= ARM64_SF(1); + instr |= ARM64_RM(rm); + instr |= ARM64_RD(rd); + emit_instr32(instr); +} + +static int arm64_asm_encode_bimm64(uint64_t x) +{ + int neg, rep, pos, len; + + neg = x & 1; + if (neg) + x = ~x; + if (!x) + return -1; + + if (x >> 2 == (x & ((((uint64_t)1) << (64 - 2)) - 1))) + rep = 2, x &= (((uint64_t)1) << 2) - 1; + else if (x >> 4 == (x & ((((uint64_t)1) << (64 - 4)) - 1))) + rep = 4, x &= (((uint64_t)1) << 4) - 1; + else if (x >> 8 == (x & ((((uint64_t)1) << (64 - 8)) - 1))) + rep = 8, x &= (((uint64_t)1) << 8) - 1; + else if (x >> 16 == (x & ((((uint64_t)1) << (64 - 16)) - 1))) + rep = 16, x &= (((uint64_t)1) << 16) - 1; + else if (x >> 32 == (x & ((((uint64_t)1) << (64 - 32)) - 1))) + rep = 32, x &= (((uint64_t)1) << 32) - 1; + else + rep = 64; + + pos = 0; + if (!(x & ((((uint64_t)1) << 32) - 1))) + x >>= 32, pos += 32; + if (!(x & ((((uint64_t)1) << 16) - 1))) + x >>= 16, pos += 16; + if (!(x & ((((uint64_t)1) << 8) - 1))) + x >>= 8, pos += 8; + if (!(x & ((((uint64_t)1) << 4) - 1))) + x >>= 4, pos += 4; + if (!(x & ((((uint64_t)1) << 2) - 1))) + x >>= 2, pos += 2; + if (!(x & ((((uint64_t)1) << 1) - 1))) + x >>= 1, pos += 1; + + len = 0; + if (!(~x & ((((uint64_t)1) << 32) - 1))) + x >>= 32, len += 32; + if (!(~x & ((((uint64_t)1) << 16) - 1))) + x >>= 16, len += 16; + if (!(~x & ((((uint64_t)1) << 8) - 1))) + x >>= 8, len += 8; + if (!(~x & ((((uint64_t)1) << 4) - 1))) + x >>= 4, len += 4; + if (!(~x & ((((uint64_t)1) << 2) - 1))) + x >>= 2, len += 2; + if (!(~x & ((((uint64_t)1) << 1) - 1))) + x >>= 1, len += 1; + + if (x) + return -1; + if (neg) { + pos = (pos + len) & (rep - 1); + len = rep - len; + } + return ((0x1000 & rep << 6) | (((rep - 1) ^ 31) << 1 & 63) | + ((rep - pos) & (rep - 1)) << 6 | (len - 1)); +} + +static void gen_logical_imm(uint32_t opcode, int rd, int rn, + uint64_t imm, int is_64bit) +{ + uint32_t instr; + uint64_t val; + int enc; + + if (is_64bit) + val = imm; + else { + val = (uint32_t)imm; + val |= val << 32; + } + + enc = arm64_asm_encode_bimm64(val); + if (enc < 0) { + tcc_error("logical immediate out of range"); + return; + } + + instr = opcode; + if (is_64bit) + instr |= ARM64_SF(1); + instr |= ARM64_N(enc >> 12); + instr |= ARM64_IMM_R(enc >> 6); + instr |= ARM64_IMM_S(enc); + instr |= ARM64_RN(rn); + instr |= ARM64_RD(rd); + emit_instr32(instr); +} + +/* return the constraint priority (we allocate first the lowest + numbered constraints) */ +static inline int constraint_priority(const char *str) +{ + int priority, c, pr; + + priority = 0; + for (;;) { + c = *str++; + if (c == '\0') + break; + switch (c) { + case '=': + case '+': + case '&': + continue; + case 'r': + pr = 1; + break; + case 'w': + case 'f': + case 'x': + case 'y': + pr = 3; + break; + case 'm': + case 'Q': + pr = 4; + break; + case 'i': + case 'S': + pr = 5; + break; + case 'U': + if (str[0] == 'm' && str[1] == 'p') { + str += 2; + pr = 4; + break; + } + tcc_warning("unknown constraint '%c'", c); + pr = 0; + break; + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'Z': + pr = 6; + break; + case 'n': + pr = 7; + break; + case 'g': + pr = 8; + break; + default: + tcc_warning("unknown constraint '%c'", c); + pr = 0; + break; + } + if (pr > priority) + priority = pr; + } + return priority; +} + +static const char *skip_constraint_modifiers(const char *p) +{ + while (*p == '=' || *p == '&' || *p == '+' || *p == '%') + p++; + return p; +} + +static int is_valid_add_imm(int64_t val) +{ + return val >= 0 && val <= 4095; +} + +static int is_valid_logical_imm(int64_t val, int bits) +{ + uint64_t uval; + + uval = (uint64_t)val; + if (bits == 32) { + uval = (uint32_t)uval; + uval |= uval << 32; + } + return arm64_asm_encode_bimm64(uval) >= 0; +} + +static int is_valid_movw_imm(int64_t val) +{ + uint64_t uval = (uint64_t)val; + + if (uval <= 0xFFFF) + return 1; + if (uval >= 0xFFFF0000 && (uval & 0xFFFF) == 0) + return 1; + if (uval >= 0xFFFF00000000ULL && (uval & 0xFFFFFFFFULL) == 0) + return 1; + if ((uval & 0xFFFFFFFF00000000ULL) == 0) + return 1; + + return 0; +} + +static int is_valid_movw_shift(int shift, int is_64bit) +{ + if (shift < 0 || (shift & 15)) + return 0; + if (shift > (is_64bit ? 48 : 16)) + return 0; + return 1; +} + +static int arm64_memory_is_base_only(const SValue *sv) +{ + int r; + + r = sv->r; + if ((r & VT_VALMASK) == VT_CONST) + return 0; + if ((r & VT_VALMASK) == VT_LOCAL) + return 0; + if ((r & VT_VALMASK) == VT_LLOCAL) + return 1; + if (r & VT_LVAL) + return (r & VT_VALMASK) < VT_CONST; + return 0; +} + +static int arm64_memory_is_pair_suitable(const SValue *sv) +{ + int64_t offset; + + if (arm64_memory_is_base_only(sv)) + return 1; + if ((sv->r & VT_VALMASK) != VT_LOCAL) + return 0; + + offset = (int64_t)sv->c.i; + return (offset & 7) == 0 && offset >= -512 && offset <= 504; +} + +static int arm64_int_reg_is_allocatable(int reg) +{ +#ifdef TCC_TARGET_PE + return reg >= TREG_X0 && reg <= TREG_X17; +#else + return reg >= TREG_X0 && reg <= TREG_X30; +#endif +} + +static int arm64_memory_needs_address_reg(const SValue *sv) +{ + int r; + + r = sv->r & ~(VT_BOUNDED | VT_NONCONST); + if (!(r & VT_LVAL)) + return 0; + switch (r & VT_VALMASK) { + case VT_LOCAL: + case VT_LLOCAL: + case VT_CONST: + return 1; + } + return 0; +} + +static int arm64_prepare_memory_operand(ASMOperand *op, uint8_t *regs_allocated) +{ + int reg; + + if (!arm64_memory_needs_address_reg(op->vt)) + return 1; + + for (reg = 0; reg < 31; reg++) { + if (arm64_int_reg_is_allocatable(reg) + && !(regs_allocated[reg] & REG_IN_MASK)) { + regs_allocated[reg] |= REG_IN_MASK; + op->reg = reg; + op->is_memory = 1; + return 1; + } + } + return 0; +} + +static void arm64_load_memory_operand_base(int reg, SValue *sv) +{ + SValue base; + int rval; + + base = *sv; + base.type.t = VT_PTR; + rval = base.r & VT_VALMASK; + if (rval == VT_LLOCAL) { + base.r = (base.r & ~VT_VALMASK) | VT_LOCAL | VT_LVAL; + } else if (rval == VT_CONST || rval == VT_LOCAL) { + base.r &= ~VT_LVAL; + } else { + tcc_internal_error("unsupported ARM64 memory operand base"); + } + load(reg, &base); +} + +static int operand_is_sp(const Operand *op) +{ + return op->reg_tok == TOK_ASM_sp; +} + +static int parse_sysreg_name(int t) +{ + const char *name; + + if (t < TOK_IDENT) + return -1; + name = get_tok_str(t, NULL); + if (!strcmp(name, "FPCR") || !strcmp(name, "fpcr")) + return 0; + if (!strcmp(name, "FPSR") || !strcmp(name, "fpsr")) + return 1; + return -1; +} + +static void gen_mrs(int rt, int sysreg) +{ + uint32_t instr; + + switch (sysreg) { + case 0: /* FPCR */ + instr = ARM64_MRS_FPCR; + break; + case 1: /* FPSR */ + instr = ARM64_MRS_FPSR; + break; + default: + tcc_error("unsupported system register"); + return; + } + emit_instr32(instr | ARM64_RD(rt)); +} + +static void gen_msr(int rt, int sysreg) +{ + uint32_t instr; + + switch (sysreg) { + case 0: /* FPCR */ + instr = ARM64_MSR_FPCR; + break; + case 1: /* FPSR */ + instr = ARM64_MSR_FPSR; + break; + default: + tcc_error("unsupported system register"); + return; + } + emit_instr32(instr | ARM64_RD(rt)); +} + +/* Generate NOP */ +static void gen_nop(void) +{ + emit_instr32(ARM64_NOP); +} + +/* Generate shift operations (LSL, LSR, ASR, ROR) */ +static void gen_shift(int rd, int rn, int rm_or_imm, int shift_type, int is_imm, int is_64bit) +{ + uint32_t instr; + int width = is_64bit ? 64 : 32; + + if (is_imm) { + /* Shift by immediate */ + switch (shift_type) { + case 0: /* LSL - UBFM alias */ + if (rm_or_imm < 0 || rm_or_imm >= width) { + tcc_error("shift immediate out of range"); + return; + } + instr = is_64bit ? ARM64_LSL_IMM : ARM64_LSR_IMM_32; + instr |= ARM64_IMM_R((width - rm_or_imm) & (width - 1)); + instr |= ARM64_IMM_S(width - rm_or_imm - 1); + break; + case 1: /* LSR - UBFM alias: immr = shift, imms = width - 1 */ + if (rm_or_imm < 0 || rm_or_imm >= width) { + tcc_error("shift immediate out of range"); + return; + } + instr = is_64bit ? ARM64_LSL_IMM : ARM64_LSR_IMM_32; + instr |= ARM64_IMM_R(rm_or_imm); + instr |= ARM64_IMM_S(width - 1); + break; + case 2: /* ASR - SBFM alias: immr = shift, imms = width - 1 */ + if (rm_or_imm < 0 || rm_or_imm >= width) { + tcc_error("shift immediate out of range"); + return; + } + instr = is_64bit ? ARM64_ASR_IMM : (ARM64_ASR_IMM & ~(1U << 31)); + instr |= ARM64_IMM_R(rm_or_imm); + instr |= ARM64_IMM_S(width - 1); + break; + case 3: /* ROR - EXTR alias: Rm = source, Rn = source, imms = shift */ + if (rm_or_imm < 0 || rm_or_imm >= width) { + tcc_error("shift immediate out of range"); + return; + } + instr = is_64bit ? ARM64_EXTR64 : ARM64_EXTR; + instr |= ARM64_RM(rn); + instr |= ARM64_IMM_S(rm_or_imm); + instr |= ARM64_RN(rn); + instr |= ARM64_RD(rd); + emit_instr32(instr); + return; + default: + tcc_error("unknown shift type"); + return; + } + instr |= ARM64_RN(rn); + instr |= ARM64_RD(rd); + } else { + /* Shift by register */ + switch (shift_type) { + case 0: /* LSL */ + instr = ARM64_LSL_REG; + break; + case 1: /* LSR */ + instr = ARM64_LSR_REG; + break; + case 2: /* ASR */ + instr = ARM64_ASR_REG; + break; + case 3: /* ROR */ + instr = ARM64_ROR_REG; + break; + default: + tcc_error("unknown shift type"); + return; + } + if (is_64bit) + instr |= ARM64_SF(1); + instr |= ARM64_RM(rm_or_imm); + instr |= ARM64_RN(rn); + instr |= ARM64_RD(rd); + } + emit_instr32(instr); +} + +/* Handle shift instructions */ +static void asm_shift(TCCState *s1, int token) +{ + Operand op1, op2, op3; + int rd, rn, shift_amount; + int shift_type; + int is_64bit = 1; + + switch (token) { + case TOK_ASM_lsl: + shift_type = 0; + break; + case TOK_ASM_lsr: + shift_type = 1; + break; + case TOK_ASM_asr: + shift_type = 2; + break; + case TOK_ASM_ror: + shift_type = 3; + break; + default: + tcc_error("unknown shift instruction"); + return; + } + + parse_operand(s1, &op1); + if (tok == ',') next(); + parse_operand(s1, &op2); + + if (!(op1.type & OP_REG)) { + tcc_error("expected register in first operand"); + return; + } + if (!(op2.type & OP_REG)) { + tcc_error("expected register in second operand"); + return; + } + + rd = op1.reg; + rn = op2.reg; + + if (tok == ',') { + next(); + parse_operand(s1, &op3); + is_64bit = (op1.reg_type & REG_X); + if (is_64bit != !!(op2.reg_type & REG_X)) { + tcc_error("mismatched register widths"); + return; + } + if (op3.type & OP_REG) { + if (is_64bit != !!(op3.reg_type & REG_X)) { + tcc_error("mismatched register widths"); + return; + } + gen_shift(rd, rn, op3.reg, shift_type, 0, is_64bit); + } else if (op3.type & OP_IM) { + shift_amount = op3.e.v; + gen_shift(rd, rn, shift_amount, shift_type, 1, is_64bit); + } else { + tcc_error("shift requires immediate or register operand"); + } + } else { + tcc_error("shift requires immediate or register operand"); + return; + } +} + +/* Generate barrier instructions (ISB, DSB, DMB) */ +static void gen_barrier(int barrier_type, int option) +{ + uint32_t instr; + + switch (barrier_type) { + case 0: /* ISB - Instruction Synchronization Barrier */ + instr = ARM64_ISB; + break; + case 1: /* DSB - Data Synchronization Barrier */ + instr = ARM64_DSB; + break; + case 2: /* DMB - Data Memory Barrier */ + instr = ARM64_DMB; + break; + default: + tcc_error("unknown barrier type"); + return; + } + instr |= ARM64_ISB_OPTION(option); + emit_instr32(instr); +} + +/* Handle barrier instructions */ +static void asm_barrier(TCCState *s1, int token) +{ + int barrier_type, option; + Operand op; + + switch (token) { + case TOK_ASM_isb: + barrier_type = 0; + break; + case TOK_ASM_dsb: + barrier_type = 1; + break; + case TOK_ASM_dmb: + barrier_type = 2; + break; + default: + tcc_error("unknown barrier instruction"); + return; + } + + /* Default option = sy/full system. */ + option = 0xF; + + /* Check for an optional named or numeric barrier scope. */ + if (tok != TOK_LINEFEED) { + option = parse_barrier_option_name(tok); + if (option >= 0) { + next(); + } else { + parse_operand(s1, &op); + if (!(op.type & OP_IM) || op.e.sym) { + tcc_error("barrier option must be an immediate or scope name"); + return; + } + if (op.e.v > 0xF) { + tcc_error("barrier option out of range"); + return; + } + option = op.e.v; + } + } + + gen_barrier(barrier_type, option); +} + +/* Generate immediate move sequence */ +static void gen_mov_imm(int rd, uint64_t imm, int is_64bit) +{ + uint16_t hw; + int i, first = 1; + + for (i = 0; i < (is_64bit ? 4 : 2); i++) { + hw = (imm >> (i * 16)) & 0xFFFF; + if (hw != 0 || i == 0) { + if (first) { + /* Pass halfword index (0-3), not bit count */ + gen_movz(rd, hw, i, is_64bit); + first = 0; + } else { + gen_movk(rd, hw, i, is_64bit); + } + } else if (!first) { + gen_movk(rd, hw, i, is_64bit); + } + } +} + +/* Handle mov instruction */ +static void asm_mov(TCCState *s1) +{ + Operand op1, op2; + int rd, rn; + int is_64bit; + + parse_operand(s1, &op1); + if (tok == ',') next(); + parse_operand(s1, &op2); + rd = op1.reg; + is_64bit = (op1.reg_type & REG_X); + + if (op2.type & OP_IM) { + /* Handle immediate: mov x0, #123 */ + if (operand_is_sp(&op1)) { + tcc_error("cannot move an immediate into sp"); + return; + } + gen_mov_imm(rd, op2.e.v, is_64bit); + } else if (op2.type & OP_REG) { + /* Handle register: mov x0, x1 */ + rn = op2.reg; + if (operand_is_sp(&op1) || operand_is_sp(&op2)) + gen_add_imm(rd, rn, 0, 1, 0); + else + gen_mov_reg(rd, rn, is_64bit); + } else { + tcc_error("invalid operand for mov"); + } +} + +/* Handle data processing instructions */ +static void asm_data_proc(TCCState *s1, int token) +{ + Operand op1, op2, op3; + int rd, rn, rm; + int is_64bit = 1; + uint32_t opcode; + + switch (token) { + case TOK_ASM_add: + case TOK_ASM_adds: + opcode = token == TOK_ASM_add ? ARM64_ADD_REG : ARM64_ADDS_REG; + break; + case TOK_ASM_sub: + case TOK_ASM_subs: + opcode = token == TOK_ASM_sub ? ARM64_SUB_REG : ARM64_SUBS_REG; + break; + case TOK_ASM_and: + case TOK_ASM_ands: + opcode = token == TOK_ASM_and ? ARM64_AND_REG : ARM64_ANDS_REG; + break; + case TOK_ASM_orr: + opcode = ARM64_ORR_REG; + break; + case TOK_ASM_eor: + opcode = ARM64_EOR_REG; + break; + case TOK_ASM_mul: + opcode = ARM64_MUL_REG; + break; + default: + tcc_error("unsupported data processing instruction"); + return; + } + + parse_operand(s1, &op1); + if (tok == ',') next(); + parse_operand(s1, &op2); + + if (!(op1.type & OP_REG)) { + tcc_error("expected register in first operand"); + return; + } + if (!(op2.type & OP_REG)) { + tcc_error("expected register in second operand"); + return; + } + + rd = op1.reg; + rn = op2.reg; + + if (tok == ',') { + next(); + parse_operand(s1, &op3); + is_64bit = (op1.reg_type & REG_X); + if (is_64bit != !!(op2.reg_type & REG_X)) { + tcc_error("mismatched register widths"); + return; + } + if (op3.type & OP_IM) { + if (token == TOK_ASM_add || token == TOK_ASM_adds) + gen_add_imm(rd, rn, op3.e.v, is_64bit, + token == TOK_ASM_adds); + else if (token == TOK_ASM_sub || token == TOK_ASM_subs) + gen_sub_imm(rd, rn, op3.e.v, is_64bit, + token == TOK_ASM_subs); + else if (token == TOK_ASM_and) + gen_logical_imm(ARM64_AND_IMM, rd, rn, op3.e.v, is_64bit); + else if (token == TOK_ASM_ands) + gen_logical_imm(ARM64_ANDS_IMM, rd, rn, op3.e.v, is_64bit); + else if (token == TOK_ASM_orr) + gen_logical_imm(ARM64_ORR_IMM_BASE, rd, rn, op3.e.v, is_64bit); + else if (token == TOK_ASM_eor) + gen_logical_imm(ARM64_EOR_IMM, rd, rn, op3.e.v, is_64bit); + else + tcc_error("immediate operand not valid for this instruction"); + } else { + if (!(op3.type & OP_REG)) { + tcc_error("expected register in third operand"); + return; + } + rm = op3.reg; + if (is_64bit != !!(op2.reg_type & REG_X) || is_64bit != !!(op3.reg_type & REG_X)) { + tcc_error("mismatched register widths"); + return; + } + gen_dp_reg(opcode, rd, rn, rm, is_64bit); + } + } else if (op2.type & OP_IM) { + tcc_error("missing source register for immediate form"); + } else { + tcc_error("missing third operand"); + } +} + +/* Handle load/store instructions */ +static void asm_ldst(TCCState *s1, int token) +{ + Operand op1, op2; + int rt, rn; + int32_t offset = 0; + int size_log2 = 3; + uint32_t base_opcode; + + parse_operand(s1, &op1); + if (tok == ',') next(); + parse_operand(s1, &op2); + + if (!(op1.type & OP_REG)) { + tcc_error("expected register in first operand"); + return; + } + if (op2.type != OP_ADDR) { + tcc_error("expected address operand in second operand"); + return; + } + + rt = op1.reg; + rn = op2.reg; + offset = op2.e.v; + + switch (token) { + case TOK_ASM_ldr: + if (op1.reg_type & REG_X) { + base_opcode = ARM64_LDR_X; + size_log2 = 3; + } else if (op1.reg_type & REG_W) { + base_opcode = ARM64_LDR_W; + size_log2 = 2; + } else if (op1.reg_type & REG_D) { + base_opcode = ARM64_LDR_D; + size_log2 = 3; + } else { + tcc_error("ldr requires a w, x, or d register"); + return; + } + break; + case TOK_ASM_ldrb: + base_opcode = ARM64_LDR_B; + size_log2 = 0; + break; + case TOK_ASM_ldrh: + base_opcode = ARM64_LDR_H; + size_log2 = 1; + break; + case TOK_ASM_str: + if (op1.reg_type & REG_X) { + base_opcode = ARM64_STR_X; + size_log2 = 3; + } else if (op1.reg_type & REG_W) { + base_opcode = ARM64_STR_W; + size_log2 = 2; + } else if (op1.reg_type & REG_D) { + base_opcode = ARM64_STR_D; + size_log2 = 3; + } else { + tcc_error("str requires a w, x, or d register"); + return; + } + break; + case TOK_ASM_strb: + base_opcode = ARM64_STR_B; + size_log2 = 0; + break; + case TOK_ASM_strh: + base_opcode = ARM64_STR_H; + size_log2 = 1; + break; + default: + tcc_error("unsupported load/store instruction"); + return; + } + + if (op2.addr_mode != ADDR_OFF) + tcc_error("only offset addressing is implemented for ldr/str"); + gen_ldst_imm(base_opcode, rt, rn, offset, size_log2); +} + +static void asm_ldst_pair(TCCState *s1, int token) +{ + Operand op1, op2, op3; + uint32_t base_opcode; + int size_log2 = 3; + + parse_operand(s1, &op1); + if (tok == ',') + next(); + parse_operand(s1, &op2); + if (tok == ',') + next(); + parse_operand(s1, &op3); + + if (!(op1.type & OP_REG)) { + tcc_error("expected register in first operand"); + return; + } + if (!(op2.type & OP_REG)) { + tcc_error("expected register in second operand"); + return; + } + if (!(op3.type & OP_ADDR)) + tcc_error("pair load/store requires an address operand"); + + if ((op1.reg_type & REG_X) && (op2.reg_type & REG_X)) { + if (token == TOK_ASM_stp) { + base_opcode = op3.addr_mode == ADDR_PRE ? ARM64_STP_X_PRE : + op3.addr_mode == ADDR_POST ? ARM64_STP_X_POST : + ARM64_STP_X; + } else { + base_opcode = op3.addr_mode == ADDR_PRE ? ARM64_LDP_X_PRE : + op3.addr_mode == ADDR_POST ? ARM64_LDP_X_POST : + ARM64_LDP_X; + } + } else if ((op1.reg_type & REG_D) && (op2.reg_type & REG_D)) { + if (token == TOK_ASM_stp) { + base_opcode = op3.addr_mode == ADDR_PRE ? ARM64_STP_D_PRE : + op3.addr_mode == ADDR_POST ? ARM64_STP_D_POST : + ARM64_STP_D; + } else { + base_opcode = op3.addr_mode == ADDR_PRE ? ARM64_LDP_D_PRE : + op3.addr_mode == ADDR_POST ? ARM64_LDP_D_POST : + ARM64_LDP_D; + } + } else { + tcc_error("stp/ldp requires matching x or d registers"); + return; + } + + gen_ldst_pair(base_opcode, op1.reg, op2.reg, op3.reg, op3.e.v, size_log2); +} + +static void asm_sysreg(TCCState *s1, int token) +{ + Operand op; + int sysreg; + + if (token == TOK_ASM_mrs) { + parse_operand(s1, &op); + if (tok == ',') + next(); + sysreg = parse_sysreg_name(tok); + if (sysreg < 0) + tcc_error("unsupported system register"); + next(); + gen_mrs(op.reg, sysreg); + return; + } + + sysreg = parse_sysreg_name(tok); + if (sysreg < 0) + tcc_error("unsupported system register"); + next(); + if (tok == ',') + next(); + parse_operand(s1, &op); + gen_msr(op.reg, sysreg); +} + +/* Get condition code from branch instruction token */ +static int get_branch_condition(int branch_token) +{ + int cond_token; + + /* Map branch token to condition token (strip 'b' prefix) */ + switch (branch_token) { + case TOK_ASM_beq: cond_token = TOK_ASM_eq; break; + case TOK_ASM_bne: cond_token = TOK_ASM_ne; break; + case TOK_ASM_bcs: + case TOK_ASM_bhs: cond_token = TOK_ASM_cs; break; + case TOK_ASM_bcc: + case TOK_ASM_blo: cond_token = TOK_ASM_cc; break; + case TOK_ASM_bmi: cond_token = TOK_ASM_mi; break; + case TOK_ASM_bpl: cond_token = TOK_ASM_pl; break; + case TOK_ASM_bvs: cond_token = TOK_ASM_vs; break; + case TOK_ASM_bvc: cond_token = TOK_ASM_vc; break; + case TOK_ASM_bhi: cond_token = TOK_ASM_hi; break; + case TOK_ASM_bls: cond_token = TOK_ASM_ls; break; + case TOK_ASM_bge: cond_token = TOK_ASM_ge; break; + case TOK_ASM_blt: cond_token = TOK_ASM_lt; break; + case TOK_ASM_bgt: cond_token = TOK_ASM_gt; break; + case TOK_ASM_ble: cond_token = TOK_ASM_le; break; + default: return -1; + } + + return parse_condition(cond_token); +} + +/* Handle branch instructions */ +static void asm_branch(TCCState *s1, int token) +{ + Operand op; + int cond; + Sym *sym; + int32_t offset; + + /* ret can be used without operand */ + if (token == TOK_ASM_ret && (tok == TOK_LINEFEED || tok == ';' || tok == TOK_EOF)) { + gen_ret(30); /* x30 is the link register */ + return; + } + + if (token == TOK_ASM_br || token == TOK_ASM_blr || token == TOK_ASM_ret) + parse_operand(s1, &op); + else + parse_expr_operand(s1, &op); + + if (op.type & OP_IM) { + sym = op.e.sym; + if (sym) { + /* Symbolic address - emit relocation */ + offset = 0; + + cond = get_branch_condition(token); + if (cond >= 0) { + /* Conditional branch - use CONDBR19 relocation */ + gen_b_cond(cond, 0); + greloca(cur_text_section, sym, ind - 4, R_AARCH64_CONDBR19, 0); + } else { + switch (token) { + case TOK_ASM_b: + gen_b(0); + greloca(cur_text_section, sym, ind - 4, R_AARCH64_JUMP26, 0); + break; + case TOK_ASM_bl: + gen_bl(0); + greloca(cur_text_section, sym, ind - 4, R_AARCH64_CALL26, 0); + break; + default: + tcc_error("unsupported branch"); + } + } + } else { + offset = (int32_t)op.e.v - ind; + + cond = get_branch_condition(token); + if (cond >= 0) { + gen_b_cond(cond, offset); + } else { + switch (token) { + case TOK_ASM_b: + gen_b(offset); + break; + case TOK_ASM_bl: + gen_bl(offset); + break; + default: + tcc_error("unsupported branch"); + } + } + } + } else if (op.type & OP_REG) { + switch (token) { + case TOK_ASM_br: + gen_br(op.reg); + break; + case TOK_ASM_blr: + gen_blr(op.reg); + break; + case TOK_ASM_ret: + gen_ret(op.reg); + break; + default: + tcc_error("register branch not valid"); + } + } +} + +/* Handle CBZ/CBNZ */ +static void asm_cb(TCCState *s1, int token) +{ + Operand op1, op2; + int rt, is_64bit; + int32_t offset; + Sym *sym; + + parse_operand(s1, &op1); + if (tok == ',') next(); + parse_expr_operand(s1, &op2); + + rt = op1.reg; + is_64bit = (op1.reg_type & REG_X); + sym = op2.e.sym; + + if (sym) { + /* Symbolic address - emit relocation */ + offset = 0; + if (token == TOK_ASM_cbz) { + gen_cbz(rt, offset, is_64bit); + greloca(cur_text_section, sym, ind - 4, R_AARCH64_CONDBR19, 0); + } else { + gen_cbnz(rt, offset, is_64bit); + greloca(cur_text_section, sym, ind - 4, R_AARCH64_CONDBR19, 0); + } + } else { + offset = (int32_t)op2.e.v - ind; + if (token == TOK_ASM_cbz) + gen_cbz(rt, offset, is_64bit); + else + gen_cbnz(rt, offset, is_64bit); + } +} + +/* Handle MOVZ/MOVN/MOVK */ +static void asm_move_wide(TCCState *s1, int token) +{ + Operand op1, op2; + int rd, is_64bit = 1; + uint16_t imm; + int shift = 0; + + parse_operand(s1, &op1); + if (tok == ',') next(); + parse_operand(s1, &op2); + + if (!(op1.type & OP_REG)) { + tcc_error("expected register in first operand"); + return; + } + if (!(op2.type & OP_IM) || op2.e.sym) { + tcc_error("expected immediate in second operand"); + return; + } + + rd = op1.reg; + is_64bit = (op1.reg_type & REG_X); + if ((uint64_t)op2.e.v > 0xFFFF) { + tcc_error("move wide immediate out of range"); + return; + } + imm = op2.e.v; + + if (tok == ',') { + next(); + if (tok != TOK_ASM_lsl) { + tcc_error("move wide shift must use lsl"); + return; + } + next(); + if (tok == '#') next(); + asm_expr(s1, &op2.e); + if (op2.e.sym || !is_valid_movw_shift((int)op2.e.v, is_64bit)) { + tcc_error("move wide shift out of range"); + return; + } + shift = (int)op2.e.v / 16; + } + + switch (token) { + case TOK_ASM_movz: + gen_movz(rd, imm, shift, is_64bit); + break; + case TOK_ASM_movn: + gen_movn(rd, imm, shift, is_64bit); + break; + case TOK_ASM_movk: + gen_movk(rd, imm, shift, is_64bit); + break; + default: + tcc_error("unknown move wide instruction"); + } +} + +/* Main assembler opcode dispatcher */ ST_FUNC void asm_opcode(TCCState *s1, int opcode) { - asm_error(); + switch (opcode) { + case TOK_ASM_add: + case TOK_ASM_adds: + case TOK_ASM_sub: + case TOK_ASM_subs: + case TOK_ASM_and: + case TOK_ASM_ands: + case TOK_ASM_orr: + case TOK_ASM_eor: + case TOK_ASM_mul: + asm_data_proc(s1, opcode); + break; + + case TOK_ASM_mov: + /* mov is handled separately - it's ORR with zero register */ + asm_mov(s1); + break; + + case TOK_ASM_lsl: + case TOK_ASM_lsr: + case TOK_ASM_asr: + case TOK_ASM_ror: + asm_shift(s1, opcode); + break; + + case TOK_ASM_ldr: + case TOK_ASM_ldrb: + case TOK_ASM_ldrh: + case TOK_ASM_str: + case TOK_ASM_strb: + case TOK_ASM_strh: + asm_ldst(s1, opcode); + break; + + case TOK_ASM_ldp: + case TOK_ASM_stp: + asm_ldst_pair(s1, opcode); + break; + + case TOK_ASM_b: + case TOK_ASM_bl: + case TOK_ASM_br: + case TOK_ASM_blr: + case TOK_ASM_ret: + case TOK_ASM_beq: + case TOK_ASM_bne: + case TOK_ASM_bcs: + case TOK_ASM_bhs: + case TOK_ASM_bcc: + case TOK_ASM_blo: + case TOK_ASM_bmi: + case TOK_ASM_bpl: + case TOK_ASM_bvs: + case TOK_ASM_bvc: + case TOK_ASM_bhi: + case TOK_ASM_bls: + case TOK_ASM_bge: + case TOK_ASM_blt: + case TOK_ASM_bgt: + case TOK_ASM_ble: + asm_branch(s1, opcode); + break; + + case TOK_ASM_cbz: + case TOK_ASM_cbnz: + asm_cb(s1, opcode); + break; + + case TOK_ASM_movz: + case TOK_ASM_movn: + case TOK_ASM_movk: + asm_move_wide(s1, opcode); + break; + + case TOK_ASM_mrs: + case TOK_ASM_msr: + asm_sysreg(s1, opcode); + break; + + case TOK_ASM_isb: + case TOK_ASM_dsb: + case TOK_ASM_dmb: + asm_barrier(s1, opcode); + break; + + case TOK_ASM_nop: + gen_nop(); + break; + + default: + tcc_error("ARM64 instruction '%s' not implemented", + get_tok_str(opcode, NULL)); + break; + } } +/* Substitute assembler operand */ ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier) { - asm_error(); + int r, reg, size, fp_reg, align; + int64_t val; + uint64_t uval; + + r = sv->r; + if ((r & VT_VALMASK) == VT_CONST) { + if ((modifier == 'w' || modifier == 'x') && !(r & VT_LVAL) + && !(r & VT_SYM) && sv->c.i == 0) { + cstr_cat(add_str, modifier == 'w' ? "wzr" : "xzr", -1); + return; + } + if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n' && + modifier != 'P') + cstr_ccat(add_str, '#'); + if (r & VT_SYM) { + const char *name = get_tok_str(sv->sym->v, NULL); + if (sv->sym->v >= SYM_FIRST_ANOM) { + get_asm_sym(tok_alloc(name, strlen(name))->tok, sv->sym); + } + if (tcc_state->leading_underscore) + cstr_ccat(add_str, '_'); + cstr_cat(add_str, name, -1); + if ((uint32_t) sv->c.i == 0) + goto no_offset; + cstr_ccat(add_str, '+'); + } + uval = sv->c.i; + if (modifier == 'n') { + val = -(int64_t)uval; + cstr_printf(add_str, "%lld", (long long)val); + } else if (uval > 0x7fffffffffffffffULL) { + cstr_printf(add_str, "0x%llx", (unsigned long long)uval); + } else { + val = (int64_t)uval; + cstr_printf(add_str, "%lld", (long long)val); + } + no_offset:; + } else if ((r & VT_VALMASK) == VT_LOCAL) { + cstr_printf(add_str, "[x29,#%d]", (int) sv->c.i); + } else if (r & VT_LVAL) { + reg = r & VT_VALMASK; + if (reg >= VT_CONST) + tcc_internal_error(""); + cstr_printf(add_str, "[x%d]", reg); + } else { + /* register case */ + reg = r & VT_VALMASK; + if (reg >= VT_CONST) + tcc_internal_error(""); + + if (ARM64_FREG_BASE <= reg && reg <= ARM64_FREG_LAST) { + fp_reg = reg - ARM64_FREG_BASE; + size = type_size(&sv->type, &align); + + if (modifier == 0) + modifier = size <= 4 ? 's' + : size == 8 ? 'd' + : 'q'; + + switch (modifier) { + case 'b': + case 'h': + case 's': + case 'd': + case 'q': + case 'Z': + cstr_printf(add_str, "%c%d", modifier == 'Z' ? 'z' : modifier, + fp_reg); + return; + default: + tcc_error("invalid operand modifier for SIMD/FP register"); + return; + } + } + + /* choose register operand size */ + if ((sv->type.t & VT_BTYPE) == VT_BYTE || + (sv->type.t & VT_BTYPE) == VT_BOOL) + size = 1; + else if ((sv->type.t & VT_BTYPE) == VT_SHORT) + size = 2; + else if ((sv->type.t & VT_BTYPE) == VT_LLONG || + (sv->type.t & VT_BTYPE) == VT_PTR) + size = 8; + else + size = 4; + + if (modifier == 'x') { + size = 8; + } else if (modifier == 'w' || modifier == 'k') { + size = 4; + } else if (modifier == 'b') { + size = 1; + } else if (modifier == 'h') { + size = 2; + } else if (modifier == 'q') { + size = 8; + } + + if (size <= 4) { + cstr_printf(add_str, "w%d", reg); + } else { + cstr_printf(add_str, "x%d", reg); + } + } } -/* generate prolog and epilog code for asm statement */ ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, - int nb_outputs, int is_output, - uint8_t *clobber_regs, - int out_reg) + int nb_outputs, int is_output, + uint8_t *clobber_regs, + int out_reg) { + uint8_t regs_allocated[NB_ASM_REGS]; + ASMOperand *op; + int i, reg, saved_count, stack_size, stack_off; + int saved_regs[12]; + static const uint8_t reg_saved[] = { + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30 + }; + + memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated)); + for (i = 0; i < nb_operands; i++) { + op = &operands[i]; + if (op->reg >= 0) + regs_allocated[op->reg] = 1; + } + + saved_count = 0; + for (i = 0; i < sizeof(reg_saved) / sizeof(reg_saved[0]); i++) { + reg = reg_saved[i]; + if (regs_allocated[reg]) + saved_regs[saved_count++] = reg; + } + stack_size = ((saved_count + 1) / 2) * 16; + + if (!is_output) { + if (saved_count > 0) { + gen_sub_imm(31, 31, stack_size, 1, 0); + + for (i = stack_off = 0; i < saved_count; ) { + if (i + 1 < saved_count) { + gen_ldst_pair(ARM64_STP_X, saved_regs[i], saved_regs[i + 1], + TREG_SP, stack_off, 3); + stack_off += 16; + i += 2; + } else { + gen_ldst_imm(ARM64_STR_X, saved_regs[i], TREG_SP, + stack_off, 3); + i++; + } + } + } + + for (i = 0; i < nb_operands; i++) { + op = &operands[i]; + if (op->reg >= 0) { + if (op->is_memory) { + arm64_load_memory_operand_base(op->reg, op->vt); + } else if (i >= nb_outputs || op->is_rw) { + load(op->reg, op->vt); + } + } + } + } else { + for (i = 0; i < nb_outputs; i++) { + op = &operands[i]; + if (op->reg >= 0) { + if (op->is_memory) + continue; + if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) { + if (!op->is_memory) { + SValue sv; + sv = *op->vt; + sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL; + sv.type.t = VT_PTR; + load(out_reg, &sv); + + sv = *op->vt; + sv.r = (sv.r & ~VT_VALMASK) | out_reg; + store(op->reg, &sv); + } + } else { + store(op->reg, op->vt); + } + } + } + + if (saved_count > 0) { + for (i = stack_off = 0; i < saved_count; ) { + if (i + 1 < saved_count) { + gen_ldst_pair(ARM64_LDP_X, saved_regs[i], saved_regs[i + 1], + TREG_SP, stack_off, 3); + stack_off += 16; + i += 2; + } else { + gen_ldst_imm(ARM64_LDR_X, saved_regs[i], TREG_SP, + stack_off, 3); + i++; + } + } + gen_add_imm(31, 31, stack_size, 1, 0); + } + } } ST_FUNC void asm_compute_constraints(ASMOperand *operands, - int nb_operands, int nb_outputs, - const uint8_t *clobber_regs, - int *pout_reg) + int nb_operands, int nb_outputs, + const uint8_t *clobber_regs, + int *pout_reg) { + ASMOperand *op; + int sorted_op[MAX_ASM_OPERANDS]; + int i, j, k, p1, p2, tmp, reg, c, reg_mask; + const char *str; + uint8_t regs_allocated[NB_ASM_REGS]; + + for (i = 0; i < nb_operands; i++) { + op = &operands[i]; + op->input_index = -1; + op->ref_index = -1; + op->reg = -1; + op->is_memory = 0; + op->is_rw = 0; + op->is_llong = 0; + } + + for (i = 0; i < nb_operands; i++) { + op = &operands[i]; + str = op->constraint; + str = skip_constraint_modifiers(str); + if (isnum(*str) || *str == '[') { + k = find_constraint(operands, nb_operands, str, NULL); + if ((unsigned)k >= i || i < nb_outputs) + tcc_error("invalid reference in constraint %d ('%s')", i, str); + op->ref_index = k; + if (operands[k].input_index >= 0) + tcc_error("cannot reference twice the same operand"); + operands[k].input_index = i; + op->priority = 5; + } else if ((op->vt->r & VT_VALMASK) == VT_LOCAL + && op->vt->sym + && (reg = op->vt->sym->r & VT_VALMASK) < VT_CONST) { + op->priority = 1; + op->reg = reg; + } else { + op->priority = constraint_priority(str); + } + } + + for (i = 0; i < nb_operands; i++) + sorted_op[i] = i; + for (i = 0; i < nb_operands - 1; i++) { + for (j = i + 1; j < nb_operands; j++) { + p1 = operands[sorted_op[i]].priority; + p2 = operands[sorted_op[j]].priority; + if (p2 < p1) { + tmp = sorted_op[i]; + sorted_op[i] = sorted_op[j]; + sorted_op[j] = tmp; + } + } + } + + for (i = 0; i < NB_ASM_REGS; i++) { + if (clobber_regs[i]) + regs_allocated[i] = REG_IN_MASK | REG_OUT_MASK; + else + regs_allocated[i] = 0; + } + + for (i = 0; i < nb_operands; i++) { + j = sorted_op[i]; + op = &operands[j]; + str = op->constraint; + if (op->ref_index >= 0) + continue; + if (op->input_index >= 0) { + reg_mask = REG_IN_MASK | REG_OUT_MASK; + } else if (j < nb_outputs) { + reg_mask = REG_OUT_MASK; + } else { + reg_mask = REG_IN_MASK; + } + if (op->reg >= 0) { + if (is_reg_allocated(op->reg)) + tcc_error("asm regvar requests register that's taken already"); + reg = op->reg; + goto reg_found; + } + try_next: + c = *str++; + switch (c) { + case '=': + goto try_next; + case '+': + op->is_rw = 1; + case '&': + if (j >= nb_outputs) + tcc_error("'%c' modifier can only be applied to outputs", c); + reg_mask = REG_IN_MASK | REG_OUT_MASK; + goto try_next; + case 'r': + for (reg = 0; reg < 31; reg++) { + if (arm64_int_reg_is_allocatable(reg) + && !is_reg_allocated(reg)) + goto reg_found; + } + goto try_next; + case 'w': + case 'f': + for (reg = ARM64_FREG_BASE; reg <= ARM64_FREG_LAST; reg++) { + if (!is_reg_allocated(reg)) + goto reg_found; + } + goto try_next; + case 'x': + case 'y': + for (reg = ARM64_FREG_BASE; reg <= ARM64_FREG_LAST; reg++) { + if (!is_reg_allocated(reg)) + goto reg_found; + } + goto try_next; + case 'm': + case 'g': + if (j < nb_outputs || c == 'm') { + if (!arm64_prepare_memory_operand(op, regs_allocated)) + goto try_next; + } + break; + case 'Q': + if (!arm64_memory_is_base_only(op->vt)) + goto try_next; + if (!arm64_prepare_memory_operand(op, regs_allocated)) + goto try_next; + break; + case 'S': + if ((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != (VT_CONST | VT_SYM)) + goto try_next; + break; + case 'U': + if (str[0] != 'm' || str[1] != 'p') + goto try_next; + str += 2; + if (!arm64_memory_is_pair_suitable(op->vt)) + goto try_next; + if (!arm64_prepare_memory_operand(op, regs_allocated)) + goto try_next; + break; + case 'i': + if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST)) + goto try_next; + break; + case 'I': + if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST)) + goto try_next; + if (!is_valid_add_imm(op->vt->c.i)) + goto try_next; + break; + case 'J': + if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST)) + goto try_next; + if (!is_valid_add_imm(-op->vt->c.i)) + goto try_next; + break; + case 'K': + if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST)) + goto try_next; + if (!is_valid_logical_imm(op->vt->c.i, 32)) + goto try_next; + break; + case 'L': + if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST)) + goto try_next; + if (!is_valid_logical_imm(op->vt->c.i, 64)) + goto try_next; + break; + case 'M': + case 'N': + if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST)) + goto try_next; + if (!is_valid_movw_imm(op->vt->c.i)) + goto try_next; + break; + case 'Z': + if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST)) + goto try_next; + if (op->vt->c.i != 0) + goto try_next; + break; + case 'n': + if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST)) + goto try_next; + break; + default: + tcc_error("asm constraint %d ('%s') could not be satisfied", + j, op->constraint); + break; + } + if (op->input_index >= 0) { + operands[op->input_index].reg = op->reg; + operands[op->input_index].is_llong = op->is_llong; + } + continue; + reg_found: + op->is_llong = 0; + op->reg = reg; + regs_allocated[reg] |= reg_mask; + if (op->input_index >= 0) { + operands[op->input_index].reg = op->reg; + operands[op->input_index].is_llong = op->is_llong; + } + } + + *pout_reg = -1; + for (i = 0; i < nb_operands; i++) { + op = &operands[i]; + if (op->reg >= 0 && + (op->vt->r & VT_VALMASK) == VT_LLOCAL && + !op->is_memory) { + for (reg = 0; reg < 31; reg++) { + if (!(regs_allocated[reg] & REG_OUT_MASK)) + goto reg_found2; + } + tcc_error("could not find free output register for reloading"); + reg_found2: + *pout_reg = reg; + break; + } + } } +/* Handle clobber list */ ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str) { - asm_error(); + int reg; + + if (!strcmp(str, "memory") || + !strcmp(str, "cc") || + !strcmp(str, "flags")) + return; + reg = arm64_parse_regvar(tok_alloc_const(str)); + if (reg == -1) + tcc_error("invalid clobber register '%s'", str); + clobber_regs[reg] = 1; } -ST_FUNC int asm_parse_regvar (int t) +/* Parse register variable - this is the ST_FUNC that tcc.h expects */ +ST_FUNC int asm_parse_regvar(int t) { - asm_error(); - return -1; + return arm64_parse_regvar(t); } /*************************************************************/ diff --git a/arm64-gen.c b/arm64-gen.c index 04204ed768..5e0c833c21 100644 --- a/arm64-gen.c +++ b/arm64-gen.c @@ -49,7 +49,7 @@ #define MAX_ALIGN 16 -#ifndef TCC_TARGET_MACHO +#if !defined(TCC_TARGET_MACHO) && !defined(TCC_TARGET_PE) #define CHAR_IS_UNSIGNED #endif @@ -190,35 +190,35 @@ static uint32_t arm64_movi(int r, uint64_t x) uint64_t m = 0xffff; int e; if (!(x & ~m)) - return 0x52800000 | r | x << 5; // movz w(r),#(x) + return ARM64_MOVZ | r | x << 5; // movz w(r),#(x) if (!(x & ~(m << 16))) - return 0x52a00000 | r | x >> 11; // movz w(r),#(x >> 16),lsl #16 + return (ARM64_MOVZ | ARM64_HW(1) | r | x >> 11); // movz w(r),#(x >> 16),lsl #16 if (!(x & ~(m << 32))) - return 0xd2c00000 | r | x >> 27; // movz x(r),#(x >> 32),lsl #32 + return (ARM64_MOVZ64 | ARM64_HW(2) | r | x >> 27); // movz x(r),#(x >> 32),lsl #32 if (!(x & ~(m << 48))) - return 0xd2e00000 | r | x >> 43; // movz x(r),#(x >> 48),lsl #48 + return (ARM64_MOVZ64 | ARM64_HW(3) | r | x >> 43); // movz x(r),#(x >> 48),lsl #48 if ((x & ~m) == m << 16) - return (0x12800000 | r | + return (ARM64_MOVN | r | (~x << 5 & 0x1fffe0)); // movn w(r),#(~x) if ((x & ~(m << 16)) == m) - return (0x12a00000 | r | + return (ARM64_MOVN | ARM64_HW(1) | r | (~x >> 11 & 0x1fffe0)); // movn w(r),#(~x >> 16),lsl #16 if (!~(x | m)) - return (0x92800000 | r | + return (ARM64_MOVN64 | r | (~x << 5 & 0x1fffe0)); // movn x(r),#(~x) if (!~(x | m << 16)) - return (0x92a00000 | r | + return (ARM64_MOVN64 | ARM64_HW(1) | r | (~x >> 11 & 0x1fffe0)); // movn x(r),#(~x >> 16),lsl #16 if (!~(x | m << 32)) - return (0x92c00000 | r | + return (ARM64_MOVN64 | ARM64_HW(2) | r | (~x >> 27 & 0x1fffe0)); // movn x(r),#(~x >> 32),lsl #32 if (!~(x | m << 48)) - return (0x92e00000 | r | + return (ARM64_MOVN64 | ARM64_HW(3) | r | (~x >> 43 & 0x1fffe0)); // movn x(r),#(~x >> 32),lsl #32 if (!(x >> 32) && (e = arm64_encode_bimm64(x | x << 32)) >= 0) - return 0x320003e0 | r | (uint32_t)e << 10; // movi w(r),#(x) + return (ARM64_ORR_IMM | r | (uint32_t)e << 10); // movi w(r),#(x) if ((e = arm64_encode_bimm64(x)) >= 0) - return 0xb20003e0 | r | (uint32_t)e << 10; // movi x(r),#(x) + return (ARM64_ORR_IMM | ARM64_SF(1) | r | (uint32_t)e << 10); // movi x(r),#(x) return 0; } @@ -230,7 +230,7 @@ static void arm64_movimm(int r, uint64_t x) else { // MOVZ/MOVN and 1-3 MOVKs int z = 0, m = 0; - uint32_t mov1 = 0xd2800000; // movz + uint32_t mov1 = ARM64_MOVZ64; // movz uint64_t x1 = x; for (i = 0; i < 64; i += 16) { z += !(x >> i & 0xffff); @@ -238,7 +238,7 @@ static void arm64_movimm(int r, uint64_t x) } if (m > z) { x1 = ~x; - mov1 = 0x92800000; // movn + mov1 = ARM64_MOVN64; // movn } for (i = 0; i < 64; i += 16) if (x1 >> i & 0xffff) { @@ -248,7 +248,7 @@ static void arm64_movimm(int r, uint64_t x) } for (i += 16; i < 64; i += 16) if (x1 >> i & 0xffff) - o(0xf2800000 | r | (x >> i & 0xffff) << 5 | i << 17); + o(ARM64_MOVK | ARM64_SF(1) | r | (x >> i & 0xffff) << 5 | i << 17); // movk x(r),#(*),lsl #(i) } } @@ -263,8 +263,8 @@ ST_FUNC void gsym_addr(int t_, int a_) uint32_t next = read32le(ptr); if (a - t + 0x8000000 >= 0x10000000) tcc_error("branch out of range"); - write32le(ptr, (a - t == 4 ? 0xd503201f : // nop - 0x14000000 | ((a - t) >> 2 & 0x3ffffff))); // b + write32le(ptr, (a - t == 4 ? ARM64_NOP : + ARM64_B | ((a - t) >> 2 & 0x3ffffff))); t = next; } } @@ -299,11 +299,10 @@ static void arm64_spoff(int reg, uint64_t off) if (sub) off = -off; if (off < 4096) - o(0x910003e0 | sub << 30 | reg | off << 10); - // (add|sub) x(reg),sp,#(off) + o(ARM64_ADD_IMM | ARM64_SF(1) | ARM64_RN(31) | ARM64_RD(reg) | ARM64_IMM12(off)); else { - arm64_movimm(30, off); // use x30 for offset - o(0x8b3e63e0 | sub << 30 | reg); // (add|sub) x(reg),sp,x30 + arm64_movimm(30, off); + o(ARM64_ADD_REG | ARM64_SF(1) | ARM64_RM(30) | ARM64_RN(31) | ARM64_RD(reg) | (sub << 30)); } } @@ -312,14 +311,15 @@ static void arm64_spoff(int reg, uint64_t off) static uint64_t arm64_check_offset(int invert, int sz_, uint64_t off) { uint32_t sz = sz_; - if (!(off & ~((uint32_t)0xfff << sz)) || + uint64_t scaled_mask = 0xffful << sz; + + if (!(off & ~scaled_mask) || (off < 256 || -off <= 256)) return invert ? off : 0ul; - else if ((off & ((uint32_t)0xfff << sz))) - return invert ? off & ((uint32_t)0xfff << sz) - : off & ~((uint32_t)0xfff << sz); - else if (off & 0x1ff) - return invert ? off & 0x1ff : off & ~0x1ff; + else if (off & scaled_mask) + return invert ? off & scaled_mask : off & ~scaled_mask; + else if (off & 0x1fful) + return invert ? off & 0x1fful : off & ~0x1fful; else return invert ? 0ul : off; } @@ -327,17 +327,18 @@ static uint64_t arm64_check_offset(int invert, int sz_, uint64_t off) static void arm64_ldrx(int sg, int sz_, int dst, int bas, uint64_t off) { uint32_t sz = sz_; + uint64_t scaled_mask = 0xffful << sz; if (sz >= 2) sg = 0; - if (!(off & ~((uint32_t)0xfff << sz))) - o(0x39400000 | dst | bas << 5 | off << (10 - sz) | + if (!(off & ~scaled_mask)) + o(ARM64_LDR_B | dst | bas << 5 | off << (10 - sz) | (uint32_t)!!sg << 23 | sz << 30); // ldr(*) x(dst),[x(bas),#(off)] else if (off < 256 || -off <= 256) - o(0x38400000 | dst | bas << 5 | (off & 511) << 12 | + o(ARM64_LDUR_B | dst | bas << 5 | (off & 511) << 12 | (uint32_t)!!sg << 23 | sz << 30); // ldur(*) x(dst),[x(bas),#(off)] else { arm64_movimm(30, off); // use x30 for offset - o(0x38206800 | dst | bas << 5 | (uint32_t)30 << 16 | + o(ARM64_LDR_B_REG | dst | bas << 5 | (uint32_t)30 << 16 | (uint32_t)(!!sg + 1) << 22 | sz << 30); // ldr(*) x(dst),[x(bas),x30] } } @@ -345,15 +346,17 @@ static void arm64_ldrx(int sg, int sz_, int dst, int bas, uint64_t off) static void arm64_ldrv(int sz_, int dst, int bas, uint64_t off) { uint32_t sz = sz_; - if (!(off & ~((uint32_t)0xfff << sz))) - o(0x3d400000 | dst | bas << 5 | off << (10 - sz) | + uint64_t scaled_mask = 0xffful << sz; + + if (!(off & ~scaled_mask)) + o(ARM64_LDR_SCALAR | dst | bas << 5 | off << (10 - sz) | (sz & 4) << 21 | (sz & 3) << 30); // ldr (s|d|q)(dst),[x(bas),#(off)] else if (off < 256 || -off <= 256) - o(0x3c400000 | dst | bas << 5 | (off & 511) << 12 | + o(ARM64_LDUR_Q_SIMD | dst | bas << 5 | (off & 511) << 12 | (sz & 4) << 21 | (sz & 3) << 30); // ldur (s|d|q)(dst),[x(bas),#(off)] else { arm64_movimm(30, off); // use x30 for offset - o(0x3c606800 | dst | bas << 5 | (uint32_t)30 << 16 | + o(ARM64_LDR_Q_REG | dst | bas << 5 | (uint32_t)30 << 16 | sz << 30 | (sz & 4) << 21); // ldr (s|d|q)(dst),[x(bas),x30] } } @@ -442,7 +445,9 @@ static void arm64_ldrs(int reg_, int size) static void arm64_strx(int sz_, int dst, int bas, uint64_t off) { uint32_t sz = sz_; - if (!(off & ~((uint32_t)0xfff << sz))) + uint64_t scaled_mask = 0xffful << sz; + + if (!(off & ~scaled_mask)) o(0x39000000 | dst | bas << 5 | off << (10 - sz) | sz << 30); // str(*) x(dst),[x(bas],#(off)] else if (off < 256 || -off <= 256) @@ -458,7 +463,9 @@ static void arm64_strx(int sz_, int dst, int bas, uint64_t off) static void arm64_strv(int sz_, int dst, int bas, uint64_t off) { uint32_t sz = sz_; - if (!(off & ~((uint32_t)0xfff << sz))) + uint64_t scaled_mask = 0xffful << sz; + + if (!(off & ~scaled_mask)) o(0x3d000000 | dst | bas << 5 | off << (10 - sz) | (sz & 4) << 21 | (sz & 3) << 30); // str (s|d|q)(dst),[x(bas),#(off)] else if (off < 256 || -off <= 256) @@ -473,25 +480,35 @@ static void arm64_strv(int sz_, int dst, int bas, uint64_t off) static void arm64_sym(int r, Sym *sym, unsigned long addend) { +#ifdef TCC_TARGET_PE + /* PE links symbol addresses directly; there is no ELF-style GOT here. */ + greloca(cur_text_section, sym, ind, R_AARCH64_ADR_PREL_PG_HI21, 0); + o(ARM64_ADRP | r); // adrp xr, #sym + greloca(cur_text_section, sym, ind, R_AARCH64_ADD_ABS_LO12_NC, 0); + o(ARM64_ADD_IMM | ARM64_SF(1) | ARM64_RN(r) | r); // add xr, xr, #sym +#else greloca(cur_text_section, sym, ind, R_AARCH64_ADR_GOT_PAGE, 0); - o(0x90000000 | r); // adrp xr, #sym + o(ARM64_ADRP | r); // adrp xr, #sym greloca(cur_text_section, sym, ind, R_AARCH64_LD64_GOT_LO12_NC, 0); - o(0xf9400000 | r | (r << 5)); // ld xr,[xr, #sym] + o(ARM64_LDR_X | ARM64_RN(r) | r); // ld xr,[xr, #sym] +#endif if (addend) { // add xr, xr, #addend if (addend & 0xffful) - o(0x91000000 | r | r << 5 | (addend & 0xfff) << 10); + o(ARM64_ADD_IMM | ARM64_SF(1) | ARM64_RN(r) | r | + (addend & 0xfff) << 10); if (addend > 0xffful) { // add xr, xr, #addend, lsl #12 if (addend & 0xfff000ul) - o(0x91400000 | r | r << 5 | ((addend >> 12) & 0xfff) << 10); + o(ARM64_ADD_IMM | ARM64_SF(1) | ARM64_SH(1) | + ARM64_RN(r) | r | ((addend >> 12) & 0xfff) << 10); if (addend > 0xfffffful) { /* very unlikely */ int t = r ? 0 : 1; - o(0xf81f0fe0 | t); /* str xt, [sp, #-16]! */ + o(ARM64_STR_X_PRE | 0x001F0FE0U | t); /* str xt, [sp, #-16]! */ arm64_movimm(t, addend & ~0xfffffful); // use xt for addent - o(0x8B000000 | (t << 16) | (r << 5) | r); /* add xr, xr, xt */ - o(0xf84107e0 | t); /* ldr xt, [sp], #16 */ + o(ARM64_ADD_REG | ARM64_SF(1) | ARM64_RM(t) | ARM64_RN(r) | r); /* add xr, xr, xt */ + o(ARM64_LDR_X_POST | 0x000107E0U | t); /* ldr xt, [sp], #16 */ } } } @@ -504,15 +521,15 @@ ST_FUNC void load(int r, SValue *sv) int svtt = sv->type.t; int svr = sv->r & ~(VT_BOUNDED | VT_NONCONST); int svrv = svr & VT_VALMASK; - uint64_t svcul = (uint32_t)sv->c.i; - svcul = svcul >> 31 & 1 ? svcul - ((uint64_t)1 << 32) : svcul; + uint64_t svcul = sv->c.i; + uint64_t svcoff = (uint64_t)(int64_t)(int32_t)sv->c.i; if (svr == (VT_LOCAL | VT_LVAL)) { if (IS_FREG(r)) - arm64_ldrv(arm64_type_size(svtt), fltr(r), 29, svcul); + arm64_ldrv(arm64_type_size(svtt), fltr(r), 29, svcoff); else arm64_ldrx(!(svtt & VT_UNSIGNED), arm64_type_size(svtt), - intr(r), 29, svcul); + intr(r), 29, svcoff); return; } @@ -546,13 +563,13 @@ ST_FUNC void load(int r, SValue *sv) if (svr == (VT_CONST | VT_LVAL | VT_SYM)) { arm64_sym(30, sv->sym, // use x30 for address - arm64_check_offset(0, arm64_type_size(svtt), svcul)); + arm64_check_offset(0, arm64_type_size(svtt), svcoff)); if (IS_FREG(r)) arm64_ldrv(arm64_type_size(svtt), fltr(r), 30, - arm64_check_offset(1, arm64_type_size(svtt), svcul)); + arm64_check_offset(1, arm64_type_size(svtt), svcoff)); else arm64_ldrx(!(svtt&VT_UNSIGNED), arm64_type_size(svtt), intr(r), 30, - arm64_check_offset(1, arm64_type_size(svtt), svcul)); + arm64_check_offset(1, arm64_type_size(svtt), svcoff)); return; } @@ -571,22 +588,22 @@ ST_FUNC void load(int r, SValue *sv) if (svr < VT_CONST) { if (IS_FREG(r) && IS_FREG(svr)) if (svtt == VT_LDOUBLE) - o(0x4ea01c00 | fltr(r) | fltr(svr) << 5); + o(ARM64_MOV_V16B | fltr(r) | fltr(svr) << 5); // mov v(r).16b,v(svr).16b else - o(0x1e604000 | fltr(r) | fltr(svr) << 5); // fmov d(r),d(svr) + o(ARM64_FMOV_SCALAR | fltr(r) | fltr(svr) << 5); // fmov d(r),d(svr) else if (!IS_FREG(r) && !IS_FREG(svr)) - o(0xaa0003e0 | intr(r) | intr(svr) << 16); // mov x(r),x(svr) + o(ARM64_MOV_REG | ARM64_SF(1) | intr(r) | intr(svr) << 16); // mov x(r),x(svr) else assert(0); return; } if (svr == VT_LOCAL) { - if (-svcul < 0x1000) - o(0xd10003a0 | intr(r) | -svcul << 10); // sub x(r),x29,#... + if (-svcoff < 0x1000) + o(0xd10003a0 | intr(r) | -svcoff << 10); // sub x(r),x29,#... else { - arm64_movimm(30, -svcul); // use x30 for offset + arm64_movimm(30, -svcoff); // use x30 for offset o(0xcb0003a0 | intr(r) | (uint32_t)30 << 16); // sub x(r),x29,x30 } return; @@ -595,14 +612,14 @@ ST_FUNC void load(int r, SValue *sv) if (svr == VT_JMP || svr == VT_JMPI) { int t = (svr == VT_JMPI); arm64_movimm(intr(r), t); - o(0x14000002); // b .+8 + o(ARM64_B | 2); // b .+8 gsym(svcul); arm64_movimm(intr(r), t ^ 1); return; } if (svr == (VT_LLOCAL | VT_LVAL)) { - arm64_ldrx(0, 3, 30, 29, svcul); // use x30 for offset + arm64_ldrx(0, 3, 30, 29, svcoff); // use x30 for offset if (IS_FREG(r)) arm64_ldrv(arm64_type_size(svtt), fltr(r), 30, 0); else @@ -625,14 +642,13 @@ ST_FUNC void store(int r, SValue *sv) int svtt = sv->type.t; int svr = sv->r & ~VT_BOUNDED; int svrv = svr & VT_VALMASK; - uint64_t svcul = (uint32_t)sv->c.i; - svcul = svcul >> 31 & 1 ? svcul - ((uint64_t)1 << 32) : svcul; + uint64_t svcoff = (uint64_t)(int64_t)(int32_t)sv->c.i; if (svr == (VT_LOCAL | VT_LVAL)) { if (IS_FREG(r)) - arm64_strv(arm64_type_size(svtt), fltr(r), 29, svcul); + arm64_strv(arm64_type_size(svtt), fltr(r), 29, svcoff); else - arm64_strx(arm64_type_size(svtt), intr(r), 29, svcul); + arm64_strx(arm64_type_size(svtt), intr(r), 29, svcoff); return; } @@ -663,17 +679,17 @@ ST_FUNC void store(int r, SValue *sv) if (svr == (VT_CONST | VT_LVAL | VT_SYM)) { arm64_sym(30, sv->sym, // use x30 for address - arm64_check_offset(0, arm64_type_size(svtt), svcul)); + arm64_check_offset(0, arm64_type_size(svtt), svcoff)); if (IS_FREG(r)) arm64_strv(arm64_type_size(svtt), fltr(r), 30, - arm64_check_offset(1, arm64_type_size(svtt), svcul)); + arm64_check_offset(1, arm64_type_size(svtt), svcoff)); else arm64_strx(arm64_type_size(svtt), intr(r), 30, - arm64_check_offset(1, arm64_type_size(svtt), svcul)); + arm64_check_offset(1, arm64_type_size(svtt), svcoff)); return; } - printf("store(%x, (%x, %x, %lx))\n", r, svtt, sv->r, (long)svcul); + printf("store(%x, (%x, %x, %lx))\n", r, svtt, sv->r, (long)svcoff); assert(0); } @@ -682,13 +698,13 @@ static void arm64_gen_bl_or_b(int b) if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST && (vtop->r & VT_SYM)) { greloca(cur_text_section, vtop->sym, ind, b ? R_AARCH64_JUMP26 : R_AARCH64_CALL26, 0); - o(0x14000000 | (uint32_t)!b << 31); // b/bl . + o(b ? ARM64_B : ARM64_BL); // b/bl . } else { #ifdef CONFIG_TCC_BCHECK vtop->r &= ~VT_MUSTBOUND; #endif - o(0xd61f0000 | (uint32_t)!b << 21 | intr(gv(RC_R30)) << 5); // br/blr + o((b ? ARM64_BR : ARM64_BLR) | intr(gv(RC_R30)) << 5); // br/blr } } @@ -699,7 +715,7 @@ static void gen_bounds_call(int v) Sym *sym = external_helper_sym(v); greloca(cur_text_section, sym, ind, R_AARCH64_CALL26, 0); - o(0x94000000); // bl + o(ARM64_BL); // bl } static void gen_bounds_prolog(void) @@ -708,10 +724,10 @@ static void gen_bounds_prolog(void) func_bound_offset = lbounds_section->data_offset; func_bound_ind = ind; func_bound_add_epilog = 0; - o(0xd503201f); /* nop -> mov x0, lbound section pointer */ - o(0xd503201f); - o(0xd503201f); - o(0xd503201f); /* nop -> call __bound_local_new */ + o(ARM64_NOP); /* nop -> mov x0, lbound section pointer */ + o(ARM64_NOP); + o(ARM64_NOP); + o(ARM64_NOP); /* nop -> call __bound_local_new */ } static void gen_bounds_epilog(void) @@ -735,10 +751,7 @@ static void gen_bounds_epilog(void) if (offset_modified) { saved_ind = ind; ind = func_bound_ind; - greloca(cur_text_section, sym_data, ind, R_AARCH64_ADR_GOT_PAGE, 0); - o(0x90000000 | 0); // adrp x0, #sym_data - greloca(cur_text_section, sym_data, ind, R_AARCH64_LD64_GOT_LO12_NC, 0); - o(0xf9400000 | 0 | (0 << 5)); // ld x0,[x0, #sym_data] + arm64_sym(0, sym_data, 0); gen_bounds_call(TOK___bound_local_new); ind = saved_ind; } @@ -746,10 +759,7 @@ static void gen_bounds_epilog(void) /* generate bound check local freeing */ o(0xa9bf07e0); /* stp x0, x1, [sp, #-16]! */ o(0x3c9f0fe0); /* str q0, [sp, #-16]! */ - greloca(cur_text_section, sym_data, ind, R_AARCH64_ADR_GOT_PAGE, 0); - o(0x90000000 | 0); // adrp x0, #sym_data - greloca(cur_text_section, sym_data, ind, R_AARCH64_LD64_GOT_LO12_NC, 0); - o(0xf9400000 | 0 | (0 << 5)); // ld x0,[x0, #sym_data] + arm64_sym(0, sym_data, 0); gen_bounds_call(TOK___bound_local_delete); o(0x3cc107e0); /* ldr q0, [sp], #16 */ o(0xa8c107e0); /* ldp x0, x1, [sp], #16 */ @@ -758,6 +768,8 @@ static void gen_bounds_epilog(void) static int arm64_hfa_aux(CType *type, int *fsize, int num) { + if (!type) + return -1; if (is_float(type->t)) { int a, n = type_size(type, &a); if (num >= 4 || (*fsize && *fsize != n)) @@ -766,14 +778,10 @@ static int arm64_hfa_aux(CType *type, int *fsize, int num) return num + 1; } else if ((type->t & VT_BTYPE) == VT_STRUCT) { - int is_struct = 0; // rather than union Sym *field; - for (field = type->ref->next; field; field = field->next) - if (field->c) { - is_struct = 1; - break; - } - if (is_struct) { + if (!type->ref) + return -1; + if (!IS_UNION(type->t)) { int num0 = num; for (field = type->ref->next; field; field = field->next) { if (field->c != (num - num0) * *fsize) @@ -801,6 +809,8 @@ static int arm64_hfa_aux(CType *type, int *fsize, int num) } else if ((type->t & VT_ARRAY) && ((type->t & VT_BTYPE) != VT_PTR)) { int num1; + if (!type->ref || (type->t & VT_VLA)) + return -1; if (!type->ref->c) return num; num1 = arm64_hfa_aux(&type->ref->type, fsize, num); @@ -816,10 +826,16 @@ static int arm64_hfa_aux(CType *type, int *fsize, int num) static int arm64_hfa(CType *type, unsigned *fsize) { + int n, sz; + + if (!type) + return 0; if ((type->t & VT_BTYPE) == VT_STRUCT || ((type->t & VT_ARRAY) && ((type->t & VT_BTYPE) != VT_PTR))) { - int sz = 0; - int n = arm64_hfa_aux(type, &sz, 0); + if (!type->ref || (type->t & VT_VLA)) + return 0; + sz = 0; + n = arm64_hfa_aux(type, &sz, 0); if (0 < n && n <= 4) { if (fsize) *fsize = sz; @@ -838,6 +854,7 @@ static unsigned long arm64_pcs_aux(int variadic, int n, CType **type, unsigned l for (i = 0; i < n; i++) { int hfa = arm64_hfa(type[i], 0); + int win_vararg_float = 0; int size, align; if ((type[i]->t & VT_ARRAY) || @@ -847,10 +864,20 @@ static unsigned long arm64_pcs_aux(int variadic, int n, CType **type, unsigned l size = type_size(type[i], &align); #if defined(TCC_TARGET_MACHO) - if (variadic && i == variadic) { + if (variadic > 0 && i == variadic) { nx = 8; nv = 8; } +#elif defined(TCC_TARGET_PE) + if ((variadic < 0 || (variadic > 0 && i >= variadic)) + && (hfa || is_float(type[i]->t))) { + hfa = 0; + if (is_float(type[i]->t)) { + win_vararg_float = 1; + size = 8; + align = 8; + } + } #endif if (hfa) // B.2 @@ -871,7 +898,7 @@ static unsigned long arm64_pcs_aux(int variadic, int n, CType **type, unsigned l size = (size + 7) & ~7; // C.1 - if (is_float(type[i]->t) && nv < 8) { + if (!win_vararg_float && is_float(type[i]->t) && nv < 8) { a[i] = 16 + (nv++ << 1); continue; } @@ -900,7 +927,7 @@ static unsigned long arm64_pcs_aux(int variadic, int n, CType **type, unsigned l size = 8; // C.6 - if (hfa || is_float(type[i]->t)) { + if (!win_vararg_float && (hfa || is_float(type[i]->t))) { a[i] = ns; ns += size; continue; @@ -956,6 +983,8 @@ static unsigned long arm64_pcs_aux(int variadic, int n, CType **type, unsigned l return ns - 32; } +#define ARM64_PCS_ALL_VARARGS (-1) + static unsigned long arm64_pcs(int variadic, int n, CType **type, unsigned long *a) { unsigned long stack; @@ -1005,6 +1034,32 @@ static int n_func_args(CType *type) return n_args; } +static void arm64_sub_sp(uint64_t diff) +{ + if (!diff) + return; +#ifdef TCC_TARGET_PE + if (diff >= 4096) { + Sym *sym = external_helper_sym(TOK___chkstk); + + arm64_movimm(15, diff >> 4); + greloca(cur_text_section, sym, ind, R_AARCH64_CALL26, 0); + o(ARM64_BL); // bl __chkstk + o(0xcb2f73ff); // sub sp,sp,x15,lsl #4 + return; + } +#endif + if (!(diff >> 24)) { + if (diff & 0xffful) + o(ARM64_SUB_IMM | ARM64_SF(1) | ARM64_RN(31) | ARM64_RD(31) | ARM64_IMM12(diff & 0xfff)); + if (diff >> 12) + o(ARM64_SUB_IMM | ARM64_SF(1) | ARM64_SH(1) | ARM64_RN(31) | ARM64_RD(31) | ARM64_IMM12((diff >> 12) & 0xfff)); + } else { + arm64_movimm(16, diff); + o(0xCB3063FFU); // sub sp,sp,x16 + } +} + ST_FUNC void gfunc_call(int nb_args) { CType *return_type; @@ -1012,8 +1067,11 @@ ST_FUNC void gfunc_call(int nb_args) unsigned long *a, *a1; unsigned long stack; int i; - int variadic = (vtop[-nb_args].type.ref->f.func_type == FUNC_ELLIPSIS); - int var_nb_arg = n_func_args(&vtop[-nb_args].type); + int pcs_variadic = 0; + int func_type = vtop[-nb_args].type.ref->f.func_type; + int variadic = (func_type == FUNC_ELLIPSIS); + int old_style = (func_type == FUNC_OLD); + int var_nb_arg = variadic ? n_func_args(&vtop[-nb_args].type) : 0; save_regs(nb_args + 1); @@ -1034,7 +1092,13 @@ ST_FUNC void gfunc_call(int nb_args) for (i = 0; i < nb_args; i++) t[nb_args - i] = &vtop[-i].type; - stack = arm64_pcs(variadic ? var_nb_arg : 0, nb_args, t, a); + if (variadic) + pcs_variadic = var_nb_arg; +#ifdef TCC_TARGET_PE + else if (old_style) + pcs_variadic = ARM64_PCS_ALL_VARARGS; +#endif + stack = arm64_pcs(pcs_variadic, nb_args, t, a); // Allocate space for structs replaced by pointer: for (i = nb_args; i; i--) @@ -1051,10 +1115,7 @@ ST_FUNC void gfunc_call(int nb_args) if (stack >= 0x1000000) // 16Mb tcc_error("stack size too big %lu", stack); - if (stack & 0xfff) - o(0xd10003ff | (stack & 0xfff) << 10); // sub sp,sp,#(n) - if (stack >> 12) - o(0xd14003ff | (stack >> 12) << 10); + arm64_sub_sp(stack); // First pass: set all values on stack for (i = nb_args; i; i--) { @@ -1102,7 +1163,14 @@ ST_FUNC void gfunc_call(int nb_args) for (i = nb_args; i; i--, vtop--) { if (a[i] < 16 && !(a[i] & 1)) { // value in general-purpose registers - if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { + if ((variadic || old_style) && i > var_nb_arg && is_float(vtop->type.t)) { + gv(RC_FLOAT); + if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) + o(ARM64_FMOV_XD | intr(a[i] / 2) | fltr(vtop->r) << 5); // fmov xN,dM + else + o(ARM64_FMOV_WS | intr(a[i] / 2) | fltr(vtop->r) << 5); // fmov wN,sM + } + else if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { int align, size = type_size(&vtop->type, &align); if (size) { vtop->type.t = VT_PTR; @@ -1121,14 +1189,20 @@ ST_FUNC void gfunc_call(int nb_args) // value in floating-point registers if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { uint32_t j, sz, n = arm64_hfa(&vtop->type, &sz); - vtop->type.t = VT_PTR; - gaddrof(); - gv(RC_R30); - for (j = 0; j < n; j++) - o(0x3d4003c0 | - (sz & 16) << 19 | -(sz & 8) << 27 | (sz & 4) << 29 | - (a[i] / 2 - 8 + j) | - j << 10); // ldr ([sdq])(*),[x30,#(j * sz)] + if (n > 0) { + /* HFA struct - load from memory into float registers */ + vtop->type.t = VT_PTR; + gaddrof(); + gv(RC_R30); + for (j = 0; j < n; j++) + o(0x3d4003c0 | + (sz & 16) << 19 | -(sz & 8) << 27 | (sz & 4) << 29 | + (a[i] / 2 - 8 + j) | + j << 10); // ldr ([sdq])(*),[x30,#(j * sz)] + } else { + /* Non-HFA struct in float register slot - shouldn't happen */ + gv(RC_F(a[i] / 2 - 8)); + } } else gv(RC_F(a[i] / 2 - 8)); @@ -1170,12 +1244,13 @@ ST_FUNC void gfunc_call(int nb_args) } else if (a[0] == 16) { + /* HFA struct return - store from float registers to the address in x8 */ uint32_t j, sz, n = arm64_hfa(return_type, &sz); for (j = 0; j < n; j++) o(0x3d000100 | (sz & 16) << 19 | -(sz & 8) << 27 | (sz & 4) << 29 | - (a[i] / 2 - 8 + j) | - j << 10); // str ([sdq])(*),[x8,#(j * sz)] + (fltr(REG_FRET) + j) | + j << 10); // str ([sdq])(j),[x8,#(j * sz)] } } } @@ -1188,13 +1263,26 @@ ST_FUNC void gfunc_call(int nb_args) static unsigned long arm64_func_va_list_stack; static int arm64_func_va_list_gr_offs; static int arm64_func_va_list_vr_offs; +static unsigned arm64_func_start_offset; static int arm64_func_sub_sp_offset; +#define ARM64_FUNC_STACK_SETUP_SLOTS 6 + +#ifdef TCC_TARGET_PE +static unsigned long arm64_pe_param_off(unsigned long a) +{ + return a < 16 ? 160 + a / 2 * 8 : + a < 32 ? 16 + (a - 16) / 2 * 16 : + 224 + ((a - 32) >> 1 << 1); +} +#endif + ST_FUNC void gfunc_prolog(Sym *func_sym) { CType *func_type = &func_sym->type; int n = 0; int i = 0; + int pcs_n; Sym *sym; CType **t; unsigned long *a; @@ -1208,13 +1296,26 @@ ST_FUNC void gfunc_prolog(Sym *func_sym) for (sym = func_type->ref; sym; sym = sym->next) ++n; - t = n ? tcc_malloc(n * sizeof(*t)) : NULL; - a = n ? tcc_malloc(n * sizeof(*a)) : NULL; + pcs_n = n - 1; + t = n || variadic ? tcc_malloc((n + variadic) * sizeof(*t)) : NULL; + a = n || variadic ? tcc_malloc((n + variadic) * sizeof(*a)) : NULL; for (sym = func_type->ref; sym; sym = sym->next) t[i++] = &sym->type; +#ifdef TCC_TARGET_PE + if (variadic) { + t[i++] = &int_type; + ++pcs_n; + } +#endif - arm64_func_va_list_stack = arm64_pcs(variadic ? var_nb_arg : 0, n - 1, t, a); + arm64_func_va_list_stack = arm64_pcs(variadic ? var_nb_arg : 0, + pcs_n, t, a); + +#ifdef TCC_TARGET_PE + if (variadic) + arm64_func_va_list_stack = arm64_pe_param_off(a[n]); +#endif #if !defined(TCC_TARGET_MACHO) if (variadic) { @@ -1241,6 +1342,7 @@ ST_FUNC void gfunc_prolog(Sym *func_sym) last_int = last_int > 4 ? 4 : last_int; last_float = last_float > 4 ? 4 : last_float; + arm64_func_start_offset = ind; o(0xa9b27bfd); // stp x29,x30,[sp,#-224]! for (i = 0; i < last_float; i++) // stp q0,q1,[sp,#16], stp q2,q3,[sp,#48] @@ -1277,11 +1379,11 @@ ST_FUNC void gfunc_prolog(Sym *func_sym) // HFAs of float and double need to be written differently: if (16 <= a[i] && a[i] < 32 && (sym->type.t & VT_BTYPE) == VT_STRUCT) { uint32_t j, sz, k = arm64_hfa(&sym->type, &sz); - if (sz < 16) + if (k > 0 && sz < 16) for (j = 0; j < k; j++) { o(0x3d0003e0 | -(sz & 8) << 27 | (sz & 4) << 29 | ((a[i] - 16) / 2 + j) | (off / sz + j) << 10); - // str ([sdq])(*),[sp,#(j * sz)] + // str ([sdq])(j),[sp,#(j * sz)] } } } @@ -1291,9 +1393,9 @@ ST_FUNC void gfunc_prolog(Sym *func_sym) o(0x910003fd); // mov x29,sp arm64_func_sub_sp_offset = ind; - // In gfunc_epilog these will be replaced with code to decrement SP: - o(0xd503201f); // nop - o(0xd503201f); // nop + /* In gfunc_epilog these will be replaced with stack setup code. */ + for (i = 0; i < ARM64_FUNC_STACK_SETUP_SLOTS; ++i) + o(ARM64_NOP); // nop loc = 0; #ifdef CONFIG_TCC_BCHECK if (tcc_state->do_bounds_check) @@ -1308,6 +1410,16 @@ ST_FUNC void gen_va_start(void) gaddrof(); r = intr(gv(RC_INT)); +#ifdef TCC_TARGET_PE + if (arm64_func_va_list_stack) { + arm64_movimm(30, arm64_func_va_list_stack); + o(0x8b1e03be); // add x30,x29,x30 + } else + o(0x910283be); // add x30,x29,#160 + o(0xf900001e | r << 5); // str x30,[x(r)] + --vtop; + return; +#else if (arm64_func_va_list_stack) { //xx could use add (immediate) here arm64_movimm(30, arm64_func_va_list_stack + 224); @@ -1337,6 +1449,7 @@ ST_FUNC void gen_va_start(void) #endif --vtop; +#endif } ST_FUNC void gen_va_arg(CType *t) @@ -1345,6 +1458,51 @@ ST_FUNC void gen_va_arg(CType *t) unsigned fsize, hfa = arm64_hfa(t, &fsize); uint32_t r0, r1; +#ifdef TCC_TARGET_PE + uint32_t slot = size; + int indirect = 0; + + if ((t->t & VT_BTYPE) == VT_STRUCT) { + if (size > 16) { + slot = 8; + indirect = 1; + } else { + slot = (size + 7) & -8; + } + } else if (slot > 16) { + slot = 8; + indirect = 1; + } else if (slot < 8) { + slot = 8; + } + + gaddrof(); + r0 = intr(gv(RC_INT)); + r1 = get_reg(RC_INT); + vtop[0].r = r1 | VT_LVAL; + r1 = intr(r1); + + o(ARM64_LDR_X | ARM64_RN(r0) | r1); // ldr x(r1),[x(r0)] // ap + if (slot) { + if (slot == 16) { + o(0x910363be); // add x30,x29,#216 + o(0xeb1e003f | r1 << 5); // cmp x(r1),x30 + o(0x54000041); // b.ne .+8 + o(0x910383a0 | r1 | 29 << 5); // add x(r1),x29,#224 + } + if (align == 16) { + o(0x91003c00 | r1 | r1 << 5); // add x(r1),x(r1),#15 + o(0x927cec00 | r1 | r1 << 5); // and x(r1),x(r1),#-16 + } + o(0x9100001e | r1 << 5 | slot << 10); // add x30,x(r1),#(slot) + o(0xf900001e | r0 << 5); // str x30,[x(r0)] // ap += slot + } + + if (indirect) + o(ARM64_LDR_X | ARM64_RN(r1) | r1); // ldr x(r1),[x(r1)] + return; +#endif + if (is_float(t->t)) { hfa = 1; fsize = size; @@ -1368,7 +1526,7 @@ ST_FUNC void gen_va_arg(CType *t) o(0x310003c0 | r1 | n << 10); // adds w(r1),w30,#(n) o(0x540000ad); // b.le .+20 #endif - o(0xf9400000 | r1 | r0 << 5); // ldr x(r1),[x(r0)] // __stack + o(ARM64_LDR_X | ARM64_RN(r0) | r1); // ldr x(r1),[x(r0)] // __stack if (align == 16) { o(0x91003c00 | r1 | r1 << 5); // add x(r1),x(r1),#15 o(0x927cec00 | r1 | r1 << 5); // and x(r1),x(r1),#-16 @@ -1376,13 +1534,13 @@ ST_FUNC void gen_va_arg(CType *t) o(0x9100001e | r1 << 5 | n << 10); // add x30,x(r1),#(n) o(0xf900001e | r0 << 5); // str x30,[x(r0)] // __stack #if !defined(TCC_TARGET_MACHO) - o(0x14000004); // b .+16 + o(ARM64_B | 4); // b .+16 o(0xb9001800 | r1 | r0 << 5); // str w(r1),[x(r0),#24] // __gr_offs o(0xf9400400 | r1 | r0 << 5); // ldr x(r1),[x(r0),#8] // __gr_top o(0x8b3ec000 | r1 | r1 << 5); // add x(r1),x(r1),w30,sxtw #endif if (size > 16) - o(0xf9400000 | r1 | r1 << 5); // ldr x(r1),[x(r1)] + o(ARM64_LDR_X | ARM64_RN(r1) | r1); // ldr x(r1),[x(r1)] } else { uint32_t ssz = (size + 7) & -(uint32_t)8; @@ -1393,7 +1551,7 @@ ST_FUNC void gen_va_arg(CType *t) o(0x310003c0 | r1 | rsz << 10); // adds w(r1),w30,#(rsz) b1 = ind; o(0x5400000d); // b.le lab1 #endif - o(0xf9400000 | r1 | r0 << 5); // ldr x(r1),[x(r0)] // __stack + o(ARM64_LDR_X | ARM64_RN(r0) | r1); // ldr x(r1),[x(r0)] // __stack if (fsize == 16) { o(0x91003c00 | r1 | r1 << 5); // add x(r1),x(r1),#15 o(0x927cec00 | r1 | r1 << 5); // and x(r1),x(r1),#-16 @@ -1401,7 +1559,7 @@ ST_FUNC void gen_va_arg(CType *t) o(0x9100001e | r1 << 5 | ssz << 10); // add x30,x(r1),#(ssz) o(0xf900001e | r0 << 5); // str x30,[x(r0)] // __stack #if !defined(TCC_TARGET_MACHO) - b2 = ind; o(0x14000000); // b lab2 + b2 = ind; o(ARM64_B); // b lab2 // lab1: write32le(cur_text_section->data + b1, 0x5400000d | (ind - b1) << 3); o(0xb9001c00 | r1 | r0 << 5); // str w(r1),[x(r0),#28] // __vr_offs @@ -1423,7 +1581,7 @@ ST_FUNC void gen_va_arg(CType *t) (uint32_t)(hfa != 3) << 21); // st(hfa) {v28.(s|d),...}[0],[x(r1)] } // lab2: - write32le(cur_text_section->data + b2, 0x14000000 | (ind - b2) >> 2); + write32le(cur_text_section->data + b2, ARM64_B | ((ind - b2) >> 2)); #endif } } @@ -1464,13 +1622,14 @@ ST_FUNC void gfunc_return(CType *func_type) } case 16: if ((func_type->t & VT_BTYPE) == VT_STRUCT) { - uint32_t j, sz, n = arm64_hfa(&vtop->type, &sz); + /* HFA struct return - load from the address on vtop into float registers */ + uint32_t j, sz, n = arm64_hfa(func_type, &sz); gaddrof(); gv(RC_R(0)); for (j = 0; j < n; j++) o(0x3d400000 | (sz & 16) << 19 | -(sz & 8) << 27 | (sz & 4) << 29 | - j | j << 10); // ldr ([sdq])(*),[x0,#(j * sz)] + (fltr(REG_FRET) + j) | j << 10); // ldr ([sdq])(j),[x0,#(j * sz)] } else gv(RC_FRET); @@ -1483,39 +1642,22 @@ ST_FUNC void gfunc_return(CType *func_type) ST_FUNC void gfunc_epilog(void) { + int i; #ifdef CONFIG_TCC_BCHECK if (tcc_state->do_bounds_check) gen_bounds_epilog(); #endif if (loc) { - // Insert instructions to subtract size of stack frame from SP. - unsigned char *ptr = cur_text_section->data + arm64_func_sub_sp_offset; + /* Insert instructions to subtract the stack frame from SP. */ + addr_t saved_ind = ind; + addr_t patch_end = arm64_func_sub_sp_offset + ARM64_FUNC_STACK_SETUP_SLOTS * 4; uint64_t diff = (-loc + 15) & ~15; - if (!(diff >> 24)) { - if (diff & 0xfff) // sub sp,sp,#(diff & 0xfff) - write32le(ptr, 0xd10003ff | (diff & 0xfff) << 10); - if (diff >> 12) // sub sp,sp,#(diff >> 12),lsl #12 - write32le(ptr + 4, 0xd14003ff | (diff >> 12) << 10); - } - else { - // In this case we may subtract more than necessary, - // but always less than 17/16 of what we were aiming for. - int i = 0; - int j = 0; - while (diff >> 20) { - diff = (diff + 0xffff) >> 16; - ++i; - } - while (diff >> 16) { - diff = (diff + 1) >> 1; - ++j; - } - write32le(ptr, 0xd2800010 | diff << 5 | i << 21); - // mov x16,#(diff),lsl #(16 * i) - write32le(ptr + 4, 0xcb3063ff | j << 10); - // sub sp,sp,x16,lsl #(j) - } + ind = arm64_func_sub_sp_offset; + arm64_sub_sp(diff); + for (i = ind; i < patch_end; i += 4) + write32le(cur_text_section->data + i, ARM64_NOP); // nop + ind = saved_ind; } o(0x910003bf); // mov sp,x29 o(0xa8ce7bfd); // ldp x29,x30,[sp],#224 @@ -1524,8 +1666,7 @@ ST_FUNC void gfunc_epilog(void) #ifdef TCC_TARGET_PE { - unsigned start = arm64_func_sub_sp_offset - 8; /* account for prolog size */ - pe_add_unwind_data(start, ind, -loc); + pe_add_unwind_data(arm64_func_start_offset, ind, -loc); } #endif } @@ -1535,7 +1676,7 @@ ST_FUNC void gen_fill_nops(int bytes) if ((bytes & 3)) tcc_error("alignment of code section not multiple of 4"); while (bytes > 0) { - o(0xd503201f); // nop + o(ARM64_NOP); // nop bytes -= 4; } } @@ -1554,7 +1695,7 @@ ST_FUNC int gjmp(int t) ST_FUNC void gjmp_addr(int a) { assert(a - ind + 0x8000000 < 0x10000000); - o(0x14000000 | ((a - ind) >> 2 & 0x3ffffff)); + o(ARM64_B | (((a - ind) >> 2) & 0x3ffffff)); } ST_FUNC int gjmp_append(int n, int t) @@ -1657,9 +1798,9 @@ static int arm64_gen_opic(int op, uint32_t l, int rev, uint64_t val, uint32_t s = l ? val >> 63 : val >> 31; val = s ? -val : val; val = l ? val : (uint32_t)val; - if (!(val & ~(uint64_t)0xfff)) + if (!(val & ~0xffful)) o(0x11000000 | l << 31 | s << 30 | x | a << 5 | val << 10); - else if (!(val & ~(uint64_t)0xfff000)) + else if (!(val & ~0xfff000ul)) o(0x11400000 | l << 31 | s << 30 | x | a << 5 | val >> 12 << 10); else { arm64_movimm(30, val); // use x30 @@ -2084,11 +2225,8 @@ ST_FUNC void gen_increment_tcov (SValue *sv) vpushv(sv); vtop->r = r1 = get_reg(RC_INT); r2 = get_reg(RC_INT); - greloca(cur_text_section, sv->sym, ind, R_AARCH64_ADR_GOT_PAGE, 0); - o(0x90000000 | r1); // adrp r1, #sym - greloca(cur_text_section, sv->sym, ind, R_AARCH64_LD64_GOT_LO12_NC, 0); - o(0xf9400000 | r1 | (r1 << 5)); // ld xr,[xr, #sym] - o(0xf9400000 | (intr(r1)<<5) | intr(r2)); // ldr r2, [r1] + arm64_sym(r1, sv->sym, 0); + o(ARM64_LDR_X | ARM64_RN(intr(r1)) | intr(r2)); // ldr r2, [r1] o(0x91000400 | (intr(r2)<<5) | intr(r2)); // add r2, r2, #1 o(0xf9000000 | (intr(r1)<<5) | intr(r2)); // str r2, [r1] vpop(); @@ -2125,21 +2263,21 @@ ST_FUNC void gen_clear_cache(void) o(0x1ac02000 | isz | p << 5 | isz << 16); // lsl w(isz),w(p),w(isz) o(0x51000400 | p | dsz << 5); // sub w(p),w(dsz),#1 o(0x8a240004 | p | beg << 5 | p << 16); // bic x(p),x(beg),x(p) - b1 = ind; o(0x14000000); // b + b1 = ind; o(ARM64_B); // b lab1 = ind; o(0xd50b7b20 | p); // dc cvau,x(p) o(0x8b000000 | p | p << 5 | dsz << 16); // add x(p),x(p),x(dsz) - write32le(cur_text_section->data + b1, 0x14000000 | (ind - b1) >> 2); + write32le(cur_text_section->data + b1, ARM64_B | ((ind - b1) >> 2)); o(0xeb00001f | p << 5 | end << 16); // cmp x(p),x(end) o(0x54ffffa3 | ((lab1 - ind) << 3 & 0xffffe0)); // b.cc lab1 o(0xd5033b9f); // dsb ish o(0x51000400 | p | isz << 5); // sub w(p),w(isz),#1 o(0x8a240004 | p | beg << 5 | p << 16); // bic x(p),x(beg),x(p) - b1 = ind; o(0x14000000); // b + b1 = ind; o(ARM64_B); // b lab1 = ind; o(0xd50b7520 | p); // ic ivau,x(p) o(0x8b000000 | p | p << 5 | isz << 16); // add x(p),x(p),x(isz) - write32le(cur_text_section->data + b1, 0x14000000 | (ind - b1) >> 2); + write32le(cur_text_section->data + b1, ARM64_B | ((ind - b1) >> 2)); o(0xeb00001f | p << 5 | end << 16); // cmp x(p),x(end) o(0x54ffffa3 | ((lab1 - ind) << 3 & 0xffffe0)); // b.cc lab1 o(0xd5033b9f); // dsb ish diff --git a/arm64-link.c b/arm64-link.c index cfdd95ea05..0b807c4b60 100644 --- a/arm64-link.c +++ b/arm64-link.c @@ -50,6 +50,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; @@ -61,7 +63,7 @@ ST_FUNC int code_reloc (int reloc_type) ST_FUNC int gotplt_entry_type (int reloc_type) { switch (reloc_type) { - case R_AARCH64_PREL32: + case R_AARCH64_PREL32: case R_AARCH64_MOVW_UABS_G0_NC: case R_AARCH64_MOVW_UABS_G1_NC: case R_AARCH64_MOVW_UABS_G2_NC: @@ -76,6 +78,8 @@ 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: return NO_GOTPLT_ENTRY; case R_AARCH64_ABS32: @@ -127,17 +131,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 +151,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 +243,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 +285,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); + 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) { +#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 - if (((val - addr) + ((uint64_t)1 << 27)) & ~(uint64_t)0xffffffc) + 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 + diff --git a/arm64-tok.h b/arm64-tok.h new file mode 100644 index 0000000000..9270e0fb56 --- /dev/null +++ b/arm64-tok.h @@ -0,0 +1,839 @@ +/* ------------------------------------------------------------------ */ +/* 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) */ +#define TCC_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 */ diff --git a/configure b/configure index 7c51343870..00a8065408 100755 --- a/configure +++ b/configure @@ -57,6 +57,7 @@ build_cross= # use CC/AR from environment when set test -n "$CC" && cc="$CC" test -n "$AR" && ar="$AR" +host_cc="${CC:-$cc}" # host_cc defaults to cc (which may have been overridden by $CC above) host_cc="${CC:-$cc}" @@ -269,7 +270,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 diff --git a/include/tccdefs.h b/include/tccdefs.h index d7596ac650..0d48b47dbf 100644 --- a/include/tccdefs.h +++ b/include/tccdefs.h @@ -263,7 +263,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; diff --git a/lib/Makefile b/lib/Makefile index 61089bcb67..04b93e0b0c 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -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 runrt.o LIN_O = dsohandle.o OSX_O = @@ -65,6 +65,7 @@ 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-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) @@ -99,6 +100,11 @@ $(TOP)/bcheck.o : XFLAGS += $(BFLAGS) $(X)crt1w.o : crt1.c $(X)wincrt1w.o : wincrt1.c +ifeq ($(T),arm64-win32) +$(TOP)/runmain.o : runmain-arm64.S $(TCC) + $S$(XCC) -c $< -o $@ $(XFLAGS) +endif + # don't try to make it $(TCC) : ; diff --git a/lib/bcheck.c b/lib/bcheck.c index cd7c8a792a..e329753d3a 100644 --- a/lib/bcheck.c +++ b/lib/bcheck.c @@ -351,6 +351,7 @@ static unsigned char print_heap; static unsigned char print_statistic; static unsigned char no_strdup; static unsigned char use_sem; +/* This counter is toggled from runtime/error paths and must remain atomic. */ static _Atomic int never_fatal; #if HAVE_TLS_FUNC #if defined(_WIN32) @@ -906,7 +907,11 @@ static void __bound_long_jump(jmp_buf env, int val, int sig, const char *func) #if !defined(_WIN32) sig ? siglongjmp(env, val) : #endif +#if defined(_WIN32) && defined(__aarch64__) + __mingw_longjmp(env, val); +#else longjmp (env, val); +#endif } void __bound_longjmp(jmp_buf env, int val) diff --git a/lib/bt-dll.c b/lib/bt-dll.c index 7c62cefad0..dce26e95af 100644 --- a/lib/bt-dll.c +++ b/lib/bt-dll.c @@ -3,10 +3,12 @@ #include #include +#include #define REDIR_ALL \ REDIR(__bt_init) \ REDIR(__bt_exit) \ + REDIR(__bt_backtrace) \ REDIR(tcc_backtrace) \ \ REDIR(__bound_ptr_add) \ @@ -48,8 +50,103 @@ static struct { REDIR_ALL } all_ptrs; #define REDIR(s) #s"\0" static const char all_names[] = REDIR_ALL; #undef REDIR +#if defined(__aarch64__) +typedef struct rt_context rt_context; +typedef struct rt_frame { + void *ip, *fp, *sp; +} rt_frame; +#ifndef FASTCALL +#define FASTCALL +#endif +#define REDIR_WRAP(ret, name, decl_args, type_args, call_args) \ + ret name decl_args \ + { \ + typedef ret (*fn_t) type_args; \ + return ((fn_t)all_ptrs.name) call_args; \ + } + +static void all_jmps(void) { + /* ARM64 uses C wrappers instead of instruction trampolines. */ +} + +REDIR_WRAP(void, __bt_init, (rt_context *p, int is_exe), + (rt_context *, int), (p, is_exe)) +REDIR_WRAP(void, __bt_exit, (rt_context *p), + (rt_context *), (p)) +REDIR_WRAP(int, __bt_backtrace, (rt_frame *f, const char *msg), + (rt_frame *, const char *), (f, msg)) + +#define REDIR_PTR_INDIR(name) \ + REDIR_WRAP(void *, name, (void *p, size_t offset), \ + (void *, size_t), (p, offset)) + +REDIR_PTR_INDIR(__bound_ptr_add) +REDIR_PTR_INDIR(__bound_ptr_indir1) +REDIR_PTR_INDIR(__bound_ptr_indir2) +REDIR_PTR_INDIR(__bound_ptr_indir4) +REDIR_PTR_INDIR(__bound_ptr_indir8) +REDIR_PTR_INDIR(__bound_ptr_indir12) +REDIR_PTR_INDIR(__bound_ptr_indir16) + +REDIR_WRAP(void FASTCALL, __bound_local_new, (void *p1), + (void *), (p1)) +REDIR_WRAP(void FASTCALL, __bound_local_delete, (void *p1), + (void *), (p1)) +REDIR_WRAP(void, __bound_new_region, (void *p, size_t size), + (void *, size_t), (p, size)) + +REDIR_WRAP(void, __bound_free, (void *ptr, const void *caller), + (void *, const void *), (ptr, caller)) +REDIR_WRAP(void *, __bound_malloc, (size_t size, const void *caller), + (size_t, const void *), (size, caller)) +REDIR_WRAP(void *, __bound_realloc, (void *ptr, size_t size, const void *caller), + (void *, size_t, const void *), (ptr, size, caller)) +REDIR_WRAP(void *, __bound_memcpy, (void *dst, const void *src, size_t size), + (void *, const void *, size_t), (dst, src, size)) +REDIR_WRAP(int, __bound_memcmp, (const void *s1, const void *s2, size_t size), + (const void *, const void *, size_t), (s1, s2, size)) +REDIR_WRAP(void *, __bound_memmove, (void *dst, const void *src, size_t size), + (void *, const void *, size_t), (dst, src, size)) +REDIR_WRAP(void *, __bound_memset, (void *dst, int c, size_t size), + (void *, int, size_t), (dst, c, size)) +REDIR_WRAP(int, __bound_strlen, (const char *s), + (const char *), (s)) +REDIR_WRAP(char *, __bound_strcpy, (char *dst, const char *src), + (char *, const char *), (dst, src)) +REDIR_WRAP(char *, __bound_strncpy, (char *dst, const char *src, size_t n), + (char *, const char *, size_t), (dst, src, n)) +REDIR_WRAP(int, __bound_strcmp, (const char *s1, const char *s2), + (const char *, const char *), (s1, s2)) +REDIR_WRAP(int, __bound_strncmp, (const char *s1, const char *s2, size_t n), + (const char *, const char *, size_t), (s1, s2, n)) +REDIR_WRAP(char *, __bound_strcat, (char *dest, const char *src), + (char *, const char *), (dest, src)) +REDIR_WRAP(char *, __bound_strchr, (const char *string, int ch), + (const char *, int), (string, ch)) +REDIR_WRAP(char *, __bound_strdup, (const char *s), + (const char *), (s)) + +int tcc_backtrace(const char *fmt, ...) +{ + char buf[1024]; + rt_frame f; + va_list ap; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + f.ip = __builtin_return_address(0); + f.fp = __builtin_frame_address(1); + f.sp = __builtin_frame_address(0); + return __bt_backtrace(&f, buf); +} + +#undef REDIR_PTR_INDIR +#undef REDIR_WRAP +#else #define REDIR(s) __asm__(".global " _(s) ";" _(s) ": jmp *%0" : : "m" (all_ptrs.s) ); static void all_jmps() { REDIR_ALL } +#endif #undef REDIR void __bt_init_dll(int bcheck) diff --git a/lib/bt-exe.c b/lib/bt-exe.c index 71c9c19c35..6542bfd029 100644 --- a/lib/bt-exe.c +++ b/lib/bt-exe.c @@ -11,19 +11,46 @@ # define __declspec(n) #endif +#if defined(_WIN64) && defined(__aarch64__) +/* The bt-only Windows ARM64 build should not rely on importing this helper. */ +LONG InterlockedExchange(LONG volatile *Target, LONG Value) +{ + LONG Old = *Target; + *Target = Value; + return Old; +} +#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) { __attribute__((weak)) int main(); __attribute__((weak)) void __bound_init(void*, int); - //fprintf(stderr, "__bt_init %d %p %p %p\n", is_exe, p, p->stab_sym, p->bounds_start), fflush(stderr); - /* call __bound_init here due to redirection of sigaction */ /* needed to add global symbols */ 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; @@ -35,6 +62,73 @@ void __bt_init(rt_context *p, int is_exe) } } +#ifdef _WIN32 +static int bt_backtrace_msg(rt_frame *f, const char *fmt, const char *msg) +{ + rt_context *rc, *rc2; + addr_t pc; + char skip[40]; + int i, level, ret, n, one; + const char *a; + bt_info bi; + addr_t (*getinfo)(rt_context*, addr_t, bt_info*); + + rt_backtrace_format(fmt, skip, &one); + + rt_wait_sem(); + rc = g_rc; + getinfo = rt_printline, n = 6; + if (rc) { + if (rc->dwarf) + getinfo = rt_printline_dwarf; + if (rc->num_callers) + n = rc->num_callers; + } + + for (i = level = 0; level < n; i++) { + ret = rt_get_caller_pc(&pc, f, i); + if (ret == -1) + break; + memset(&bi, 0, sizeof bi); + for (rc2 = rc; rc2; rc2 = rc2->next) { + if (getinfo(rc2, pc, &bi)) + break; + if (!!(a = rt_elfsym(rc2, pc, &bi.func_pc))) { + pstrcpy(bi.func, sizeof bi.func, a); + break; + } + } + if (skip[0] && strstr(bi.file, skip)) + continue; + if (bi.file[0]) { + rt_printf("%s:%d", bi.file, bi.line); + } else { + rt_printf("0x%08llx", (long long)pc); + } + rt_printf(": %s %s", level ? "by" : "at", bi.func[0] ? bi.func : "???"); + if (level == 0) { + rt_printf(": %s", msg); + if (one) + break; + } + rt_printf("\n"); + if (rc2 + && bi.func_pc + && bi.func_pc == (addr_t)rc2->top_func) + break; + ++level; + } + rt_post_sem(); + return 0; +} + +__declspec(dllexport) +int __bt_backtrace(rt_frame *f, const char *msg) +{ + return bt_backtrace_msg(f, msg, msg); +} +#endif + __declspec(dllexport) void __bt_exit(rt_context *p) { diff --git a/lib/libtcc1.c b/lib/libtcc1.c index 85c95df9ff..4bbd1cc741 100644 --- a/lib/libtcc1.c +++ b/lib/libtcc1.c @@ -630,6 +630,12 @@ long long __fixxfdi (long double a1) /* MSVC x64 intrinsic */ void __faststorefence(void) { - __asm__("lock; orl $0,(%rsp)"); +#if defined(__aarch64__) + /* ARM64: Data Memory Barrier (Inner Shareable) */ + __asm__("dmb ish"); +#else + /* x86-64: lock prefix to flush store buffer */ + __asm__("lock; orl $0,(%%rsp)" ::: "memory"); +#endif } #endif diff --git a/libtcc.c b/libtcc.c index 8604f5f0b4..8a376e8ba9 100644 --- a/libtcc.c +++ b/libtcc.c @@ -905,6 +905,9 @@ LIBTCCAPI TCCState *tcc_new(void) #ifdef TCC_TARGET_ARM s->float_abi = ARM_FLOAT_ABI; #endif +#ifdef TCC_IS_NATIVE + s->run_arg_start = -1; +#endif #ifdef CONFIG_NEW_DTAGS s->enable_new_dtags = 1; #endif diff --git a/tcc-doc.texi b/tcc-doc.texi index 9a9695f76d..267a0d3c89 100644 --- a/tcc-doc.texi +++ b/tcc-doc.texi @@ -175,7 +175,6 @@ In a script, it gives the following header: @example #!/usr/local/bin/tcc -run -L/usr/X11R6/lib -lX11 @end example - @item -v Display TCC version. diff --git a/tcc.c b/tcc.c index e1819239d6..b18152b95b 100644 --- a/tcc.c +++ b/tcc.c @@ -231,6 +231,36 @@ static void print_search_dirs(TCCState *s) static void set_environment(TCCState *s) { +#ifdef _WIN32 + static const char * const names[] = { + "C_INCLUDE_PATH", + "CPATH", + "LIBRARY_PATH", + }; + int i; + + for (i = 0; i < sizeof(names) / sizeof(names[0]); ++i) { + DWORD len = GetEnvironmentVariableA(names[i], NULL, 0); + char *path; + + if (!len) + continue; + path = tcc_malloc(len); + if (!path) + continue; + if (!GetEnvironmentVariableA(names[i], path, len)) { + tcc_free(path); + continue; + } + if (i == 0) + tcc_add_sysinclude_path(s, path); + else if (i == 1) + tcc_add_include_path(s, path); + else + tcc_add_library_path(s, path); + tcc_free(path); + } +#else char * path; path = getenv("C_INCLUDE_PATH"); @@ -245,6 +275,7 @@ static void set_environment(TCCState *s) if(path != NULL) { tcc_add_library_path(s, path); } +#endif } static char *default_outputfile(TCCState *s, const char *first_file) @@ -299,6 +330,9 @@ int main(int argc, char **argv) argc = argc0, argv = argv0; s = s1 = tcc_new(); opt = tcc_parse_args(s, &argc, &argv); +#ifdef TCC_IS_NATIVE + s->run_arg_start = (int)(argv - argv0); +#endif if (n == 0) { ret = 0; diff --git a/tcc.h b/tcc.h index 491b3f7975..84f96b2535 100644 --- a/tcc.h +++ b/tcc.h @@ -23,6 +23,9 @@ #define _GNU_SOURCE #define _DARWIN_C_SOURCE +#if defined(_M_ARM64) && !defined(__aarch64__) +# define __aarch64__ 1 +#endif #include "config.h" #include @@ -505,6 +508,7 @@ struct SymAttr { unsigned short aligned : 5, /* alignment as log2+1 (0 == unspecified) */ packed : 1, + typedef_align : 1, /* alignment came from a typedef'ed type */ weak : 1, visibility : 2, dllexport : 1, @@ -512,7 +516,7 @@ struct SymAttr { dllimport : 1, addrtaken : 1, nodebug : 1, - xxxx : 2; /* not used */ + xxxx : 1; /* not used */ }; /* function attributes or temporary attributes for parsing */ @@ -592,7 +596,7 @@ typedef struct Section { typedef struct DLLReference { int level; void *handle; - unsigned char found, index; + unsigned char found, index, process_scoped; char name[1]; } DLLReference; @@ -952,6 +956,7 @@ struct TCCState { # if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_ARM64) Section *uw_pdata; int uw_sym; + int uw_xsym; unsigned uw_offs; # endif #endif @@ -974,6 +979,7 @@ struct TCCState { #ifdef TCC_IS_NATIVE const char *run_main; /* entry for tcc_run() */ + int run_arg_start; /* argv index for tcc -run relative to the host command line */ void *run_ptr; /* runtime_memory */ unsigned run_size; /* size of runtime_memory */ const char *run_stdin; /* custom stdin file for run_main */ diff --git a/tccasm.c b/tccasm.c index 523cbab0ce..e9dad5ba41 100644 --- a/tccasm.c +++ b/tccasm.c @@ -1032,11 +1032,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; @@ -1175,7 +1178,10 @@ static void subst_asm_operands(ASMOperand *operands, int nb_operands, modifier = 0; if (*str == 'c' || *str == 'n' || *str == 'b' || *str == 'w' || *str == 'h' || *str == 'k' || - *str == 'q' || *str == 'l' || + *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 +1202,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 +1254,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); } } diff --git a/tccgen.c b/tccgen.c index fce4288f42..1ac117fa04 100644 --- a/tccgen.c +++ b/tccgen.c @@ -1177,8 +1177,10 @@ ST_FUNC void vpush_helper_func(int v) /* Merge symbol attributes. */ static void merge_symattr(struct SymAttr *sa, struct SymAttr *sa1) { - if (sa1->aligned && !sa->aligned) + if (sa1->aligned && !sa->aligned) { sa->aligned = sa1->aligned; + sa->typedef_align = sa1->typedef_align; + } sa->packed |= sa1->packed; sa->weak |= sa1->weak; sa->nodebug |= sa1->nodebug; @@ -1691,6 +1693,7 @@ ST_FUNC void gbound_args(int nb_args) v = sv->sym->v; if (v == TOK_setjmp || v == TOK__setjmp + || v == TOK___mingw_setjmp #ifndef TCC_TARGET_PE || v == TOK_sigsetjmp || v == TOK___sigsetjmp @@ -4003,6 +4006,7 @@ static void parse_attribute(AttributeDef *ad) n = MAX_ALIGN; } ad->a.aligned = exact_log2p1(n); + ad->a.typedef_align = 0; if (n != 1 << (ad->a.aligned - 1)) tcc_error("alignment of %d is larger than implemented", n); break; @@ -4228,8 +4232,17 @@ static void struct_layout(CType *type, AttributeDef *ad) } } /* some individual align was specified */ +#ifdef TCC_TARGET_PE + /* GNU aligned(n) on a field is a minimum, not a way to lower alignment. + Typedef-level alignment changes the type alignment itself. */ + if (f->a.typedef_align && a) + align = a; + else if (a > align) + align = a; +#else if (a) align = a; +#endif if (type->ref->type.t == VT_UNION) { if (pcc && bit_size >= 0) @@ -4701,7 +4714,10 @@ static void struct_decl(CType *type, int u) static void sym_to_attr(AttributeDef *ad, Sym *s) { + int had_aligned = ad->a.aligned; merge_symattr(&ad->a, &s->a); + if (!had_aligned && (s->type.t & VT_TYPEDEF) && s->a.aligned) + ad->a.typedef_align = 1; merge_funcattr(&ad->f, &s->f); } diff --git a/tccpe.c b/tccpe.c index fce8e296b8..5c9f3d5e31 100644 --- a/tccpe.c +++ b/tccpe.c @@ -29,6 +29,53 @@ #include /* chmod() */ #endif +#if defined(_WIN32) && defined(TCC_IS_NATIVE) && defined(TCC_TARGET_ARM64) +static TCCSem pe_msvcrt_sem; + +static HMODULE pe_get_process_msvcrt_handle(void) +{ + static HMODULE handle; + HMODULE dll; + + wait_sem(&pe_msvcrt_sem); + dll = handle; + if (!dll) { + if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_PIN, "msvcrt.dll", &dll)) { + handle = dll; + } else { + dll = LoadLibraryA("msvcrt.dll"); + if (dll) { + HMODULE pinned; + /* Keep one process-lifetime handle so later states do not cache + an unloadable msvcrt handle after the first state is deleted. */ + if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_PIN, "msvcrt.dll", &pinned)) { + FreeLibrary(dll); + dll = pinned; + } + handle = dll; + } + } + } + post_sem(&pe_msvcrt_sem); + return dll; +} + +static HMODULE pe_load_runtime_dll(const char *name, unsigned char *process_scoped) +{ + HMODULE dll = NULL; + + *process_scoped = 0; + if (0 == PATHCMP(tcc_basename(name), "msvcrt.dll")) { + dll = pe_get_process_msvcrt_handle(); + if (dll) + *process_scoped = 1; + } + if (!dll) + dll = LoadLibraryA(name); + return dll; +} +#endif + #ifdef TCC_TARGET_X86_64 # define ADDR3264 ULONGLONG # define PE_IMAGE_REL IMAGE_REL_BASED_DIR64 @@ -235,6 +282,19 @@ typedef struct _IMAGE_BASE_RELOCATION { #define IMAGE_SIZEOF_BASE_RELOCATION 8 +#ifndef IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA +#define IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA 0x0020 +#endif +#ifndef IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE +#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040 +#endif +#ifndef IMAGE_DLLCHARACTERISTICS_NX_COMPAT +#define IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100 +#endif +#ifndef IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE +#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000 +#endif + #define IMAGE_REL_BASED_ABSOLUTE 0 #define IMAGE_REL_BASED_HIGH 1 #define IMAGE_REL_BASED_LOW 2 @@ -260,6 +320,9 @@ typedef struct _IMAGE_BASE_RELOCATION { #endif /* ndef IMAGE_NT_SIGNATURE */ /* ----------------------------------------------------------- */ +#ifndef IMAGE_FILE_MACHINE_ARM64 +#define IMAGE_FILE_MACHINE_ARM64 0xAA64 +#endif #ifndef IMAGE_REL_BASED_DIR64 # define IMAGE_REL_BASED_DIR64 10 #endif @@ -617,8 +680,8 @@ static int pe_write(struct pe_info *pe) #define CHARACTERISTICS_DLL 0x230F #elif defined(TCC_TARGET_ARM64) 0x00F0, /*WORD SizeOfOptionalHeader; */ - 0x022F /*WORD Characteristics; */ -#define CHARACTERISTICS_DLL 0x222E + 0x0022 /*WORD Characteristics; */ +#define CHARACTERISTICS_DLL 0x2022 #endif },{ /* IMAGE_OPTIONAL_HEADER opthdr */ @@ -641,23 +704,42 @@ static int pe_write(struct pe_info *pe) /* 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; */ 0x00000000, /*DWORD CheckSum; */ 0x0002, /*WORD Subsystem; */ +#if defined(TCC_TARGET_ARM64) + IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA | + IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE | + IMAGE_DLLCHARACTERISTICS_NX_COMPAT | + IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE, +#else 0x0000, /*WORD DllCharacteristics; */ +#endif 0x00100000, /*DWORD SizeOfStackReserve; */ 0x00001000, /*DWORD SizeOfStackCommit; */ 0x00100000, /*DWORD SizeOfHeapReserve; */ @@ -948,8 +1030,14 @@ static void pe_build_imports(struct pe_info *pe) #ifdef TCC_IS_NATIVE if (pe->type == PE_RUN) { if (dllref) { - if ( !dllref->handle ) + if (!dllref->handle) { +#if defined(_WIN32) && defined(TCC_TARGET_ARM64) + dllref->handle = pe_load_runtime_dll(dllref->name, + &dllref->process_scoped); +#else dllref->handle = LoadLibraryA(dllref->name); +#endif + } v = (ADDR3264)GetProcAddress(dllref->handle, ordinal?(char*)0+ordinal:name); } if (!v) @@ -1205,7 +1293,11 @@ 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 +#ifdef TCC_TARGET_ARM64 + || PE_EXE == pe->type || PE_GUI == pe->type +#endif + ) pe->reloc = new_section(s1, ".reloc", SHT_PROGBITS, 0); //pe->thunk = new_section(s1, ".iedat", SHT_PROGBITS, SHF_ALLOC); @@ -1394,14 +1486,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, 16); - /* ldr x16, [pc, #8] */ - write32le(p + 0, 0x58000050); +#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 + 4, 0xd61f0200); + write32le(p + 8, 0xd61f0200); + /* nop for 8-byte literal alignment */ + write32le(p + 12, 0xd503201f); put_elf_reloc(symtab_section, text_section, - offset + 8, R_XXX_THUNKFIX, is->iat_index); + offset + 16, R_XXX_THUNKFIX, is->iat_index); #else p = section_ptr_add(text_section, 8); write16le(p, 0x25FF); @@ -1633,7 +1729,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)) @@ -1856,7 +1952,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"); @@ -1901,7 +1997,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); @@ -1915,7 +2011,7 @@ 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 +#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 @@ -1923,54 +2019,88 @@ ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack) alloc_m: 11000iii xxxxxxxx - sub sp,sp,#X*16 (up to 32KB) end: 11100100 - end of unwind codes */ -static unsigned pe_add_uwwind_info(TCCState *s1) +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"); + if (NULL == s) { + s = new_section(s1, ".xdata", SHT_PROGBITS, SHF_ALLOC); + 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_base"); - if (0 == s1->uw_offs) { - /* TCC ARM64 prolog: stp x29,lr,[sp,#-224]!; mov x29,sp; sub sp,sp,#N - Unwind codes (reverse order): alloc_s, set_fp, save_fplr_x, end */ - static const unsigned char uw_info[] = { - /* .xdata header word 0: - FunctionLength[17:0]=0 (patched), Vers[1:0]=0, X=0, E=1, - EpilogCount[4:0]=0, CodeWords[4:0]=1 */ - 0x00, 0x00, 0x00, 0x01, - /* Unwind codes (4 bytes, padded): */ - 0xE1, /* set_fp: mov x29,sp */ - 0x9B, /* save_fplr_x: stp x29,lr,[sp,#-224]! (224/8-1=27=0x1B) */ - 0xE4, /* end */ - 0xE3, /* nop (padding) */ - }; - - Section *s = text_section; - unsigned char *p; - - section_ptr_add(s, -s->data_offset & 3); /* align */ - s1->uw_offs = s->data_offset; - p = section_ptr_add(s, sizeof uw_info); - memcpy(p, uw_info, sizeof uw_info); - } - return s1->uw_offs; + 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; - unsigned o, n, d; + Section *pd, *xd; + unsigned o, n, d, code_bytes, func_len, stack_slots; + unsigned char *q; + uint32_t header; struct { DWORD BeginAddress; DWORD EndAddress; DWORD UnwindData; } *p; - d = pe_add_uwwind_info(s1); + xd = pe_add_unwind_info(s1); pd = s1->uw_pdata; + + stack = (stack + 15) & ~15; + stack_slots = stack >> 4; + func_len = (end - start) >> 2; + code_bytes = 0; + if (stack_slots) { + if (stack_slots <= 31) { + code_bytes += 1; + } else if (stack_slots <= 0x7ff) { + code_bytes += 2; + } else { + code_bytes += 4; + } + } + 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) | (1u << 21) | ((code_bytes >> 2) << 27); + write32le(q, header); + q += 4; + + if (stack_slots) { + if (stack_slots <= 31) { + *q++ = stack_slots; /* alloc_s */ + } else if (stack_slots <= 0x7ff) { + *q++ = 0xC0 | (stack_slots >> 8); /* alloc_m */ + *q++ = stack_slots & 0xff; + } else { + *q++ = 0xE0; /* alloc_l */ + *q++ = (stack_slots >> 16) & 0xff; + *q++ = (stack_slots >> 8) & 0xff; + *q++ = stack_slots & 0xff; + } + } + *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); @@ -1978,8 +2108,9 @@ ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack) p->EndAddress = end; p->UnwindData = d; - for (n = o + sizeof *p; o < n; o += sizeof p->BeginAddress) + for (n = o + 2 * sizeof p->BeginAddress; o < n; o += sizeof p->BeginAddress) put_elf_reloc(symtab_section, pd, o, R_XXX_RELATIVE, s1->uw_sym); + put_elf_reloc(symtab_section, pd, n, R_XXX_RELATIVE, s1->uw_xsym); } #endif /* ------------------------------------------------------------- */ @@ -2108,10 +2239,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 diff --git a/tccpp.c b/tccpp.c index 2df9ed8bd8..96f1b4749e 100644 --- a/tccpp.c +++ b/tccpp.c @@ -942,11 +942,11 @@ static void preprocess_skip(void) 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: @@ -2522,9 +2522,13 @@ static void parse_number(const char *p) tcc_error("invalid digit"); n1 = n; n = n * b + t; - /* detect overflow */ - if (n1 >= 0x1000000000000000ULL && n / b != n1) - ov = 1; + if (!ov) { + /* detect overflow */ + if (n1 >= 0x1000000000000000ULL && n / b != n1) + ov = 1; + else + n1 = n; + } } /* Determine the characteristics (unsigned and/or 64bit) the type of @@ -2698,7 +2702,7 @@ static void next_nomacro(void) 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; diff --git a/tccrun.c b/tccrun.c index 53118f7906..ecdfd46dae 100644 --- a/tccrun.c +++ b/tccrun.c @@ -19,6 +19,15 @@ */ #include "tcc.h" +#ifdef _WIN32 +#include +# if !defined(_UCRT) && !defined(_ARM_) && !defined(__arm__) && !defined(_ARM64_) && !defined(__aarch64__) && !defined(_ARM64EC_) && !defined(__arm64ec__) +/* MinGW's x86_64 msvcrt headers prefer __p__environ(), but msvcrt.dll exports + _get_environ() directly, which is what the TCC runtime can reliably import. */ +_CRTIMP errno_t __cdecl _get_environ(char ***); +_CRTIMP errno_t __cdecl _get_wenviron(wchar_t ***); +# endif +#endif /* only native compiler supports -run */ #ifdef TCC_IS_NATIVE @@ -70,6 +79,9 @@ static void rt_wait_sem(void) { WAIT_SEM(&rt_sem); } static void rt_post_sem(void) { POST_SEM(&rt_sem); } static int rt_get_caller_pc(addr_t *paddr, rt_frame *f, int level); static void rt_exit(rt_frame *f, int code); +#if defined(_WIN64) && defined(__aarch64__) && !defined(CONFIG_TCC_BACKTRACE_ONLY) +static void rt_restore_context_from_jmpbuf(void *p_jmp_buf, int code); +#endif /* ------------------------------------------------------------- */ /* defined when included from lib/bt-exe.c */ @@ -174,6 +186,10 @@ ST_FUNC void tcc_run_free(TCCState *s1) DLLReference *ref = s1->loaded_dlls[i]; if ( ref->handle ) #ifdef _WIN32 +# if defined(__aarch64__) + if (ref->process_scoped) + continue; +# endif FreeLibrary((HMODULE)ref->handle); #else dlclose(ref->handle); @@ -199,6 +215,70 @@ ST_FUNC void tcc_run_free(TCCState *s1) #define RT_EXIT_ZERO 0xE0E00E0E /* passed from longjmp instead of '0' */ +#ifdef _WIN32 +static char **rt_get_environ(void) +{ + char **env = NULL; +#ifdef _UCRT + char ***penv = __p__environ(); + if (penv) + env = *penv; +#else + _get_environ(&env); +#endif + return env; +} + +static wchar_t **rt_get_wenviron(void) +{ + wchar_t **env = NULL; +#ifdef _UCRT + wchar_t ***penv = __p__wenviron(); + if (penv) + env = *penv; +#else + _get_wenviron(&env); +#endif + return env; +} + +static int rt_run_argstart = -1; + +static int __cdecl rt_get_run_argstart(void) +{ + return rt_run_argstart; +} +#endif + +#ifdef _WIN32 +static void rt_flush_target_io(void) +{ + typedef int (__cdecl *rt_fflush_func_t)(void *); + static rt_fflush_func_t fn; + static int init; + + if (!init) { + HMODULE dll = GetModuleHandleA("msvcrt.dll"); + if (dll) + fn = (rt_fflush_func_t)(void *)GetProcAddress(dll, "fflush"); + init = 1; + } + if (fn) + fn(NULL); +} + +static void rt_cleanup_run_targv(TCCState *s) +{ + void (*cleanup)(void); + + if (!s) + return; + cleanup = (void (*)(void))tcc_get_symbol(s, "__tcc_cleanup_run_targv"); + if (cleanup) + cleanup(); +} +#endif + /* launch the compiled program with the given arguments */ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv) { @@ -209,6 +289,8 @@ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv) #if defined(__APPLE__) extern char ***_NSGetEnviron(void); char **envp = *_NSGetEnviron(); +#elif defined(_WIN32) + char **envp = rt_get_environ(); #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) extern char **environ; char **envp = environ; @@ -221,6 +303,12 @@ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv) return 0; tcc_add_symbol(s1, "__rt_exit", rt_exit); +#ifdef _WIN32 + tcc_add_symbol(s1, "__rt_get_environ", rt_get_environ); + tcc_add_symbol(s1, "__rt_get_wenviron", rt_get_wenviron); + tcc_add_symbol(s1, "__rt_get_run_argstart", rt_get_run_argstart); + rt_run_argstart = s1->run_arg_start; +#endif s1->run_main = "_runmain", top_sym = "main"; if (s1->elf_entryname) s1->run_main = top_sym = s1->elf_entryname; @@ -228,7 +316,6 @@ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv) if (tcc_relocate(s1) < 0) return -1; - prog_main = (void*)get_sym_addr(s1, s1->run_main, 1, 1); if ((addr_t)-1 == (addr_t)prog_main) return -1; @@ -250,9 +337,15 @@ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv) ret = tcc_setjmp(s1, main_jb, tcc_get_symbol(s1, top_sym)); if (0 == ret) { ret = prog_main(argc, argv, envp); - } else if (RT_EXIT_ZERO == ret) { + } else if (RT_EXIT_ZERO == ret) ret = 0; - } + +#ifdef _WIN32 + rt_flush_target_io(); + rt_run_argstart = -1; +#endif + fflush(stdout); + fflush(stderr); if (s1->dflag & 16 && ret) /* tcc -dt -run ... */ fprintf(s1->ppfp, "[returns %d]\n", ret), fflush(s1->ppfp); @@ -617,9 +710,17 @@ static void rt_exit(rt_frame *f, int code) ((void (*)(void))p)(); } #endif +#ifdef _WIN32 + rt_cleanup_run_targv(s); +#endif +#if defined(_WIN64) && defined(__aarch64__) && !defined(CONFIG_TCC_BACKTRACE_ONLY) + rt_restore_context_from_jmpbuf(s->run_jb, code); + return; +#else if (code == 0) code = RT_EXIT_ZERO; ((void(*)(void*,int))s->run_lj)(s->run_jb, code); +#endif } exit(code); } @@ -651,6 +752,25 @@ static int rt_printf(const char *fmt, ...) return r; } +static const char *rt_backtrace_format(const char *fmt, char *skip, int *one) +{ + const char *a, *b; + + skip[0] = 0; + if (fmt[0] == '^' && (b = strchr(a = fmt + 1, fmt[0]))) { + size_t len = b - a; + if (len >= 40) + len = 39; + memcpy(skip, a, len); + skip[len] = 0; + fmt = b + 1; + } + *one = 0; + if (fmt[0] == '\001') + ++fmt, *one = 1; + return fmt; +} + static char *rt_elfsym(rt_context *rc, addr_t wanted_pc, addr_t *func_addr) { ElfW(Sym) *esym; @@ -1089,20 +1209,11 @@ int _tcc_backtrace(rt_frame *f, const char *fmt, va_list ap) addr_t pc; char skip[40], msg[200]; int i, level, ret, n, one; - const char *a, *b; + const char *a; bt_info bi; addr_t (*getinfo)(rt_context*, addr_t, bt_info*); - skip[0] = 0; - /* If fmt is like "^file.c^..." then skip calls from 'file.c' */ - if (fmt[0] == '^' && (b = strchr(a = fmt + 1, fmt[0]))) { - memcpy(skip, a, b - a), skip[b - a] = 0; - fmt = b + 1; - } - one = 0; - /* hack for bcheck.c:dprintf(): one level, no newline */ - if (fmt[0] == '\001') - ++fmt, one = 1; + fmt = rt_backtrace_format(fmt, skip, &one); vsnprintf(msg, sizeof msg, fmt, ap); rt_wait_sem(); @@ -1202,9 +1313,9 @@ static int rt_error(rt_frame *f, const char *fmt, ...) static void rt_getcontext(ucontext_t *uc, rt_frame *rc) { #if defined _WIN64 && defined __aarch64__ - rc->ip = uc->Pc; - rc->fp = uc->Fp; - rc->sp = uc->Sp; + 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; @@ -1371,11 +1482,82 @@ static void set_exception_handler(void) #else /* WIN32 */ +static PVOID rt_exception_handler; + +#if defined(_WIN64) && defined(__aarch64__) && !defined(CONFIG_TCC_BACKTRACE_ONLY) +typedef VOID (__cdecl *rt_restore_context_func_t)(PCONTEXT, struct _EXCEPTION_RECORD *); + +#define RT_ARM64_CONTEXT_ASSERT(name, expr) \ + typedef char rt_arm64_context_assert_##name[(expr) ? 1 : -1] +RT_ARM64_CONTEXT_ASSERT(size, sizeof(CONTEXT) == 0x390); +RT_ARM64_CONTEXT_ASSERT(flags_offset, offsetof(CONTEXT, ContextFlags) == 0x000); +RT_ARM64_CONTEXT_ASSERT(x_offset, offsetof(CONTEXT, X) == 0x008); +RT_ARM64_CONTEXT_ASSERT(fp_offset, offsetof(CONTEXT, Fp) == 0x0f0); +RT_ARM64_CONTEXT_ASSERT(lr_offset, offsetof(CONTEXT, Lr) == 0x0f8); +RT_ARM64_CONTEXT_ASSERT(sp_offset, offsetof(CONTEXT, Sp) == 0x100); +RT_ARM64_CONTEXT_ASSERT(pc_offset, offsetof(CONTEXT, Pc) == 0x108); +RT_ARM64_CONTEXT_ASSERT(v_offset, offsetof(CONTEXT, V) == 0x110); +RT_ARM64_CONTEXT_ASSERT(v_slot_size, sizeof(((CONTEXT *)0)->V[0]) == 16); +RT_ARM64_CONTEXT_ASSERT(fpcr_offset, offsetof(CONTEXT, Fpcr) == 0x310); +RT_ARM64_CONTEXT_ASSERT(fpsr_offset, offsetof(CONTEXT, Fpsr) == 0x314); +RT_ARM64_CONTEXT_ASSERT(bvr_offset, offsetof(CONTEXT, Bvr) == 0x338); +RT_ARM64_CONTEXT_ASSERT(wvr_offset, offsetof(CONTEXT, Wvr) == 0x380); +#undef RT_ARM64_CONTEXT_ASSERT + +static rt_restore_context_func_t rt_get_restore_context_func(void) +{ + static rt_restore_context_func_t fn; + + if (!fn) { + HMODULE dll = GetModuleHandleA("ntdll.dll"); + if (dll) + fn = (rt_restore_context_func_t)(void *)GetProcAddress(dll, "RtlRestoreContext"); + } + return fn; +} + +static void rt_restore_context_from_jmpbuf(void *p_jmp_buf, int code) +{ + int i; + _JUMP_BUFFER *jb = (_JUMP_BUFFER *)p_jmp_buf; + CONTEXT ctx; + rt_restore_context_func_t fn; + + memset(&ctx, 0, sizeof(ctx)); + ctx.ContextFlags = CONTEXT_FULL; + ctx.X[0] = code ? code : RT_EXIT_ZERO; + ctx.X[19] = jb->X19; + ctx.X[20] = jb->X20; + ctx.X[21] = jb->X21; + ctx.X[22] = jb->X22; + ctx.X[23] = jb->X23; + ctx.X[24] = jb->X24; + ctx.X[25] = jb->X25; + ctx.X[26] = jb->X26; + ctx.X[27] = jb->X27; + ctx.X[28] = jb->X28; + ctx.Fp = jb->Fp; + ctx.Lr = jb->Lr; + ctx.Sp = jb->Sp; + ctx.Pc = jb->Lr; + for (i = 0; i < 8; ++i) + memcpy(&ctx.V[8 + i].D[0], &jb->D[i], sizeof(jb->D[i])); + ctx.Fpcr = jb->Fpcr; + ctx.Fpsr = jb->Fpsr; + fn = rt_get_restore_context_func(); + if (fn) + fn(&ctx, NULL); +} +#endif + /* signal handler for fatal errors */ static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info) { rt_frame f; unsigned code; +#if defined(_WIN64) && defined(__aarch64__) && !defined(CONFIG_TCC_BACKTRACE_ONLY) + TCCState *s; +#endif rt_getcontext(ex_info->ContextRecord, &f); switch (code = ex_info->ExceptionRecord->ExceptionCode) { @@ -1397,6 +1579,22 @@ static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info) rt_error(&f, "caught exception %08x", code); break; } +#if defined(_WIN64) && defined(__aarch64__) && !defined(CONFIG_TCC_BACKTRACE_ONLY) + rt_wait_sem(); + s = rt_find_state(&f); + rt_post_sem(); + if (s && s->run_lj) { +#ifdef CONFIG_TCC_BCHECK + if (f.fp) { + void *p = tcc_get_symbol(s, "__bound_exit"); + if (p) + ((void (*)(void))p)(); + } +#endif + rt_cleanup_run_targv(s); + rt_restore_context_from_jmpbuf(s->run_jb, 255); + } +#endif rt_exit(&f, 255); return EXCEPTION_EXECUTE_HANDLER; } @@ -1404,7 +1602,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) { + if (!rt_exception_handler) + rt_exception_handler = AddVectoredExceptionHandler(1, cpu_exception_handler); +#if !defined(_WIN64) || !defined(__aarch64__) || defined(CONFIG_TCC_BACKTRACE_ONLY) SetUnhandledExceptionFilter(cpu_exception_handler); +#endif } #endif diff --git a/tcctok.h b/tcctok.h index 4c0d687b0e..2283757ae8 100644 --- a/tcctok.h +++ b/tcctok.h @@ -356,10 +356,12 @@ DEF(TOK_sigsetjmp, "sigsetjmp") DEF(TOK___sigsetjmp, "__sigsetjmp") DEF(TOK_siglongjmp, "siglongjmp") -# endif + # endif DEF(TOK_setjmp, "setjmp") DEF(TOK__setjmp, "_setjmp") + DEF(TOK___mingw_setjmp, "__mingw_setjmp") DEF(TOK_longjmp, "longjmp") + DEF(TOK___mingw_longjmp, "__mingw_longjmp") #endif @@ -421,10 +423,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 diff --git a/tests/Makefile b/tests/Makefile index 4aa4d7f9c8..dcc7a2ac82 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -52,6 +52,9 @@ ifeq ($(ARCH),arm) # of functions via bit masking comes out as 1. Just disable thumb. test.ref: CFLAGS+=-marm endif +ifeq ($(ARCH),arm64) + TESTS += arm64-ext-asm +endif ifeq ($(ARCH)$(CONFIG_WIN32),i386) # tcctest.c:get_asm_string uses a construct that is checked too strictly # by GCC in 32bit mode when PIC is enabled. @@ -69,12 +72,25 @@ ifneq (,$(filter FreeBSD NetBSD,$(TARGETOS))) TESTS += test1 endif +HOST_WIN_UCRT = no +ifeq ($(TARGETOS),WIN32) +HOST_WIN_UCRT := $(shell printf '#include \n' | $(CC) -dM -E -xc - 2>/dev/null | grep -q '^#define _UCRT' && echo yes || echo no) +endif + +TCCTEST_REF = test.ref +ifeq ($(ARCH)-$(TARGETOS),arm64-WIN32) +TCCTEST_REF = test.ref.win32-arm64 +else ifeq ($(ARCH)-$(TARGETOS)-$(CC_NAME)-$(HOST_WIN_UCRT),x86_64-WIN32-clang-yes) +# CLANG64/UCRT generates a different host reference than the msvcrt-backed TCC runtime. +TCCTEST_REF = test.ref.win32-x86_64-ucrt +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 : @@ -82,6 +98,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) @@ -109,54 +126,54 @@ libtcc_test_mt$(EXESUF): libtcc_test_mt.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 $(TESTREF_FLAGS) + ./tcctest.gcc$(EXESUF) > $@ # auto test -test1 test1b: tcctest.c test.ref +test1 test1b: tcctest.c $(TCCTEST_REF) @echo ------------ $@ ------------ $(TCC) $(RUN_TCC) -w -run $< > test.out1 - @diff -u test.ref test.out1 && echo "$(AUTO_TEST) OK" + @diff -u $(TCCTEST_REF) test.out1 && echo "$(AUTO_TEST) OK" # iterated test2 (compile tcc then compile tcctest.c !) -test2 test2b: tcctest.c test.ref +test2 test2b: tcctest.c $(TCCTEST_REF) @echo ------------ $@ ------------ $(TCC) $(RUN_TCC) $(RUN_TCC) -w -run $< > test.out2 - @diff -u test.ref test.out2 && echo "$(AUTO_TEST)2 OK" + @diff -u $(TCCTEST_REF) test.out2 && echo "$(AUTO_TEST)2 OK" # iterated test3 (compile tcc then compile tcc then compile tcctest.c !) -test3 test3b: tcctest.c test.ref +test3 test3b: tcctest.c $(TCCTEST_REF) @echo ------------ $@ ------------ $(TCC) $(RUN_TCC) $(RUN_TCC) $(RUN_TCC) -w -run $< > test.out3 - @diff -u test.ref test.out3 && echo "$(AUTO_TEST)3 OK" + @diff -u $(TCCTEST_REF) test.out3 && echo "$(AUTO_TEST)3 OK" AUTO_TEST = Auto Test test%b : TCCFLAGS += -b -bt1 test%b : AUTO_TEST = Auto Bound-Test # binary output test -test4: tcctest.c test.ref +test4: tcctest.c $(TCCTEST_REF) @echo ------------ $@ ------------ # object + link output $(TCC) -c -o tcctest3.o $< $(TCC) -o tcctest3 tcctest3.o ./tcctest3 > test3.out - @if diff -u test.ref test3.out ; then echo "Object $(AUTO_TEST) OK"; fi + @if diff -u $(TCCTEST_REF) test3.out ; then echo "Object $(AUTO_TEST) OK"; fi # dynamic output $(TCC) -o tcctest1 $< ./tcctest1 > test1.out - @if diff -u test.ref test1.out ; then echo "Dynamic $(AUTO_TEST) OK"; fi + @if diff -u $(TCCTEST_REF) test1.out ; then echo "Dynamic $(AUTO_TEST) OK"; fi # dynamic output + bound check $(TCC) -b -o tcctest4 $< ./tcctest4 > test4.out - @if diff -u test.ref test4.out ; then echo "BCheck $(AUTO_TEST) OK"; fi + @if diff -u $(TCCTEST_REF) test4.out ; then echo "BCheck $(AUTO_TEST) OK"; fi -test4_static: tcctest.c test.ref +test4_static: tcctest.c $(TCCTEST_REF) @echo ------------ $@ ------------ # static output. $(TCC) -static -o tcctest2 $< ./tcctest2 > test2.out - @if diff -u test.ref test2.out ; then echo "Static $(AUTO_TEST) OK"; fi + @if diff -u $(TCCTEST_REF) test2.out ; then echo "Static $(AUTO_TEST) OK"; fi # use tcc to create libtcc.so/.dll and the tcc(.exe) frontend and run them dlltest: @@ -218,7 +235,7 @@ speedtest: ex2 ex3 time ./ex3 35 time $(TCC) -run $(TOPSRC)/examples/ex3.c 35 -weaktest: tcctest.c test.ref +weaktest: tcctest.c $(TCCTEST_REF) @echo ------------ $@ ------------ $(TCC) -c $< -o weaktest.tcc.o $(CC) -c $< -o weaktest.gcc.o $(CFLAGS) -w -O0 -std=gnu99 -fno-omit-frame-pointer @@ -248,6 +265,10 @@ endif # test assembler with tcc compiled by itself asmtest2: MAYBE_RUN_TCC = $(RUN_TCC) +arm64-ext-asm: $(TOPSRC)/tests/asm/test-asm-arm64-ext.c + @echo ------------ $@ ------------ + $(TCC) -run $(TOPSRC)/tests/asm/test-asm-arm64-ext.c + # Check that code generated by libtcc is binary compatible with # that generated by CC abitest-cc.exe: abitest.c @@ -309,6 +330,7 @@ CROSS-TGTS = \ arm-NetBSD \ arm-wince \ arm64 \ + arm64-win32 \ arm64-osx \ arm64-win32 \ arm64-FreeBSD \ @@ -347,4 +369,3 @@ clean: rm -f ex? tcc_g weaktest.*.txt *.def *.pdb *.obj libtcc_test_mt @$(MAKE) -C tests2 $@ @$(MAKE) -C pp $@ - diff --git a/tests/asm/test-asm-arm64-ext.c b/tests/asm/test-asm-arm64-ext.c new file mode 100644 index 0000000000..3e3097e02a --- /dev/null +++ b/tests/asm/test-asm-arm64-ext.c @@ -0,0 +1,455 @@ +/* + * ARM64 Extended Inline Assembly Tests + * Tests for GCC-style extended inline assembly with operands, constraints, and clobbers + */ + +#include +#include +#include + +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"); + printf("=====================================\n\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("\n=====================================\n"); + printf("All tests PASSED!\n"); + return 0; +} diff --git a/tests/asm/test-basic.c b/tests/asm/test-basic.c new file mode 100644 index 0000000000..a3deebc82c --- /dev/null +++ b/tests/asm/test-basic.c @@ -0,0 +1,9 @@ +#include + +int main(void) +{ + int x = 42; + asm("mov x0, #0"); + printf("x = %d\n", x); + return 0; +} diff --git a/tests/asm/test-mini.c b/tests/asm/test-mini.c new file mode 100644 index 0000000000..7dad446d70 --- /dev/null +++ b/tests/asm/test-mini.c @@ -0,0 +1,13 @@ +#include +#include + +int main(void) +{ + int x = 42; + int y; + asm("ldr %0, [%1]" : "=r"(y) : "r"(&x)); + printf("y = %d\n", y); + assert(y == 42); + printf("PASSED\n"); + return 0; +} diff --git a/tests/asm/test-nop.c b/tests/asm/test-nop.c new file mode 100644 index 0000000000..94034fc3c3 --- /dev/null +++ b/tests/asm/test-nop.c @@ -0,0 +1,8 @@ +#include + +int main(void) +{ + asm("nop"); + printf("Hello\n"); + return 0; +} diff --git a/tests/asm/test-simple.c b/tests/asm/test-simple.c new file mode 100644 index 0000000000..8868c0b18f --- /dev/null +++ b/tests/asm/test-simple.c @@ -0,0 +1,9 @@ +#include + +int main(void) +{ + int x; + asm("mov %0, #42" : "=r"(x)); + printf("x = %d\n", x); + return 0; +} diff --git a/tests/libtcc_test_mt.c b/tests/libtcc_test_mt.c index f9c90bd6d5..23764637be 100644 --- a/tests/libtcc_test_mt.c +++ b/tests/libtcc_test_mt.c @@ -338,7 +338,16 @@ int main(int argc, char **argv) #else #include +#ifdef _WIN32 +# ifdef __i386__ +# define LIBTCC_TEST_WINAPI __attribute__((__stdcall__)) +# else +# define LIBTCC_TEST_WINAPI +# endif +void LIBTCC_TEST_WINAPI Sleep(unsigned int milliseconds); +#else unsigned int sleep(unsigned int seconds); +#endif int fib(n) { @@ -347,7 +356,11 @@ int fib(n) int main(int argc, char **argv) { +#ifdef _WIN32 + Sleep(1000); +#else sleep(1); +#endif printf(" %d", fib(atoi(argv[1]))); return 0; } diff --git a/tests/msvcrt_start.c b/tests/msvcrt_start.c new file mode 100644 index 0000000000..1578886a1e --- /dev/null +++ b/tests/msvcrt_start.c @@ -0,0 +1,142 @@ +/* ------------------------------------------------------------- */ +/* minimal startup with runtime linker to msvcrt */ + +#define REDIR_ALL \ + REDIR(puts)\ + REDIR(printf)\ + REDIR(putchar)\ + REDIR(_vsnprintf)\ + REDIR(strtod)\ + REDIR(memset)\ + REDIR(strcpy)\ + REDIR(strlen)\ + REDIR(malloc)\ + REDIR(free)\ + REDIR(__set_app_type)\ + REDIR(exit) + +#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" + #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 + +#ifdef __i386__ +# define STDCALL __declspec(stdcall) +#else +# define STDCALL +#endif +STDCALL void *LoadLibraryA(const char *); +STDCALL void *GetProcAddress(void*, char*); +STDCALL void ExitProcess(int); +STDCALL int WriteFile(void*, const void*, unsigned, unsigned *, void*); +STDCALL void* GetStdHandle(unsigned); +STDCALL int FlushFileBuffers(void*); +#define STD_INPUT_HANDLE (-10) +#define STD_OUTPUT_HANDLE (-11) +#define STD_ERROR_HANDLE (-12) + +void eput(const char *s) +{ + unsigned n_out; + int n; + + for (n = 0; s[n]; ) + ++n; + WriteFile(GetStdHandle(STD_ERROR_HANDLE), s, n, &n_out, 0); +} + +void __rt_init() +{ + 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("CRT: RUNTIME RELOCATION ERROR: '"); + eput(buf+1); + eput("'\n"); + ExitProcess(-1); + } + ++p; + } while (*s); +} + +void __main() {} +void __chkstk(unsigned n) {} +void _pei386_runtime_relocator(void) {} + +int main(int argc, char **argv); +void exit(int); +void __set_app_type(int apptype); + +#include +#define size_t __SIZE_TYPE__ + +int printf(const char *, ...); +int _vsnprintf(char *, size_t, const char *, va_list); + +int vprintf(const char *format, va_list ap) +{ + char buf[1000]; + _vsnprintf(buf, sizeof buf, format, ap); + return printf("%s", buf); +} + +#ifdef __clang__ +# define _start mainCRTStartup +#endif + +void _start(void) +{ + __rt_init(); + __set_app_type(1); + exit(main(1, 0)); +} diff --git a/tests/pp/Makefile b/tests/pp/Makefile index 7e989c99fb..7a5a6b5c11 100644 --- a/tests/pp/Makefile +++ b/tests/pp/Makefile @@ -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: diff --git a/tests/tcctest.c b/tests/tcctest.c index 4995941715..a442d841ff 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -1662,7 +1662,7 @@ void cast_test() printf("sizeof(-(char)'a') = %d\n", sizeof(-(char)'a')); printf("sizeof(~(char)'a') = %d\n", sizeof(-(char)'a')); -#if CC_NAME != CC_clang /* clang doesn't support non-portable conversions */ +#if __SIZEOF_LONG__ == __SIZEOF_POINTER__ /* avoid LLP64 lossy pointer casts */ /* from pointer to integer types */ printf("%d %d %ld %ld %lld %lld\n", (int)p, (unsigned int)p, @@ -2176,10 +2176,9 @@ 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 */ +#if defined(_WIN32) || CC_NAME == CC_clang +/* Windows CRT NaN rendering is not stable across toolchains. + Also some older clang versions do v=-v as v = -0 - v. */ static char enable_nan_test = 0; #else static char enable_nan_test = 1; @@ -2556,11 +2555,11 @@ 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", + printf(LONG_LONG_FORMAT " " LONG_LONG_FORMAT " " LONG_LONG_FORMAT " " XLONG_LONG_FORMAT "\n", (long long)1, (long long)-2, 1LL, - 0x1234567812345679); + 0x1234567812345679ULL); a = llfunc1(-3); printf(LONG_LONG_FORMAT "\n", a); @@ -3466,7 +3465,7 @@ void other_constraints_test(void) { word ret; int var; -#if CC_NAME != CC_clang +#if !defined(_WIN32) && CC_NAME != CC_clang __asm__ volatile ("mov %P1,%0" : "=r" (ret) : "p" (&var)); printf ("oc1: %d\n", ret == (word)&var); #endif @@ -3888,11 +3887,28 @@ int constant_p_var; int func(void); +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#if defined(__TINYC__) || defined(__GNUC__) || defined(__clang__) +#define HAVE_GNU_BUILTIN_TESTS 1 +#endif + +#if defined(__TINYC__) +#define HAVE_GNU_CLRSB_BUILTINS 1 +#elif defined(__clang__) +#if __has_builtin(__builtin_clrsb) && __has_builtin(__builtin_clrsbl) && __has_builtin(__builtin_clrsbll) +#define HAVE_GNU_CLRSB_BUILTINS 1 +#endif +#elif defined(__GNUC__) && __GNUC__ >= 6 +#define HAVE_GNU_CLRSB_BUILTINS 1 +#endif /* __builtin_clz and __builtin_ctz return random values for 0 */ static void builtin_test_bits(unsigned long long x, int cnt[]) { -#if GCC_MAJOR >= 4 +#ifdef HAVE_GNU_BUILTIN_TESTS cnt[0] += __builtin_ffs(x); cnt[1] += __builtin_ffsl(x); cnt[2] += __builtin_ffsll(x); @@ -3905,8 +3921,7 @@ static void builtin_test_bits(unsigned long long x, int cnt[]) if ((unsigned long) x) cnt[7] += __builtin_ctzl(x); if ((unsigned long long) x) cnt[8] += __builtin_ctzll(x); -#if GCC_MAJOR >= 6 && (CC_NAME != CC_clang || GCC_MAJOR >= 11) -/* Apple clang 10 does not have __builtin_clrsb[l[l]] */ +#ifdef HAVE_GNU_CLRSB_BUILTINS cnt[9] += __builtin_clrsb(x); cnt[10] += __builtin_clrsbl(x); cnt[11] += __builtin_clrsbll(x); @@ -3927,7 +3942,7 @@ void builtin_test(void) short s; int i; long long ll; -#if GCC_MAJOR >= 3 +#ifdef HAVE_GNU_BUILTIN_TESTS COMPAT_TYPE(int, int); COMPAT_TYPE(int, unsigned int); COMPAT_TYPE(int, char); diff --git a/tests/test-win32.bat b/tests/test-win32.bat new file mode 100644 index 0000000000..47bcf049b3 --- /dev/null +++ b/tests/test-win32.bat @@ -0,0 +1,56 @@ +@echo off +setlocal +set ARCH= +set CC=gcc +set TEST= +goto :p1 + +:usage +echo usage: test-win32.bat [ options ... ] test targets... +echo options: +echo -t target target architecture (required) +echo -c compiler reference compiler (gcc or clang) +echo -p path prepend path to PATH +exit /B 1 + +:p0 +shift +:p1 +if "%1"=="-t" set ARCH=%2&&shift&&goto p0 +if "%1"=="-c" set CC=%2&&shift&&goto p0 +if "%1"=="-p" set PATH=%2;%PATH%&&shift&&goto p0 +if not "%1"=="" set TEST=%TEST% %1&&goto p0 +if "%ARCH%"=="" goto :usage +if "%TEST%"=="" set TEST=all -k + +set CFG_MAK=..\config.mak +set CFG_H=..\config.h + +set TFLAGS=msvcrt_start.c -nostdlib -lgcc -lkernel32 -Wl,-entry=_start +set TLIB=libtcc.dll +if not %CC%==clang goto :is_set +set TFLAGS=msvcrt_start.c -nostdlib -lkernel32 +:is_set + +echo>>%CFG_H% #define CC_NAME CC_%CC% +echo>%CFG_MAK% prefix = . +echo>>%CFG_MAK% CC = %CC%.exe +echo>>%CFG_MAK% CC_NAME = %CC% +echo>>%CFG_MAK% GCC_MAJOR = 15 +echo>>%CFG_MAK% CFLAGS = -O0 +echo>>%CFG_MAK% LDFLAGS = +echo>>%CFG_MAK% LIBSUF = .lib +echo>>%CFG_MAK% EXESUF = .exe +echo>>%CFG_MAK% DLLSUF = .dll +echo>>%CFG_MAK% ARCH = %ARCH% +echo>>%CFG_MAK% TARGETOS = WIN32 +echo>>%CFG_MAK% CONFIG_WIN32 = yes +echo>>%CFG_MAK% VERSION = 0.9.28rc +echo>>%CFG_MAK% TOPSRC = $(TOP) +echo>>%CFG_MAK% TESTREF_FLAGS = %TFLAGS% +echo>>%CFG_MAK% SHELL = sh + +set PATH=%CD%\..\win32;%PATH% +set TCC_LOCAL=%CD:\=/%/../tcc.exe +echo on +make TCC_LOCAL=%TCC_LOCAL% LIBTCC=win32/%TLIB% %TEST% diff --git a/tests/test.ref.win32-arm64 b/tests/test.ref.win32-arm64 new file mode 100644 index 0000000000..6f45108e8f --- /dev/null +++ b/tests/test.ref.win32-arm64 @@ -0,0 +1,1005 @@ +---- whitespace_test ---- +whitspace: +N=2 +aaa=3 +min=4 +len1=1 +len1=1 str[0]=10 +len1=3 +__LINE__ defined +"123 + 456" +1 + +---- macro_test ---- +N=1236 +aaa=4 +min=-1 +s1=hello +s2=hello, world +s3="c" +s4=a1 +B3=1 +onetwothree=123 +A defined +A defined +B not defined +A defined +B1 not defined +test true1 +test trueA +test 2 +123 +__func__ = macro_test +vaarg=1 +vaarg1 +vaarg1=2 +vaarg1=1 2 +func='macro_test' +INT64_MIN=-9223372036854775808 +a=2 +a=8 +hi +tralala +hi +qq=42 +qq1=1 +basefromheader tcctest.c +base tcctest.c +filefromheader tcctest.h +file tcctest.c +print a backslash: \ + +---- recursive_macro_test ---- +43 +fn tcctest.c, line 326, num 123 +fn tcctest.c, line 327, num 123 +fn tcctest.c, line 328, num 123 +rm_field = 0 +rm_field = 0 +rm_field = 0 0 + +---- string_test ---- +string: +ab3c +ABC: +c=r +wc=a 0x1234 c +foo1_string='bar +testa' +test +!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ +fib=3524578 +262144 +524288 +1048576 +2097152 +4194304 +8388608 +16777216 +33554432 +67108864 +134217728 +268435456 +536870912 +1073741824 +string_test2 +b +b +b +z +r +q +p +c +hello +world +bla +one +two +aa +one +ef +yz + +---- expr_test ---- +1 +-1 +-31232132 +-7808033 +-13 +2 +5 +13 +1 +16 +22322 +22319 +6964152 +5580 +-5580 +1073736243 +1 +-1 +15 +0 +22326 +22329 +2790 +-2791 +536868121 +357136 +-22322 +-22321 +22321 +13 +10 +3744 +3 +12 +4 +11 +12 +3 +192 +-13 +-12 +12 +1 1 1 0 + +---- scope_test ---- +g1=1 +g2=2 +g3=3 +g4=4 +g5=2 + +---- scope2_test ---- +exloc: 43 +exloc: 44 +exloc: 45 +exloc: 46 +exloc: 47 +exloc: 48 +exloc: 49 +exloc: 50 +exloc: 51 +exloc: 51 + +---- forward_test ---- +forward ok +forward ok + +---- funcptr_test ---- +12345 +12345 +12346 +sizeof1 = 1 +sizeof2 = 1 +sizeof3 = 8 +sizeof4 = 8 +42 +42 +43 +aligned_function (should be zero): 0 + +---- if_test ---- +if1t: 1 4 0 0 +if1t: 1 6 0 0 +if1t: 1 7 0 0 +if1t: 1 8 0 0 +if1t: 1 12 0 0 0 +if1t: 2 3 0 3 +if1t: 2 5 0 3 +if1t: 2 7 0 3 +if1t: 2 8 0 3 +if1t: 3 2 2 0 +if1t: 3 5 2 0 +if1t: 3 6 2 0 +if1t: 3 8 2 0 +if1t: 3 10 2 0 0 +if1t: 3 12 2 0 0 +if1t: 3 13 2 0 0 +if1t: 3 14 2 0 0 +if1t: 4 1 2 3 +if1t: 4 5 2 3 +if1t: 4 6 2 3 +if1t: 4 7 2 3 +if1t: 4 9 2 3 0 +if1t: 4 10 2 3 0 +if1t: 4 13 2 3 0 +if1t: 4 14 2 3 0 +if2t:ok +if2t:ok2 +if2t:ok3 + +---- loop_test ---- +0123456789 +0123456789 +0123456789 +count=1 +count=2 +count = 123 +12456 +12456 +012456789 + +---- switch_test ---- +aa2b4567ccccc1314 +ullsw:1 +ullsw:2 +ullsw:3 +ullsw:4 +ullsw:5 +llsw:1 +llsw:2 +llsw:3 +llsw:4 +llsw:5 +ucsw:1 +ucsw:1 +ucsw:1 +ucsw:1 +ucsw:2 +ucsw:3 +ucsw:3 +ucsw:3 +ucsw:3 +ucsw:3 +ucsw:3 +scsw:1 +scsw:1 +scsw:1 +scsw:1 +scsw:2 +scsw:3 +scsw:3 +scsw:3 +scsw:3 +scsw:3 +scsw:3 + +---- goto_test ---- + +goto: +0123456789 +label1 +label2 +label3 + +---- enum_test ---- +43 0 2 4 5 6 1000 +b1=1 +enum large: 0 +enum unsigned: ok +enum unsigned: ok + +---- typedef_test ---- +a=1234 +mytype2=2 + +---- struct_test ---- +sizes: 20 8 4 4 +offsets: 12 +st1: 1 2 3 +union1: 2 +union2: 2 +st2: 3 2 1 +str_addr=10 +aligntest1 sizeof=10 alignof=1 +aligntest2 sizeof=16 alignof=4 +aligntest3 sizeof=16 alignof=8 +aligntest4 sizeof=0 alignof=8 +aligntest5 sizeof=16 alignof=16 +aligntest6 sizeof=16 alignof=16 +aligntest7 sizeof=4 alignof=4 +aligntest8 sizeof=4096 alignof=4096 +aligntest9 sizeof=12 alignof=4 +aligntest10 sizeof=16 alignof=8 +altest5 sizeof=32 alignof=16 +altest6 sizeof=32 alignof=16 +altest7 sizeof=8 alignof=16 +sizeof(struct empty) = 0 +alignof(struct empty) = 1 +Large: sizeof=48 +Large: offsetof(compound_head)=32 + +---- array_test ---- +sizeof(a) = 16 +sizeof("a") = 2 +sizeof(__func__) = 11 +sizeof tab 12 +sizeof tab2 24 +1 2 3 + 0 1 10 11 20 21 +sizeof(size_t)=8 +sizeof(ptrdiff_t)=8 + +---- expr_ptr_test ---- +diff=10 +inc=1 +dec=0 +inc=1 +dec=0 +add=3 +add=3 +FFFFFFFFFFFFFFFC 0000000000000000 1 +0 1 1 1 0 0 +FFFFFFFFFFFFFFFC FFFFFFFFC0000000 -268435455 +0 1 1 1 0 0 +FFFFFFFFFFFFFFFC 00000000B0000000 738197505 +0 1 1 1 0 0 +FFFFFFFFFFFFFFFC 0000000470000000 469762049 +0 1 1 1 0 0 +42 +-1 + +---- bool_test ---- +!s=1 +a=1 +a=0 1 1 +a=0 0 1 +a=1 0 +a1 +a2 +a4 +b=6 +a=400 +exp=1 +r=1 +type of bool: 2147483647 +0 +type of cond: 2147483647 +aspect=65535 +aspect=65536 +34 -1 +60 31 +bits = 0x1 + +---- optimize_out_test ---- +oo:40 +oo:41 +oo:42 +oo:43 +oow:44 +oos:45 +ool1:46 +ool2:47 +ool3:48 +ool5:49 +ool6:50 + +---- expr2_test ---- +res= 112 2 + +---- constant_expr_test ---- +48 +3 +3 +16 + +---- expr_cmp_test ---- +1 +0 +1 +1 +1 +1 +1 +1 +0 +0 +1 +0 +1 +0 + +---- char_short_test ---- +s8=4 -4 +u8=4 252 +s16=772 -516 +u16=772 65020 +s32=16909060 -66052 +u32=16909060 -66052 +var1=1020308 +var1=1020809 +var1=8090a0b +promote char/short assign -85 120 +promote char/short assign VA -85 120 +promote char/short cast VA -85 120 +promote char/short funcret 137 -85 +promote char/short fumcret VA 52685 -4113 0 1 +promote multicast (char)(unsigned char) -9 -9 +promote multicast (unsigned)(int) 2862188664 +promote multicast (unsigned)(char) 4294967177 + +---- init_test ---- +sinit1=2 +sinit2=3 +sinit3=12 1 2 3 +sinit6=12 +sinit7=12 1 2 3 +sinit8=hellotrala +sinit9=1 2 3 +sinit10=1 2 3 +sinit11=1 2 3 10 11 12 +[0][0] = 1 1 1 +[0][1] = 2 2 2 +[1][0] = 3 3 3 +[1][1] = 4 4 4 +[2][0] = 5 5 5 +[2][1] = 6 6 6 +linit1=2 +linit2=3 +linit6=12 +linit8=11 hellotrala +sinit12=hello world +sinit13=24 test1 test2 test3 +sinit14=abc + 1 2 0 0 0 0 0 0 0 0 + 1 2 0 4 0 0 0 3 0 0 + 97 98 99 0 0 0 0 0 0 0 + 2 3 0 0 0 0 4 0 0 0 +1 0 2 0 +linit17=4 +sinit15=12 +sinit16=1 2 +sinit17=a1 4 a2 1 +0 0 14 14 14 14 2 0 a 0 +cix: 2000 2001 2002 2003 2003 0 0 +cix2: 3003 4006 +sizeof cix20 4, cix21 4, sizeof cix22 4 +arrtype1: 1 2 3 +arrtype2: 4 8 +arrtype3: 4 0 0 +arrtype4: 5 6 7 +arrtype5: 12 12 +arrtype6: 12 +sinit23= 8 42 +sinit24=1 +linit18= 1 1 +bf1: 1 0 +bf2: 1 0 +bf3: 1 0 +bf4: 1 0 +bf5[0]: 0 0 +bf5[1]: 1 0 +bf5[2]: 0 0 +bf6[0]: 0 0 +bf6[1]: 1 0 +bf6[2]: 0 0 +bf7[0]: 0 0 +bf7[1]: 1 0 +bf7[2]: 0 0 +bf8[0]: 0 0 +bf8[1]: 1 0 +bf8[2]: 0 0 +bf9[0]: 0 0 +bf9[1]: 0 0 +bf9[2]: 1 0 +bf10[0]: 0 0 +bf10[1]: 0 0 +bf10[2]: 1 0 + +---- compound_literal_test ---- + 1 2 3 +321 +q1=tralala1 +q2=tralala2 +q3=tralala2 +q4=tralala3 + 1 2 3 +1 2 4 +1 2 5 +1 2 6 + +---- kr_test ---- +func1=7 +func2=7 + +---- struct_assign_test ---- +3 4 3 4.500000 +before call: 3 4 +after call: 7 0 +1 +2 1 + +---- cast_test ---- +-1 -1 255 65535 +-1 -1 255 65535 +-1 -1 255 65535 +-127 +1 +sizeof(c) = 1, sizeof((int)c) = 4 +((unsigned)(short)0x0000f000) = 0xfffff000 +((unsigned)(char)0x0000f0f0) = 0xfffffff0 +1 2 +sizeof(+(char)'a') = 4 +sizeof(-(char)'a') = 4 +sizeof(~(char)'a') = 4 +0000000000000001 000000000000F0F0 0000000000000000 00000000FFFFFFF0 +0x80000000 + +---- bitfield_test ---- +sizeof(st1) = 8 +3 -1 15 -8 121 +121 121 +st1.f1 == -1 +st1.f2 == -1 +4886718345 4026531841 120 +st4.y == 1 +st5 = 1 2 3 4 -3 6 +st6.y == 1 + +---- c99_bool_test ---- +sizeof(_Bool) = 1 +cast: 1 0 1 +b = 1 +b = 1 +sizeof(x ? _Bool : _Bool) = 4 (should be sizeof int) + +---- float_test ---- +sizeof(float) = 4 +sizeof(double) = 8 +sizeof(long double) = 8 +testing 'float' +0 1 1 0 0 1 +1.000000 2.500000 3.500000 -1.500000 2.500000 0.400000 -1.000000 +2.000000 +2.000000 +3.000000 +0 1 +0 1 0 1 1 0 +2.000000 1.500000 3.500000 0.500000 3.000000 1.333333 -2.000000 +3.000000 +3.000000 +4.000000 +0 1 +1 0 0 0 1 1 +1.000000 1.000000 2.000000 0.000000 1.000000 1.000000 -1.000000 +2.000000 +2.000000 +3.000000 +0 1 +ftof: 234.600006 234.600006 234.600006 +ftoi: 234 234 234 234 +itof: -1234.000000 +utof: 2166572288.000000 +lltof: -81985531201716224.000000 +ulltof: 17375807653627822000.000000 +ftof: -2334.600098 -2334.600098 -2334.600098 +ftoi: -2334 2334 -2334 2334 +itof: -1234.000000 +utof: 2166572288.000000 +lltof: -81985531201716224.000000 +ulltof: 17375807653627822000.000000 +float: 42.12346 +double: 42.123455 +long double: 42.123455 +strtof: 1.200000 +Test 1.0 / x != 1.0 / y returns 1 (should be 1). +Test 1.0 / x != 1.0 / -x returns 1 (should be 1). +Test 1.0 / x != 1.0 / +y returns 1 (should be 1). +Test 1.0 / x != 1.0 / -y returns 0 (should be 0). +testing 'double' +0 1 1 0 0 1 +1.000000 2.500000 3.500000 -1.500000 2.500000 0.400000 -1.000000 +2.000000 +2.000000 +3.000000 +0 1 +0 1 0 1 1 0 +2.000000 1.500000 3.500000 0.500000 3.000000 1.333333 -2.000000 +3.000000 +3.000000 +4.000000 +0 1 +1 0 0 0 1 1 +1.000000 1.000000 2.000000 0.000000 1.000000 1.000000 -1.000000 +2.000000 +2.000000 +3.000000 +0 1 +ftof: 234.600006 234.600000 234.600000 +ftoi: 234 234 234 234 +itof: -1234.000000 +utof: 2166572288.000000 +lltof: -81985529205302080.000000 +ulltof: 17375808098308006000.000000 +ftof: -2334.600098 -2334.600000 -2334.600000 +ftoi: -2334 2334 -2334 2334 +itof: -1234.000000 +utof: 2166572288.000000 +lltof: -81985529205302080.000000 +ulltof: 17375808098308006000.000000 +float: 42.12346 +double: 42.123457 +long double: 42.123457 +strtod: 1.200000 +Test 1.0 / x != 1.0 / y returns 1 (should be 1). +Test 1.0 / x != 1.0 / -x returns 1 (should be 1). +Test 1.0 / x != 1.0 / +y returns 1 (should be 1). +Test 1.0 / x != 1.0 / -y returns 0 (should be 0). +testing 'long double' +0 1 1 0 0 1 +1.000000 2.500000 3.500000 -1.500000 2.500000 0.400000 -1.000000 +2.000000 +2.000000 +3.000000 +0 1 +0 1 0 1 1 0 +2.000000 1.500000 3.500000 0.500000 3.000000 1.333333 -2.000000 +3.000000 +3.000000 +4.000000 +0 1 +1 0 0 0 1 1 +1.000000 1.000000 2.000000 0.000000 1.000000 1.000000 -1.000000 +2.000000 +2.000000 +3.000000 +0 1 +ftof: 234.600006 234.600000 234.600000 +ftoi: 234 234 234 234 +itof: -1234.000000 +utof: 2166572288.000000 +lltof: -81985529205302080.000000 +ulltof: 17375808098308006000.000000 +ftof: -2334.600098 -2334.600000 -2334.600000 +ftoi: -2334 2334 -2334 2334 +itof: -1234.000000 +utof: 2166572288.000000 +lltof: -81985529205302080.000000 +ulltof: 17375808098308006000.000000 +float: 42.12346 +double: 42.123457 +long double: 42.123457 +strtold: 1.200000 +Test 1.0 / x != 1.0 / y returns 1 (should be 1). +Test 1.0 / x != 1.0 / -x returns 1 (should be 1). +Test 1.0 / x != 1.0 / +y returns 1 (should be 1). +Test 1.0 / x != 1.0 / -y returns 0 (should be 0). +1.200000 3.400000 -5.600000 +2.120000 0.500000 23000000000.000000 +da=123.000000 +fa=123.000000 +da = -294967296.000000 +db = 4000000000.000000 +nan != nan = 1, inf1 = 1.#INF00, inf2 = 1.#INF00 +da subnormal = 0x0.880000p-1022 +da subnormal = 1.1820704873319507e-308 +la subnormal = 0x0.880000p-1022 +la subnormal = 1.1820704873319507e-308 +da/2 subnormal = 0x0.440000p-1022 +da/2 subnormal = 5.9103524366597537e-309 +la/2 subnormal = 0x0.440000p-1022 +la/2 subnormal = 5.9103524366597537e-309 +fa subnormal = 0x1.100000p-127 +fa subnormal = 6.2448137387434024e-039 +la subnormal = 0x1.100000p-127 +la subnormal = 6.2448137387434024e-039 +fa/2 subnormal = 0x1.100000p-128 +fa/2 subnormal = 3.1224068693717012e-039 +la/2 subnormal = 0x1.100000p-128 +la/2 subnormal = 3.1224068693717012e-039 + +---- longlong_test ---- +sizeof(long long) = 8 +-1 4294967294 +1 -2 1 1234567812345679 +-6 +arith: 1023 977 23000 +arith1: 43 11 +bin: 0 1023 1023 +test: 0 1 0 1 1 0 +utest: 0 1 0 1 1 0 +arith2: 1001 24 +arith2: 1001 24 +arith2: 1001 24 +arith2: 1001 24 +not: 0 0 1 1 +arith: 4915 -4405 1188300 +arith1: 0 255 +bin: 52 4863 4811 +test: 0 1 1 0 0 1 +utest: 0 1 1 0 0 1 +arith2: 256 4661 +arith2: 256 4661 +arith2: 256 4661 +arith2: 256 4661 +not: 0 0 1 1 +arith: -782639107 782639101 2347917312 +arith1: 0 -3 +bin: -782639104 -3 782639101 +test: 0 1 0 1 1 0 +utest: 0 1 0 1 1 0 +arith2: -2 -782639103 +arith2: -2 -782639103 +arith2: -2 -782639103 +arith2: -2 -782639103 +not: 0 0 1 1 +shift: 9 9 9312 +shiftc: 36 36 2328 +shiftc: 0 0 9998683865088 +shift: 576460752303423487 -1 -736 +shiftc: 2305843009213693949 -3 -184 +shiftc: 536870911 -1 -790273982464 +shift: 0 0 -1152921504606846976 +shiftc: 245252176896 245252176896 15696139321344 +shiftc: 57 57 -8444530776296390656 +la=320255972942661 ula=16458594985017606144 +lltof: 320255981256704.000000 320255972942661.000000 320255972942661.000000 +ftoll: 320255981256704 320255972942661 320255972942661 +ulltof: 16458595053737083000.000000 16458594985017606000.000000 16458594985017606000.000000 +ftoull: 16458595053737082880 16458594985017606144 16458594985017606144 +12345677 +3 +arith: 2147483648 2147483648 0 +bin: 0 2147483648 2147483648 +test: 0 1 0 1 1 0 +utest: 0 1 0 1 1 0 +arith2: 2147483649 1 +arith2: 2147483649 1 +arith2: 2147483649 1 +arith2: 2147483649 1 +not: 0 0 1 1 +another long long spill test : 2 +a long long function (arm-)reg-args test : -4 +1 0 1 0 +4886718345 +shift: 9 9 9312 +shiftc: 36 36 2328 +shiftc: 0 0 9998683865088 +long long u=2 +long long u=2862188664 +check_opl_save_regs: 1 + +---- manyarg_test ---- +1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 8.900000 9.000000 +1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 8.900000 9.000000 1234567891234 987654321986 42.000000 43.000000 +1234567891234.000000 1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 8.900000 9.000000 1234567891234 987654321986 42.000000 43.000000 +1 2 3 4 5 6 7 8 1234567891234.000000 +1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 8.900000 9.000000 1234567891234 98765432198642.000000 43.000000 1234567891234.000000 +1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 8.900000 9.000000 1234567891234.000000 1234567891234 987654321986 42.000000 43.000000 1234567891234.000000 + +---- stdarg_test ---- +1 2 3 +1.000000 2 3.000000 +1234567891234 987654321986 3 1234.000000 +1.200000 2.300000 3.400000 +1 1.200000 3 4.500000 6 7.800000 9 0.100000 +1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 +1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 8.900000 9.000000 +1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 8.900000 9.000000 1234567891234 987654321986 42.000000 43.000000 +1234567891234.000000 1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 8.900000 9.000000 1234567891234 987654321986 42.000000 43.000000 +1 2 3 4 5 6 7 8 1234567891234.000000 +1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 8.900000 9.000000 1234567891234 987654321986 42.000000 43.000000 1234567891234.000000 +1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 8.900000 9.000000 1234567891234.000000 1234567891234 987654321986 42.000000 43.000000 1234567891234.000000 +stdarg_for_struct: 1 2 3 42 42 42 42 +stdarg_for_libc: string 1.23 456 +stdarg_void_expr: 17 +stdarg_double_struct: -1 +pts[0] = 1 2 +pts[1] = 3 4 +pts[2] = 5 6 +pts[3] = 7 8 +pts[4] = 9 10 +pts[5] = 11 12 +stdarg_double_struct: 1 +pts[0] = 1 2 +d 1 = -1 +pts[2] = 3 4 +pts[3] = 5 6 +pts[4] = 7 8 +pts[5] = 9 10 +pts[6] = 11 12 +stdarg_double_struct: 2 +pts[0] = 1 2 +pts[1] = 3 4 +d 2 = -1 +pts[3] = 5 6 +pts[4] = 7 8 +pts[5] = 9 10 +pts[6] = 11 12 +stdarg_double_struct: 3 +pts[0] = 1 2 +pts[1] = 3 4 +pts[2] = 5 6 +d 3 = -1 +pts[4] = 7 8 +pts[5] = 9 10 +pts[6] = 11 12 +stdarg_double_struct: 4 +pts[0] = 1 2 +pts[1] = 3 4 +pts[2] = 5 6 +pts[3] = 7 8 +d 4 = -1 +pts[5] = 9 10 +pts[6] = 11 12 +stdarg_double_struct: 5 +pts[0] = 1 2 +pts[1] = 3 4 +pts[2] = 5 6 +pts[3] = 7 8 +pts[4] = 9 10 +d 5 = -1 +pts[6] = 11 12 + +---- relocation_test ---- +*rel1=2 +*rel2=3 +in getmyaddress + +---- old_style_function_test ---- +a=1 b=2 b=3.000000 +cmpfn=0 +cmpfn=0 + +---- alloca_test ---- +alloca: p is 123456789012345 +alloca: This is only a test. + + +---- c99_vla_test ---- +Test C99 VLA 1 (sizeof): PASSED +Test C99 VLA 2 (ptrs subtract): PASSED +Test C99 VLA 3 (ptr add): PASSED +Test C99 VLA 4 (ptr access): PASSED +Test C99 VLA 5 (bounds checking (might be disabled)): PASSED PASSED PASSED PASSED PASSED PASSED PASSED PASSED +Test C99 VLA 6 (pointer) + 1 2 3 4 5 + 6 7 8 9 10 + 11 12 13 14 15 + 16 17 18 19 20 + + 21 22 23 24 25 + 26 27 28 29 30 + 31 32 33 34 35 + 36 37 38 39 40 + + 41 42 43 44 45 + 46 47 48 49 50 + 51 52 53 54 55 + 56 57 58 59 60 + + sizes : 80 20 4 + pdiff : 2 3 + tests : 1 1 1 +123 +123 +123 +123 +8 4 123 + +---- sizeof_test ---- +sizeof(int) = 4 +sizeof(unsigned int) = 4 +sizeof(long) = 4 +sizeof(unsigned long) = 4 +sizeof(short) = 2 +sizeof(unsigned short) = 2 +sizeof(char) = 1 +sizeof(unsigned char) = 1 +sizeof(func) = 1 +sizeof(a++) = 4 +a=1 +sizeof(**ptr) = 4 +sizeof(sizeof(int) = 8 +1 0 +__alignof__(int) = 4 +__alignof__(unsigned int) = 4 +__alignof__(short) = 2 +__alignof__(unsigned short) = 2 +__alignof__(char) = 1 +__alignof__(unsigned char) = 1 +__alignof__(func) = 1 +sizeof(char[1+2*a]) = 5 +sizeof( (struct {int i; int j;}){4,5} ) = 8 +sizeof (struct {short i; short j;}){4,5} = 4 +sizeof(t && 0) = 4 +sizeof(1 && 1) = 4 +sizeof(t || 1) = 4 +sizeof(0 || 0) = 4 +sizeof(0, arr) = 8 +sizeof(0, fn) = 8 + +---- typeof_test ---- +a=1.500000 b=2.500000 c=3.500000 + +---- statement_expr_test ---- +a=110 +stmtexpr: somerandomlongstring anotherlongstring +stmtexpr: 40 41 42 +stmtexpr: 43 44 +stmtexpr: 2 should be 2 + +---- local_label_test ---- +bb2 +bb1 +aa2 +aa3 +aa1 +a=2 + +---- asm_test ---- + +---- builtin_test ---- +__builtin_types_compatible_p(int, int) = 1 +__builtin_types_compatible_p(int, unsigned int) = 0 +__builtin_types_compatible_p(int, char) = 0 +__builtin_types_compatible_p(int, const int) = 1 +__builtin_types_compatible_p(int, volatile int) = 1 +__builtin_types_compatible_p(int *, int *) = 1 +__builtin_types_compatible_p(int *, void *) = 0 +__builtin_types_compatible_p(int *, const int *) = 0 +__builtin_types_compatible_p(char *, unsigned char *) = 0 +__builtin_types_compatible_p(char *, signed char *) = 0 +__builtin_types_compatible_p(char *, char *) = 1 +__builtin_types_compatible_p(char **, void *) = 0 +res1 = 1 +res2 = 1 +res3 = 0 +res4 = 0 +res5 = 0 +res6 = 0 +res7 = 1 +res8 = 0 +res10 = 1 +res11 = 1 +res12 = 1 +res13 = 0 +bce: 2 +bce: 1 +bce: 8 +bce: 2 +0 2523 +1 2523 +2 4075 +3 1420 +4 1420 +5 3028 +6 1490 +7 1490 +8 3010 +9 2444 +10 2444 +11 3056 +12 16149 +13 16149 +14 32126 +15 547 +16 547 +17 546 + +---- weak_test ---- + +---- global_data_test ---- +43 + +---- cmp_comparison_test ---- + +---- math_cmp_test ---- + +---- callsave_test ---- +callsavetest: 8 +0 + +---- builtin_frame_address_test ---- +str: __builtin_frame_address +bfa1: __builtin_frame_address +bfa2: __builtin_frame_address +bfa3: __builtin_frame_address + +---- volatile_test ---- + ok + +---- attrib_test ---- + +---- bounds_check1_test ---- +0a +0000000000000014 + +---- func_arg_test ---- +0 1 2 3 4 5 6 7 0 + diff --git a/tests/test.ref.win32-x86_64-ucrt b/tests/test.ref.win32-x86_64-ucrt new file mode 100644 index 0000000000..c25630d373 --- /dev/null +++ b/tests/test.ref.win32-x86_64-ucrt @@ -0,0 +1,1023 @@ +---- whitespace_test ---- +whitspace: +N=2 +aaa=3 +min=4 +len1=1 +len1=1 str[0]=10 +len1=3 +__LINE__ defined +"123 + 456" +1 + +---- macro_test ---- +N=1236 +aaa=4 +min=-1 +s1=hello +s2=hello, world +s3="c" +s4=a1 +B3=1 +onetwothree=123 +A defined +A defined +B not defined +A defined +B1 not defined +test true1 +test trueA +test 2 +123 +__func__ = macro_test +vaarg=1 +vaarg1 +vaarg1=2 +vaarg1=1 2 +func='macro_test' +INT64_MIN=-9223372036854775808 +a=2 +a=8 +hi +tralala +hi +qq=42 +qq1=1 +basefromheader tcctest.c +base tcctest.c +filefromheader tcctest.h +file tcctest.c +print a backslash: \ + +---- recursive_macro_test ---- +43 +fn tcctest.c, line 326, num 123 +fn tcctest.c, line 327, num 123 +fn tcctest.c, line 328, num 123 +rm_field = 0 +rm_field = 0 +rm_field = 0 0 + +---- string_test ---- +string: +ab3c +ABC: +c=r +wc=a 0x1234 c +foo1_string='bar +testa' +test +!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ +fib=3524578 +262144 +524288 +1048576 +2097152 +4194304 +8388608 +16777216 +33554432 +67108864 +134217728 +268435456 +536870912 +1073741824 +string_test2 +b +b +b +z +r +q +p +c +hello +world +bla +one +two +aa +one +ef +yz + +---- expr_test ---- +1 +-1 +-31232132 +-7808033 +-13 +2 +5 +13 +1 +16 +22322 +22319 +6964152 +5580 +-5580 +1073736243 +1 +-1 +15 +0 +22326 +22329 +2790 +-2791 +536868121 +357136 +-22322 +-22321 +22321 +13 +10 +3744 +3 +12 +4 +11 +12 +3 +192 +-13 +-12 +12 +1 1 1 0 + +---- scope_test ---- +g1=1 +g2=2 +g3=3 +g4=4 +g5=2 + +---- scope2_test ---- +exloc: 43 +exloc: 44 +exloc: 45 +exloc: 46 +exloc: 47 +exloc: 48 +exloc: 49 +exloc: 50 +exloc: 51 +exloc: 51 + +---- forward_test ---- +forward ok +forward ok + +---- funcptr_test ---- +12345 +12345 +12346 +sizeof1 = 1 +sizeof2 = 1 +sizeof3 = 8 +sizeof4 = 8 +42 +42 +43 +aligned_function (should be zero): 0 + +---- if_test ---- +if1t: 1 4 0 0 +if1t: 1 6 0 0 +if1t: 1 7 0 0 +if1t: 1 8 0 0 +if1t: 1 12 0 0 0 +if1t: 2 3 0 3 +if1t: 2 5 0 3 +if1t: 2 7 0 3 +if1t: 2 8 0 3 +if1t: 3 2 2 0 +if1t: 3 5 2 0 +if1t: 3 6 2 0 +if1t: 3 8 2 0 +if1t: 3 10 2 0 0 +if1t: 3 12 2 0 0 +if1t: 3 13 2 0 0 +if1t: 3 14 2 0 0 +if1t: 4 1 2 3 +if1t: 4 5 2 3 +if1t: 4 6 2 3 +if1t: 4 7 2 3 +if1t: 4 9 2 3 0 +if1t: 4 10 2 3 0 +if1t: 4 13 2 3 0 +if1t: 4 14 2 3 0 +if2t:ok +if2t:ok2 +if2t:ok3 + +---- loop_test ---- +0123456789 +0123456789 +0123456789 +count=1 +count=2 +count = 123 +12456 +12456 +012456789 + +---- switch_test ---- +aa2b4567ccccc1314 +ullsw:1 +ullsw:2 +ullsw:3 +ullsw:4 +ullsw:5 +llsw:1 +llsw:2 +llsw:3 +llsw:4 +llsw:5 +ucsw:1 +ucsw:1 +ucsw:1 +ucsw:1 +ucsw:2 +ucsw:3 +ucsw:3 +ucsw:3 +ucsw:3 +ucsw:3 +ucsw:3 +scsw:1 +scsw:1 +scsw:1 +scsw:1 +scsw:2 +scsw:3 +scsw:3 +scsw:3 +scsw:3 +scsw:3 +scsw:3 + +---- goto_test ---- + +goto: +0123456789 +label1 +label2 +label3 + +---- enum_test ---- +43 0 2 4 5 6 1000 +b1=1 +enum large: 0 +enum unsigned: ok +enum unsigned: ok + +---- typedef_test ---- +a=1234 +mytype2=2 + +---- struct_test ---- +sizes: 20 8 4 4 +offsets: 12 +st1: 1 2 3 +union1: 2 +union2: 2 +st2: 3 2 1 +str_addr=10 +aligntest1 sizeof=10 alignof=1 +aligntest2 sizeof=16 alignof=4 +aligntest3 sizeof=16 alignof=8 +aligntest4 sizeof=0 alignof=8 +aligntest5 sizeof=16 alignof=16 +aligntest6 sizeof=16 alignof=16 +aligntest7 sizeof=4 alignof=4 +aligntest8 sizeof=4096 alignof=4096 +aligntest9 sizeof=12 alignof=4 +aligntest10 sizeof=16 alignof=8 +altest5 sizeof=32 alignof=16 +altest6 sizeof=32 alignof=16 +altest7 sizeof=8 alignof=16 +sizeof(struct empty) = 0 +alignof(struct empty) = 1 +Large: sizeof=48 +Large: offsetof(compound_head)=32 + +---- array_test ---- +sizeof(a) = 16 +sizeof("a") = 2 +sizeof(__func__) = 11 +sizeof tab 12 +sizeof tab2 24 +1 2 3 + 0 1 10 11 20 21 +sizeof(size_t)=8 +sizeof(ptrdiff_t)=8 + +---- expr_ptr_test ---- +diff=10 +inc=1 +dec=0 +inc=1 +dec=0 +add=3 +add=3 +FFFFFFFFFFFFFFFC 0000000000000000 1 +0 1 1 1 0 0 +FFFFFFFFFFFFFFFC FFFFFFFFC0000000 -268435455 +0 1 1 1 0 0 +FFFFFFFFFFFFFFFC 00000000B0000000 738197505 +0 1 1 1 0 0 +FFFFFFFFFFFFFFFC 0000000470000000 469762049 +0 1 1 1 0 0 +42 +-1 + +---- bool_test ---- +!s=1 +a=1 +a=0 1 1 +a=0 0 1 +a=1 0 +a1 +a2 +a4 +b=6 +a=400 +exp=1 +r=1 +type of bool: 2147483647 +0 +type of cond: 2147483647 +aspect=65535 +aspect=65536 +34 -1 +60 31 +bits = 0x1 + +---- optimize_out_test ---- +oo:40 +oo:41 +oo:42 +oo:43 +oow:44 +oos:45 +ool1:46 +ool2:47 +ool3:48 +ool5:49 +ool6:50 + +---- expr2_test ---- +res= 112 2 + +---- constant_expr_test ---- +48 +3 +3 +16 + +---- expr_cmp_test ---- +1 +0 +1 +1 +1 +1 +1 +1 +0 +0 +1 +0 +1 +0 + +---- char_short_test ---- +s8=4 -4 +u8=4 252 +s16=772 -516 +u16=772 65020 +s32=16909060 -66052 +u32=16909060 -66052 +var1=1020308 +var1=1020809 +var1=8090a0b +promote char/short assign -85 120 +promote char/short assign VA -85 120 +promote char/short cast VA -85 120 +promote char/short funcret 137 -85 +promote char/short fumcret VA 52685 -4113 0 1 +promote multicast (char)(unsigned char) -9 -9 +promote multicast (unsigned)(int) 2862188664 +promote multicast (unsigned)(char) 4294967177 + +---- init_test ---- +sinit1=2 +sinit2=3 +sinit3=12 1 2 3 +sinit6=12 +sinit7=12 1 2 3 +sinit8=hellotrala +sinit9=1 2 3 +sinit10=1 2 3 +sinit11=1 2 3 10 11 12 +[0][0] = 1 1 1 +[0][1] = 2 2 2 +[1][0] = 3 3 3 +[1][1] = 4 4 4 +[2][0] = 5 5 5 +[2][1] = 6 6 6 +linit1=2 +linit2=3 +linit6=12 +linit8=11 hellotrala +sinit12=hello world +sinit13=24 test1 test2 test3 +sinit14=abc + 1 2 0 0 0 0 0 0 0 0 + 1 2 0 4 0 0 0 3 0 0 + 97 98 99 0 0 0 0 0 0 0 + 2 3 0 0 0 0 4 0 0 0 +1 0 2 0 +linit17=4 +sinit15=12 +sinit16=1 2 +sinit17=a1 4 a2 1 +0 0 14 14 14 14 2 0 a 0 +cix: 2000 2001 2002 2003 2003 0 0 +cix2: 3003 4006 +sizeof cix20 4, cix21 4, sizeof cix22 4 +arrtype1: 1 2 3 +arrtype2: 4 8 +arrtype3: 4 0 0 +arrtype4: 5 6 7 +arrtype5: 12 12 +arrtype6: 12 +sinit23= 8 42 +sinit24=1 +linit18= 1 1 +bf1: 1 0 +bf2: 1 0 +bf3: 1 0 +bf4: 1 0 +bf5[0]: 0 0 +bf5[1]: 1 0 +bf5[2]: 0 0 +bf6[0]: 0 0 +bf6[1]: 1 0 +bf6[2]: 0 0 +bf7[0]: 0 0 +bf7[1]: 1 0 +bf7[2]: 0 0 +bf8[0]: 0 0 +bf8[1]: 1 0 +bf8[2]: 0 0 +bf9[0]: 0 0 +bf9[1]: 0 0 +bf9[2]: 1 0 +bf10[0]: 0 0 +bf10[1]: 0 0 +bf10[2]: 1 0 + +---- compound_literal_test ---- + 1 2 3 +321 +q1=tralala1 +q2=tralala2 +q3=tralala2 +q4=tralala3 + 1 2 3 +1 2 4 +1 2 5 +1 2 6 + +---- kr_test ---- +func1=7 +func2=7 + +---- struct_assign_test ---- +3 4 3 4.500000 +before call: 3 4 +after call: 7 0 +1 +2 1 + +---- cast_test ---- +-1 -1 255 65535 +-1 -1 255 65535 +-1 -1 255 65535 +-127 +1 +sizeof(c) = 1, sizeof((int)c) = 4 +((unsigned)(short)0x0000f000) = 0xfffff000 +((unsigned)(char)0x0000f0f0) = 0xfffffff0 +1 2 +sizeof(+(char)'a') = 4 +sizeof(-(char)'a') = 4 +sizeof(~(char)'a') = 4 +0000000000000001 000000000000F0F0 0000000000000000 00000000FFFFFFF0 +0x80000000 + +---- bitfield_test ---- +sizeof(st1) = 8 +3 -1 15 -8 121 +121 121 +st1.f1 == -1 +st1.f2 == -1 +4886718345 4026531841 120 +st4.y == 1 +st5 = 1 2 3 4 -3 6 +st6.y == 1 + +---- c99_bool_test ---- +sizeof(_Bool) = 1 +cast: 1 0 1 +b = 1 +b = 1 +sizeof(x ? _Bool : _Bool) = 4 (should be sizeof int) + +---- float_test ---- +sizeof(float) = 4 +sizeof(double) = 8 +sizeof(long double) = 8 +testing 'float' +0 1 1 0 0 1 +1.000000 2.500000 3.500000 -1.500000 2.500000 0.400000 -1.000000 +2.000000 +2.000000 +3.000000 +0 1 +0 1 0 1 1 0 +2.000000 1.500000 3.500000 0.500000 3.000000 1.333333 -2.000000 +3.000000 +3.000000 +4.000000 +0 1 +1 0 0 0 1 1 +1.000000 1.000000 2.000000 0.000000 1.000000 1.000000 -1.000000 +2.000000 +2.000000 +3.000000 +0 1 +ftof: 234.600006 234.600006 234.600006 +ftoi: 234 234 234 234 +itof: -1234.000000 +utof: 2166572288.000000 +lltof: -81985531201716224.000000 +ulltof: 17375807653627822000.000000 +ftof: -2334.600098 -2334.600098 -2334.600098 +ftoi: -2334 2334 -2334 2334 +itof: -1234.000000 +utof: 2166572288.000000 +lltof: -81985531201716224.000000 +ulltof: 17375807653627822000.000000 +float: 42.123455 +double: 42.123455 +long double: 42.123455 +strtof: 1.200000 +Test 1.0 / x != 1.0 / y returns 1 (should be 1). +Test 1.0 / x != 1.0 / -x returns 1 (should be 1). +Test 1.0 / x != 1.0 / +y returns 1 (should be 1). +Test 1.0 / x != 1.0 / -y returns 0 (should be 0). +testing 'double' +0 1 1 0 0 1 +1.000000 2.500000 3.500000 -1.500000 2.500000 0.400000 -1.000000 +2.000000 +2.000000 +3.000000 +0 1 +0 1 0 1 1 0 +2.000000 1.500000 3.500000 0.500000 3.000000 1.333333 -2.000000 +3.000000 +3.000000 +4.000000 +0 1 +1 0 0 0 1 1 +1.000000 1.000000 2.000000 0.000000 1.000000 1.000000 -1.000000 +2.000000 +2.000000 +3.000000 +0 1 +ftof: 234.600006 234.600000 234.600000 +ftoi: 234 234 234 234 +itof: -1234.000000 +utof: 2166572288.000000 +lltof: -81985529205302080.000000 +ulltof: 17375808098308006000.000000 +ftof: -2334.600098 -2334.600000 -2334.600000 +ftoi: -2334 2334 -2334 2334 +itof: -1234.000000 +utof: 2166572288.000000 +lltof: -81985529205302080.000000 +ulltof: 17375808098308006000.000000 +float: 42.123455 +double: 42.123457 +long double: 42.123457 +strtod: 1.200000 +Test 1.0 / x != 1.0 / y returns 1 (should be 1). +Test 1.0 / x != 1.0 / -x returns 1 (should be 1). +Test 1.0 / x != 1.0 / +y returns 1 (should be 1). +Test 1.0 / x != 1.0 / -y returns 0 (should be 0). +testing 'long double' +0 1 1 0 0 1 +1.000000 2.500000 3.500000 -1.500000 2.500000 0.400000 -1.000000 +2.000000 +2.000000 +3.000000 +0 1 +0 1 0 1 1 0 +2.000000 1.500000 3.500000 0.500000 3.000000 1.333333 -2.000000 +3.000000 +3.000000 +4.000000 +0 1 +1 0 0 0 1 1 +1.000000 1.000000 2.000000 0.000000 1.000000 1.000000 -1.000000 +2.000000 +2.000000 +3.000000 +0 1 +ftof: 234.600006 234.600000 234.600000 +ftoi: 234 234 234 234 +itof: -1234.000000 +utof: 2166572288.000000 +lltof: -81985529205302080.000000 +ulltof: 17375808098308006000.000000 +ftof: -2334.600098 -2334.600000 -2334.600000 +ftoi: -2334 2334 -2334 2334 +itof: -1234.000000 +utof: 2166572288.000000 +lltof: -81985529205302080.000000 +ulltof: 17375808098308006000.000000 +float: 42.123455 +double: 42.123457 +long double: 42.123457 +strtold: 1.200000 +Test 1.0 / x != 1.0 / y returns 1 (should be 1). +Test 1.0 / x != 1.0 / -x returns 1 (should be 1). +Test 1.0 / x != 1.0 / +y returns 1 (should be 1). +Test 1.0 / x != 1.0 / -y returns 0 (should be 0). +1.200000 3.400000 -5.600000 +2.120000 0.500000 23000000000.000000 +da=123.000000 +fa=123.000000 +da = -294967296.000000 +db = 4000000000.000000 +nan != nan = 1, inf1 = 1.#INF00, inf2 = 1.#INF00 +da subnormal = 0x0.880000p-1022 +da subnormal = 1.1820704873319507e-308 +la subnormal = 0x0.880000p-1022 +la subnormal = 1.1820704873319507e-308 +da/2 subnormal = 0x0.440000p-1022 +da/2 subnormal = 5.9103524366597537e-309 +la/2 subnormal = 0x0.440000p-1022 +la/2 subnormal = 5.9103524366597537e-309 +fa subnormal = 0x1.100000p-127 +fa subnormal = 6.2448137387434024e-039 +la subnormal = 0x1.100000p-127 +la subnormal = 6.2448137387434024e-039 +fa/2 subnormal = 0x1.100000p-128 +fa/2 subnormal = 3.1224068693717012e-039 +la/2 subnormal = 0x1.100000p-128 +la/2 subnormal = 3.1224068693717012e-039 + +---- longlong_test ---- +sizeof(long long) = 8 +-1 4294967294 +1 -2 1 1234567812345679 +-6 +arith: 1023 977 23000 +arith1: 43 11 +bin: 0 1023 1023 +test: 0 1 0 1 1 0 +utest: 0 1 0 1 1 0 +arith2: 1001 24 +arith2: 1001 24 +arith2: 1001 24 +arith2: 1001 24 +not: 0 0 1 1 +arith: 4915 -4405 1188300 +arith1: 0 255 +bin: 52 4863 4811 +test: 0 1 1 0 0 1 +utest: 0 1 1 0 0 1 +arith2: 256 4661 +arith2: 256 4661 +arith2: 256 4661 +arith2: 256 4661 +not: 0 0 1 1 +arith: -782639107 782639101 2347917312 +arith1: 0 -3 +bin: -782639104 -3 782639101 +test: 0 1 0 1 1 0 +utest: 0 1 0 1 1 0 +arith2: -2 -782639103 +arith2: -2 -782639103 +arith2: -2 -782639103 +arith2: -2 -782639103 +not: 0 0 1 1 +shift: 9 9 9312 +shiftc: 36 36 2328 +shiftc: 0 0 9998683865088 +shift: 576460752303423487 -1 -736 +shiftc: 2305843009213693949 -3 -184 +shiftc: 536870911 -1 -790273982464 +shift: 0 0 -1152921504606846976 +shiftc: 245252176896 245252176896 15696139321344 +shiftc: 57 57 -8444530776296390656 +la=320255972942661 ula=16458594985017606144 +lltof: 320255981256704.000000 320255972942661.000000 320255972942661.000000 +ftoll: 320255981256704 320255972942661 320255972942661 +ulltof: 16458595053737083000.000000 16458594985017606000.000000 16458594985017606000.000000 +ftoull: 16458595053737082880 16458594985017606144 16458594985017606144 +12345677 +3 +arith: 2147483648 2147483648 0 +bin: 0 2147483648 2147483648 +test: 0 1 0 1 1 0 +utest: 0 1 0 1 1 0 +arith2: 2147483649 1 +arith2: 2147483649 1 +arith2: 2147483649 1 +arith2: 2147483649 1 +not: 0 0 1 1 +another long long spill test : 2 +a long long function (arm-)reg-args test : -4 +1 0 1 0 +4886718345 +shift: 9 9 9312 +shiftc: 36 36 2328 +shiftc: 0 0 9998683865088 +long long u=2 +long long u=2862188664 +check_opl_save_regs: 1 + +---- manyarg_test ---- +1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 8.900000 9.000000 +1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 8.900000 9.000000 1234567891234 987654321986 42.000000 43.000000 +1234567891234.000000 1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 8.900000 9.000000 1234567891234 987654321986 42.000000 43.000000 +1 2 3 4 5 6 7 8 1234567891234.000000 +1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 8.900000 9.000000 1234567891234 98765432198642.000000 43.000000 1234567891234.000000 +1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 8.900000 9.000000 1234567891234.000000 1234567891234 987654321986 42.000000 43.000000 1234567891234.000000 + +---- stdarg_test ---- +1 2 3 +1.000000 2 3.000000 +1234567891234 987654321986 3 1234.000000 +1.200000 2.300000 3.400000 +1 1.200000 3 4.500000 6 7.800000 9 0.100000 +1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 +1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 8.900000 9.000000 +1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 8.900000 9.000000 1234567891234 987654321986 42.000000 43.000000 +1234567891234.000000 1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 8.900000 9.000000 1234567891234 987654321986 42.000000 43.000000 +1 2 3 4 5 6 7 8 1234567891234.000000 +1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 8.900000 9.000000 1234567891234 987654321986 42.000000 43.000000 1234567891234.000000 +1 2 3 4 5 6 7 8 0.100000 1.200000 2.300000 3.400000 4.500000 5.600000 6.700000 7.800000 8.900000 9.000000 1234567891234.000000 1234567891234 987654321986 42.000000 43.000000 1234567891234.000000 +stdarg_for_struct: 1 2 3 42 42 42 42 +stdarg_for_libc: string 1.23 456 +stdarg_void_expr: 17 +stdarg_double_struct: -1 +pts[0] = 1 2 +pts[1] = 3 4 +pts[2] = 5 6 +pts[3] = 7 8 +pts[4] = 9 10 +pts[5] = 11 12 +stdarg_double_struct: 1 +pts[0] = 1 2 +d 1 = -1 +pts[2] = 3 4 +pts[3] = 5 6 +pts[4] = 7 8 +pts[5] = 9 10 +pts[6] = 11 12 +stdarg_double_struct: 2 +pts[0] = 1 2 +pts[1] = 3 4 +d 2 = -1 +pts[3] = 5 6 +pts[4] = 7 8 +pts[5] = 9 10 +pts[6] = 11 12 +stdarg_double_struct: 3 +pts[0] = 1 2 +pts[1] = 3 4 +pts[2] = 5 6 +d 3 = -1 +pts[4] = 7 8 +pts[5] = 9 10 +pts[6] = 11 12 +stdarg_double_struct: 4 +pts[0] = 1 2 +pts[1] = 3 4 +pts[2] = 5 6 +pts[3] = 7 8 +d 4 = -1 +pts[5] = 9 10 +pts[6] = 11 12 +stdarg_double_struct: 5 +pts[0] = 1 2 +pts[1] = 3 4 +pts[2] = 5 6 +pts[3] = 7 8 +pts[4] = 9 10 +d 5 = -1 +pts[6] = 11 12 + +---- relocation_test ---- +*rel1=2 +*rel2=3 +in getmyaddress + +---- old_style_function_test ---- +a=1 b=2 b=3.000000 +cmpfn=0 +cmpfn=0 + +---- alloca_test ---- +alloca: p is 123456789012345 +alloca: This is only a test. + + +---- c99_vla_test ---- +Test C99 VLA 1 (sizeof): PASSED +Test C99 VLA 2 (ptrs subtract): PASSED +Test C99 VLA 3 (ptr add): PASSED +Test C99 VLA 4 (ptr access): PASSED +Test C99 VLA 5 (bounds checking (might be disabled)): PASSED PASSED PASSED PASSED PASSED PASSED PASSED PASSED +Test C99 VLA 6 (pointer) + 1 2 3 4 5 + 6 7 8 9 10 + 11 12 13 14 15 + 16 17 18 19 20 + + 21 22 23 24 25 + 26 27 28 29 30 + 31 32 33 34 35 + 36 37 38 39 40 + + 41 42 43 44 45 + 46 47 48 49 50 + 51 52 53 54 55 + 56 57 58 59 60 + + sizes : 80 20 4 + pdiff : 2 3 + tests : 1 1 1 +123 +123 +123 +123 +8 4 123 + +---- sizeof_test ---- +sizeof(int) = 4 +sizeof(unsigned int) = 4 +sizeof(long) = 4 +sizeof(unsigned long) = 4 +sizeof(short) = 2 +sizeof(unsigned short) = 2 +sizeof(char) = 1 +sizeof(unsigned char) = 1 +sizeof(func) = 1 +sizeof(a++) = 4 +a=1 +sizeof(**ptr) = 4 +sizeof(sizeof(int) = 8 +1 0 +__alignof__(int) = 4 +__alignof__(unsigned int) = 4 +__alignof__(short) = 2 +__alignof__(unsigned short) = 2 +__alignof__(char) = 1 +__alignof__(unsigned char) = 1 +__alignof__(func) = 1 +sizeof(char[1+2*a]) = 5 +sizeof( (struct {int i; int j;}){4,5} ) = 8 +sizeof (struct {short i; short j;}){4,5} = 4 +sizeof(t && 0) = 4 +sizeof(1 && 1) = 4 +sizeof(t || 1) = 4 +sizeof(0 || 0) = 4 +sizeof(0, arr) = 8 +sizeof(0, fn) = 8 + +---- typeof_test ---- +a=1.500000 b=2.500000 c=3.500000 + +---- statement_expr_test ---- +a=110 +stmtexpr: somerandomlongstring anotherlongstring +stmtexpr: 40 41 42 +stmtexpr: 43 44 +stmtexpr: 2 should be 2 + +---- local_label_test ---- +bb2 +bb1 +aa2 +aa3 +aa1 +a=2 + +---- asm_test ---- +hello wo +hello wo +mul64=0x1df4d840 +inc64=0x0 +mconstraint: 148 51 52 +set=0x1080fd +swab32(0x01020304) = 0x4030201 +asm_local_statics: 42 +asmbool: failed +fancycpy(43)=43 +fancycpy2(44)=44 +regvar=4243 +asmhc: 0x4542 +asm_dot_test 1: 8 +asm_dot_test 2: 123 +asm_dot_test 3: 456 +asm_dot_test 4: 789 +asm_pcrel_test : 0 + +---- builtin_test ---- +__builtin_types_compatible_p(int, int) = 1 +__builtin_types_compatible_p(int, unsigned int) = 0 +__builtin_types_compatible_p(int, char) = 0 +__builtin_types_compatible_p(int, const int) = 1 +__builtin_types_compatible_p(int, volatile int) = 1 +__builtin_types_compatible_p(int *, int *) = 1 +__builtin_types_compatible_p(int *, void *) = 0 +__builtin_types_compatible_p(int *, const int *) = 0 +__builtin_types_compatible_p(char *, unsigned char *) = 0 +__builtin_types_compatible_p(char *, signed char *) = 0 +__builtin_types_compatible_p(char *, char *) = 1 +__builtin_types_compatible_p(char **, void *) = 0 +res1 = 1 +res2 = 1 +res3 = 0 +res4 = 0 +res5 = 0 +res6 = 0 +res7 = 1 +res8 = 0 +res10 = 1 +res11 = 1 +res12 = 1 +res13 = 0 +bce: 2 +bce: 1 +bce: 8 +bce: 2 +0 2523 +1 2523 +2 4075 +3 1420 +4 1420 +5 3028 +6 1490 +7 1490 +8 3010 +9 2444 +10 2444 +11 3056 +12 16149 +13 16149 +14 32126 +15 547 +16 547 +17 546 + +---- weak_test ---- + +---- global_data_test ---- +43 + +---- cmp_comparison_test ---- + +---- math_cmp_test ---- + +---- callsave_test ---- +callsavetest: 8 +0 + +---- builtin_frame_address_test ---- +str: __builtin_frame_address +bfa1: __builtin_frame_address +bfa2: __builtin_frame_address +bfa3: __builtin_frame_address + +---- volatile_test ---- + ok + +---- attrib_test ---- + +---- bounds_check1_test ---- +0a +0000000000000014 + +---- func_arg_test ---- +0 1 2 3 4 5 6 7 0 + diff --git a/tests/tests2/112_backtrace.c b/tests/tests2/112_backtrace.c index 0b596d080f..2b5bd4cd60 100644 --- a/tests/tests2/112_backtrace.c +++ b/tests/tests2/112_backtrace.c @@ -61,7 +61,7 @@ void f2() void f1() { printf("* f1()\n"), fflush(stdout); - tcc_backtrace("Hello from %s!", "f1"); + tcc_backtrace("^12345678901234567890123456789012345678901234567890^Hello from %s!", "f1"); f2(); } int main(int argc, char **argv) diff --git a/tests/tests2/138_arm64_encoding.c b/tests/tests2/138_arm64_encoding.c new file mode 100644 index 0000000000..20aeb39054 --- /dev/null +++ b/tests/tests2/138_arm64_encoding.c @@ -0,0 +1,280 @@ +/* ARM64 instruction encoding verification test. + Exercises shift-by-immediate, load/store addressing modes, + conditional branches, and system register access. */ + +#include +#include + +/* ---- 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; +} diff --git a/tests/tests2/138_arm64_encoding.expect b/tests/tests2/138_arm64_encoding.expect new file mode 100644 index 0000000000..d0f4694ffb --- /dev/null +++ b/tests/tests2/138_arm64_encoding.expect @@ -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 diff --git a/tests/tests2/139_arm64_errors.c b/tests/tests2/139_arm64_errors.c new file mode 100644 index 0000000000..00deace511 --- /dev/null +++ b/tests/tests2/139_arm64_errors.c @@ -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 + +#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 diff --git a/tests/tests2/139_arm64_errors.expect b/tests/tests2/139_arm64_errors.expect new file mode 100644 index 0000000000..53cb39b7de --- /dev/null +++ b/tests/tests2/139_arm64_errors.expect @@ -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' diff --git a/tests/tests2/73_arm64.c b/tests/tests2/73_arm64.c index 855c47671f..16be10473f 100644 --- a/tests/tests2/73_arm64.c +++ b/tests/tests2/73_arm64.c @@ -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* diff --git a/tests/tests2/73_arm64.expect b/tests/tests2/73_arm64.expect index 7bdebd3076..7bc0750011 100644 --- a/tests/tests2/73_arm64.expect +++ b/tests/tests2/73_arm64.expect @@ -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 diff --git a/tests/tests2/Makefile b/tests/tests2/Makefile index 1a137b7f85..14edc1600d 100644 --- a/tests/tests2/Makefile +++ b/tests/tests2/Makefile @@ -55,7 +55,10 @@ ifneq (,$(filter arm% riscv%,$(ARCH))) SKIP += 99_fastcall.test SKIP += 127_asm_goto.test endif -# Skip pthread tests for Windows cross-compilation targets +ifeq (,$(filter arm64 aarch64,$(ARCH))) + SKIP += 138_arm64_encoding.test + SKIP += 139_arm64_errors.test +endif ifneq (,$(findstring win32,$(CROSS_TARGET))) SKIP += 106_versym.test SKIP += 114_bound_signal.test @@ -86,6 +89,7 @@ endif # These tests run several snippets from the same file one by one 60_errors_and_warnings.test : FLAGS += -dt +139_arm64_errors.test : FLAGS += -dt 96_nodata_wanted.test : FLAGS += -dt # Always generate certain .expects (don't put these in the GIT), diff --git a/win32/build-tcc.bat b/win32/build-tcc.bat index 8c1119abea..5d4ab04295 100644 --- a/win32/build-tcc.bat +++ b/win32/build-tcc.bat @@ -32,7 +32,7 @@ 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 use prog (gcc/clang/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 -v "version" set tcc version @@ -65,6 +65,12 @@ exit /B 0 if exist %1 rmdir /Q/S %1 && %LOG% %1 exit /B 0 +:select_clang +if /I not "%CC%"=="clang" exit /B 0 +if "%~1"=="64" if exist C:\msys64\clang64\bin\x86_64-w64-mingw32-clang.exe set CC=C:\msys64\clang64\bin\x86_64-w64-mingw32-clang.exe +if "%~1"=="arm64" if exist C:\msys64\clangarm64\bin\aarch64-w64-mingw32-clang.exe set CC=C:\msys64\clangarm64\bin\aarch64-w64-mingw32-clang.exe +exit /B 0 + :cl @echo off set CMD=cl @@ -119,11 +125,10 @@ goto :p3 :tarm64 set D=%DARM64% set P=%PARM64% -@rem ARM64 with clang: use single-source build (lld-link can't link DLL directly) -set TCC_C=..\tcc.c goto :p3 :p3 +call :select_clang %T% 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 @@ -180,8 +185,7 @@ if exist libtcc.dll .\tcc -impdef libtcc.dll -o libtcc\libtcc.def @if errorlevel 1 goto :the_end :lib -@rem Skip lib building for ARM64 - tcc's ARM64 assembler is not implemented -if %T%==arm64 goto :files_done +@rem ARM64 assembler files and basic inline asm strings are supported here. call :make_lib %T% || goto :the_end @if exist %PX%-tcc.exe call :make_lib %TX% %PX%- || goto :the_end @@ -214,16 +218,28 @@ exit /B %ERRORLEVEL% .\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/runrt.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 -ar lib/%2libtcc1.a libtcc1.o crt1.o crt1w.o wincrt1.o wincrt1w.o dllcrt1.o dllmain.o runrt.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 +@if "%1"=="arm64" ( + .\tcc -B. -m%1 -c lib/runmain-arm64.S -o lib/%2runmain.o +) else ( + .\tcc -B. -m%1 -c ../lib/runmain.c -o lib/%2runmain.o +) +@if "%~2"=="" ( + @rem Keep the repo-root runtime helpers in sync for native -run and tests. + if exist tcc.exe copy>nul /y tcc.exe ..\tcc.exe + if exist libtcc.dll copy>nul /y libtcc.dll ..\libtcc.dll + copy>nul /y lib\libtcc1.a ..\libtcc1.a + for %%f in (bcheck bt-dll bt-exe bt-log runmain) do @copy>nul /y lib\%%f.o ..\%%f.o +) exit /B %ERRORLEVEL% diff --git a/win32/include/_mingw.h b/win32/include/_mingw.h index d10a6b18c3..77a3bce6f1 100644 --- a/win32/include/_mingw.h +++ b/win32/include/_mingw.h @@ -70,12 +70,21 @@ #ifdef _WIN64 #define __stdcall +#if defined(__aarch64__) || defined(_M_ARM64) || defined(_ARM64_) +#ifndef _M_ARM64 +#define _M_ARM64 1 +#endif +#ifndef _ARM64_ +#define _ARM64_ 1 +#endif +#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 diff --git a/win32/include/setjmp.h b/win32/include/setjmp.h index e4f142a3f0..d9b136b548 100644 --- a/win32/include/setjmp.h +++ b/win32/include/setjmp.h @@ -124,37 +124,119 @@ 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); + __declspec(noreturn) __attribute__ ((__nothrow__)) void __cdecl longjmp(jmp_buf _Buf, int _Value); -#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); +#if (defined(_X86_) && !defined(__x86_64)) + int __cdecl __attribute__ ((__nothrow__, __returns_twice__)) _setjmp(jmp_buf _Buf); + int __cdecl __attribute__ ((__nothrow__, __returns_twice__)) _setjmp3(jmp_buf _Buf, int _Count, ...); #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); + #ifndef __aarch64__ + int __cdecl __attribute__ ((__nothrow__, __returns_twice__)) _setjmp(jmp_buf _Buf, void *_Frame); + #endif + int __cdecl __attribute__ ((__nothrow__, __returns_twice__)) _setjmpex(jmp_buf _Buf, void *_Frame); #endif -#else -#ifndef _INC_SETJMPEX -#define setjmp _setjmp + +#if defined(__arm__) || defined(__aarch64__) + int __cdecl __attribute__ ((__nothrow__, __returns_twice__)) __mingw_setjmp(jmp_buf _Buf); + __declspec(noreturn) __attribute__ ((__nothrow__)) void __cdecl __mingw_longjmp(jmp_buf _Buf, int _Value); #endif - int __cdecl __attribute__ ((__nothrow__)) setjmp(jmp_buf _Buf); + +#if defined(__TCC_BCHECK__) + __declspec(noreturn) __attribute__ ((__nothrow__)) void __cdecl __bound_longjmp(jmp_buf _Buf, int _Value); #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); + void * __cdecl __attribute__ ((__nothrow__)) mingw_getsp(void); + __declspec(noreturn) __attribute__ ((__nothrow__)) void __cdecl ms_longjmp(jmp_buf _Buf, int _Value); #ifdef __cplusplus } #endif #pragma pack(pop) + +#ifdef setjmp +#undef setjmp +#endif +#if (defined(_X86_) && !defined(__x86_64)) +#define setjmp(BUF) _setjmp3((BUF), 0) +#elif defined(__arm__) || defined(__aarch64__) +#define setjmp(BUF) __mingw_setjmp((BUF)) +#elif defined(USE_MINGW_SETJMP_TWO_ARGS) + #ifndef _INC_SETJMPEX + #define setjmp(BUF) _setjmp((BUF), mingw_getsp()) + #else + #define setjmp(BUF) _setjmpex((BUF), mingw_getsp()) + #define setjmpex(BUF) _setjmpex((BUF), mingw_getsp()) + #endif +#else + #ifndef _INC_SETJMPEX + #define setjmp _setjmp + #endif +#endif + +#ifdef longjmp +#undef longjmp +#endif +#if defined(__TCC_BCHECK__) && defined(__aarch64__) +#define longjmp __bound_longjmp +#elif defined(__arm__) || defined(__aarch64__) +#define longjmp __mingw_longjmp +#else +#define longjmp longjmp +#endif #endif diff --git a/win32/include/stdlib.h b/win32/include/stdlib.h index 21a0fbd9cf..fb680d95f4 100644 --- a/win32/include/stdlib.h +++ b/win32/include/stdlib.h @@ -224,6 +224,8 @@ extern "C" { extern wchar_t **_imp___wpgmptr; #endif #endif + _CRTIMP errno_t __cdecl _get_environ(char ***_Value); + _CRTIMP errno_t __cdecl _get_wenviron(wchar_t ***_Value); errno_t __cdecl _get_pgmptr(char **_Value); errno_t __cdecl _get_wpgmptr(wchar_t **_Value); #ifndef _fmode diff --git a/win32/include/winapi/winnt.h b/win32/include/winapi/winnt.h index fb90d2161e..01a850a873 100644 --- a/win32/include/winapi/winnt.h +++ b/win32/include/winapi/winnt.h @@ -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 @@ -47,6 +47,9 @@ extern "C" { #endif #endif +#if !defined(I_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(__aarch64__) && !defined(_ARM64_) +#define _ARM64_ +#endif #ifdef _WIN64 #define MAX_NATURAL_ALIGNMENT sizeof(ULONGLONG) @@ -65,7 +68,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 +82,7 @@ extern "C" { #include -#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 +324,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,7 +832,7 @@ typedef DWORD LCID; typedef ULONG_PTR KSPIN_LOCK; typedef KSPIN_LOCK *PKSPIN_LOCK; -#ifdef _AMD64_ +#if defined(_AMD64_) || defined(_ARM64_) #if defined(__x86_64) && !defined(RC_INVOKED) @@ -1336,6 +1339,7 @@ typedef DWORD LCID; #define LEGACY_SAVE_AREA_LENGTH sizeof(XMM_SAVE_AREA32) +#if defined(__x86_64) || defined(_AMD64_) typedef struct DECLSPEC_ALIGN(16) _CONTEXT { DWORD64 P1Home; DWORD64 P2Home; @@ -1407,6 +1411,7 @@ typedef DWORD LCID; DWORD64 LastExceptionToRip; DWORD64 LastExceptionFromRip; } CONTEXT,*PCONTEXT; +#endif /* defined(__x86_64) || defined(_AMD64_) */ #define RUNTIME_FUNCTION_INDIRECT 0x1 @@ -1417,6 +1422,123 @@ typedef DWORD LCID; } RUNTIME_FUNCTION,*PRUNTIME_FUNCTION; typedef PRUNTIME_FUNCTION (*PGET_RUNTIME_FUNCTION_CALLBACK)(DWORD64 ControlPc,PVOID Context); + +#if defined(_ARM64_) || defined(__aarch64__) + +/* ARM64 Context Definition */ +#define CONTEXT_ARM64 0x00400000 + +#ifndef CONTEXT_CONTROL +#define CONTEXT_CONTROL (CONTEXT_ARM64 | 0x00000001L) +#endif +#ifndef CONTEXT_INTEGER +#define CONTEXT_INTEGER (CONTEXT_ARM64 | 0x00000002L) +#endif +#ifndef CONTEXT_FLOATING_POINT +#define CONTEXT_FLOATING_POINT (CONTEXT_ARM64 | 0x00000004L) +#endif +#ifndef CONTEXT_DEBUG +#define CONTEXT_DEBUG (CONTEXT_ARM64 | 0x00000008L) +#endif + +#ifndef CONTEXT_FULL +#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT) +#endif +#ifndef CONTEXT_ALL +#define CONTEXT_ALL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG) +#endif + +#ifndef ARM64_MAX_BREAKPOINTS +#define ARM64_MAX_BREAKPOINTS 8 +#endif +#ifndef ARM64_MAX_WATCHPOINTS +#define ARM64_MAX_WATCHPOINTS 2 +#endif + +#ifndef _ARM64_NT_NEON128_DECLARED +#define _ARM64_NT_NEON128_DECLARED + 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; +#endif + +#ifndef _ARM64_CONTEXT_DECLARED +#define _ARM64_CONTEXT_DECLARED + 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); + C_ASSERT(offsetof(ARM64_NT_CONTEXT, ContextFlags) == 0x000); + C_ASSERT(offsetof(ARM64_NT_CONTEXT, X) == 0x008); + C_ASSERT(offsetof(ARM64_NT_CONTEXT, Fp) == 0x0f0); + C_ASSERT(offsetof(ARM64_NT_CONTEXT, Lr) == 0x0f8); + C_ASSERT(offsetof(ARM64_NT_CONTEXT, Sp) == 0x100); + C_ASSERT(offsetof(ARM64_NT_CONTEXT, Pc) == 0x108); + C_ASSERT(offsetof(ARM64_NT_CONTEXT, V) == 0x110); + C_ASSERT(sizeof(((ARM64_NT_CONTEXT *)0)->V[0]) == 16); + C_ASSERT(offsetof(ARM64_NT_CONTEXT, Fpcr) == 0x310); + C_ASSERT(offsetof(ARM64_NT_CONTEXT, Fpsr) == 0x314); + C_ASSERT(offsetof(ARM64_NT_CONTEXT, Bvr) == 0x338); + C_ASSERT(offsetof(ARM64_NT_CONTEXT, Wvr) == 0x380); +#endif + + typedef ARM64_NT_CONTEXT CONTEXT,*PCONTEXT; + +#endif /* _ARM64_ || __aarch64__ */ 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" @@ -3701,6 +3823,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 +3980,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 diff --git a/win32/lib/chkstk.S b/win32/lib/chkstk.S index 7bacd436bd..43e2163450 100644 --- a/win32/lib/chkstk.S +++ b/win32/lib/chkstk.S @@ -13,14 +13,83 @@ .globl __chkstk __chkstk: - lsl x15, x15, #4 + /* 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 -1: - sub x16, x16, #4096 + 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] - subs x15, x15, #4096 - b.gt 1b - mov sp, x16 + sub x17, x17, 4096 + b L_chkstk_loop +L_chkstk_tail: + sub x16, x16, x17 + ldr xzr, [x16] +L_chkstk_done: + ret + +.globl _(tinyc_getbp) +_(tinyc_getbp): + mov x0, x29 + ret + +.globl _(mingw_getsp) +_(mingw_getsp): + mov x0, sp + ret + +.globl _(__mingw_setjmp) +_(__mingw_setjmp): + /* _JUMP_BUFFER layout matches win32/include/setjmp.h for _ARM64_: + 0x00 Frame, 0x08 Reserved, 0x10-0x68 X19-X30, 0x70 Sp, + 0x78 Fpcr/Fpsr, 0x80-0xB8 D8-D15. */ + str xzr, [x0] /* Frame = 0 */ + stp x19, x20, [x0, 16] + stp x21, x22, [x0, 32] + stp x23, x24, [x0, 48] + stp x25, x26, [x0, 64] + stp x27, x28, [x0, 80] + stp x29, x30, [x0, 96] + mov x2, sp + str x2, [x0, 112] /* Sp */ + mrs x2, FPCR + str w2, [x0, 120] /* Fpcr */ + mrs x2, FPSR + str w2, [x0, 124] /* Fpsr */ + stp d8, d9, [x0, 128] + stp d10, d11, [x0, 144] + stp d12, d13, [x0, 160] + stp d14, d15, [x0, 176] + mov x0, 0 + ret + +.globl _(__mingw_longjmp) +_(__mingw_longjmp): + ldp x19, x20, [x0, 16] + ldp x21, x22, [x0, 32] + ldp x23, x24, [x0, 48] + ldp x25, x26, [x0, 64] + ldp x27, x28, [x0, 80] + ldp x29, x30, [x0, 96] + ldr x2, [x0, 112] /* Sp */ + mov sp, x2 + ldr w2, [x0, 120] /* Fpcr */ + msr FPCR, x2 + ldr w2, [x0, 124] /* Fpsr */ + msr FPSR, x2 + ldp d8, d9, [x0, 128] + ldp d10, d11, [x0, 144] + ldp d12, d13, [x0, 160] + ldp d14, d15, [x0, 176] + mov x0, x1 + cbnz x0, L_longjmp_done + mov x0, 1 +L_longjmp_done: ret /* ---------------------------------------------- */ @@ -85,4 +154,3 @@ _(tinyc_getbp): /* ---------------------------------------------- */ #endif /* ---------------------------------------------- */ - diff --git a/win32/lib/crt1.c b/win32/lib/crt1.c index e1910813f9..70db7291ef 100644 --- a/win32/lib/crt1.c +++ b/win32/lib/crt1.c @@ -5,8 +5,11 @@ #include #include +#include #include #include +#include +#include #define _UNKNOWN_APP 0 #define _CONSOLE_APP 1 @@ -22,18 +25,33 @@ #define _tstart _wstart #define _tmain wmain #define _runtmain _runwmain +#define get_tenviron _get_wenviron #else #define __tgetmainargs __getmainargs #define _tstart _start #define _tmain main #define _runtmain _runmain +#define get_tenviron _get_environ #endif typedef struct { int newmode; } _startupinfo; int __cdecl __tgetmainargs(int *pargc, _TCHAR ***pargv, _TCHAR ***penv, int globb, _startupinfo*); +int __cdecl __getmainargs(int *pargc, char ***pargv, char ***penv, int globb, _startupinfo*); +int __cdecl __wgetmainargs(int *pargc, wchar_t ***pargv, wchar_t ***penv, int globb, _startupinfo*); +int __cdecl get_tenviron(_TCHAR ***penv); +int __cdecl _XcptFilter(unsigned long, struct _EXCEPTION_POINTERS *); void __cdecl __set_app_type(int apptype); unsigned int __cdecl _controlfp(unsigned int new_value, unsigned int mask); extern int _tmain(int argc, _TCHAR * argv[], _TCHAR * env[]); +#ifdef UNICODE +__attribute__((weak)) wchar_t **__cdecl __rt_get_wenviron(void); +#else +__attribute__((weak)) char **__cdecl __rt_get_environ(void); +#endif +__attribute__((weak)) int __cdecl __rt_get_run_argstart(void); + +__attribute__((weak)) void __run_on_exit(int ret); +void __attribute__((noreturn)) __tcc_exit(int code); #include "crtinit.c" @@ -48,6 +66,7 @@ static LONG WINAPI catch_sig(EXCEPTION_POINTERS *ex) void _tstart(void) { int ret; + _TCHAR **env = NULL; _startupinfo start_info = {0}; SetUnhandledExceptionFilter(catch_sig); @@ -60,41 +79,443 @@ void _tstart(void) _controlfp(_PC_53, _MCW_PC); #endif - __tgetmainargs( &__argc, &__targv, &_tenviron, _dowildcard, &start_info); - run_ctors(__argc, __targv, _tenviron); - ret = _tmain(__argc, __targv, _tenviron); - run_dtors(); - exit(ret); + __tgetmainargs(&__argc, &__targv, &env, _dowildcard, &start_info); + run_ctors(__argc, __targv, env); + ret = _tmain(__argc, __targv, env); + __tcc_exit(ret); } // ============================================= // for 'tcc -run ,,,' -__attribute__((weak)) extern int __run_on_exit(); +static void run_stdio_init(void) +{ + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); +} -int _runtmain(int argc, /* as tcc passed in */ char **argv) +static _TCHAR **run_get_tenviron(void) { - int ret; -#ifdef UNICODE + _TCHAR **env = NULL; + _TCHAR **argv = NULL; + int argc; _startupinfo start_info = {0}; - __tgetmainargs(&__argc, &__targv, &_tenviron, _dowildcard, &start_info); - /* may be wrong when tcc has received wildcards (*.c) */ - if (argc < __argc) { - __targv += __argc - argc; - __argc = argc; +#ifdef UNICODE + if (__rt_get_wenviron) + env = __rt_get_wenviron(); + if (!env) + get_tenviron(&env); +#else + if (__rt_get_environ) + env = __rt_get_environ(); + if (!env) + get_tenviron(&env); +#endif + if (!env) + __tgetmainargs(&argc, &argv, &env, 0, &start_info); + return env; +} + +typedef struct run_targv_state { + int saved_argc; + _TCHAR **saved_argv; + _TCHAR **run_argv; + int active; + struct run_targv_state *prev; +} run_targv_state; + +static __declspec(thread) run_targv_state *run_targv_tls; + +static void free_run_targv(_TCHAR **argv) +{ + int i; + + if (!argv) + return; + for (i = 0; argv[i]; ++i) + free(argv[i]); + free(argv); +} + +static _TCHAR **dup_run_targv_from_tchar(int argc, _TCHAR **argv) +{ + int i; + _TCHAR **copy = malloc(sizeof(*copy) * (argc + 1)); + + if (!copy) + return NULL; + for (i = 0; i < argc; ++i) { + size_t len; + + if (!argv[i]) { + copy[i] = NULL; + continue; + } + len = _tcslen(argv[i]) + 1; + copy[i] = malloc(sizeof(*copy[i]) * len); + if (!copy[i]) + goto fail; + memcpy(copy[i], argv[i], sizeof(*copy[i]) * len); + } + copy[argc] = NULL; + return copy; +fail: + copy[i] = NULL; + free_run_targv(copy); + return NULL; +} + +static _TCHAR **dup_run_targv_from_char(int argc, char **argv) +{ + int i; + _TCHAR **copy = malloc(sizeof(*copy) * (argc + 1)); + + if (!copy) + return NULL; + for (i = 0; i < argc; ++i) { +#ifdef UNICODE + size_t len; + + if (!argv[i]) { + copy[i] = NULL; + continue; + } + len = strlen(argv[i]) + 1; + copy[i] = malloc(sizeof(*copy[i]) * len); + if (!copy[i] || (size_t)-1 == mbstowcs(copy[i], argv[i], len)) + goto fail; +#else + size_t len; + + if (!argv[i]) { + copy[i] = NULL; + continue; + } + len = strlen(argv[i]) + 1; + copy[i] = malloc(len); + if (!copy[i]) + goto fail; + memcpy(copy[i], argv[i], len); +#endif + } + copy[argc] = NULL; + return copy; +fail: + copy[i] = NULL; + free_run_targv(copy); + return NULL; +} + +typedef LPWSTR *(WINAPI *run_command_line_to_argv_w_func)(LPCWSTR, int *); + +static wchar_t **run_command_line_to_argv_w(int *pargc) +{ + static run_command_line_to_argv_w_func fn; + static int init; + + if (!init) { + HMODULE dll = GetModuleHandleA("shell32.dll"); + if (!dll) + dll = LoadLibraryA("shell32.dll"); + if (dll) + fn = (run_command_line_to_argv_w_func)(void *)GetProcAddress(dll, "CommandLineToArgvW"); + init = 1; + } + return fn ? fn(GetCommandLineW(), pargc) : NULL; +} + +static wchar_t *dup_run_wstr(const wchar_t *s) +{ + size_t len = wcslen(s) + 1; + wchar_t *copy = malloc(sizeof(*copy) * len); + + if (!copy) + return NULL; + memcpy(copy, s, sizeof(*copy) * len); + return copy; +} + +static wchar_t *dup_run_wstr_from_tchar(const _TCHAR *s) +{ +#ifdef UNICODE + return dup_run_wstr(s); +#else + size_t len; + wchar_t *copy; + + len = mbstowcs(NULL, s, 0); + if ((size_t)-1 == len) + return NULL; + copy = malloc(sizeof(*copy) * (len + 1)); + if (!copy) + return NULL; + if ((size_t)-1 == mbstowcs(copy, s, len + 1)) { + free(copy); + return NULL; + } + return copy; +#endif +} + +static void free_run_wargv(wchar_t **argv) +{ + int i; + + if (!argv) + return; + for (i = 0; argv[i]; ++i) + free(argv[i]); + free(argv); +} + +static int append_run_warg(wchar_t ***pargv, int *pargc, wchar_t *arg) +{ + wchar_t **next = realloc(*pargv, sizeof(**pargv) * (*pargc + 2)); + + if (!next) + return 0; + *pargv = next; + next[*pargc] = arg; + next[*pargc + 1] = NULL; + ++*pargc; + return 1; +} + +static int run_has_wildcard(const wchar_t *s) +{ + for (; *s; ++s) + if (*s == L'*' || *s == L'?') + return 1; + return 0; +} + +static int run_expand_wildcard_arg(const wchar_t *pattern, wchar_t ***pargv, int *pargc) +{ + WIN32_FIND_DATAW data; + HANDLE h = INVALID_HANDLE_VALUE; + const wchar_t *p; + wchar_t **matches = NULL; + int nmatches = 0, i, j, ok = 0; + size_t prefix_len = 0; + + for (p = pattern; *p; ++p) { + if (*p == L'\\' || *p == L'/' || *p == L':') + prefix_len = (size_t)(p - pattern + 1); + } + + h = FindFirstFileW(pattern, &data); + if (h == INVALID_HANDLE_VALUE) + return 0; + do { + wchar_t *full; + size_t name_len; + + if (data.cFileName[0] == L'.' + && (!data.cFileName[1] || (data.cFileName[1] == L'.' && !data.cFileName[2]))) + continue; + name_len = wcslen(data.cFileName); + full = malloc(sizeof(*full) * (prefix_len + name_len + 1)); + if (!full) + goto done; + memcpy(full, pattern, sizeof(*full) * prefix_len); + memcpy(full + prefix_len, data.cFileName, sizeof(*full) * (name_len + 1)); + if (!append_run_warg(&matches, &nmatches, full)) { + free(full); + goto done; + } + } while (FindNextFileW(h, &data)); + for (i = 0; i + 1 < nmatches; ++i) { + for (j = i + 1; j < nmatches; ++j) { + if (wcscmp(matches[j], matches[i]) < 0) { + wchar_t *tmp = matches[i]; + matches[i] = matches[j]; + matches[j] = tmp; + } + } + } + for (i = 0; i < nmatches; ++i) { + wchar_t *copy = dup_run_wstr(matches[i]); + + if (!copy || !append_run_warg(pargv, pargc, copy)) { + free(copy); + goto done; + } + } + ok = nmatches != 0; +done: + if (h != INVALID_HANDLE_VALUE) + FindClose(h); + free_run_wargv(matches); + return ok; +} + +static wchar_t **build_run_wargv_from_targv(int src_argc, _TCHAR **src_argv, int base, int *prun_argc) +{ + wchar_t **run_argv = NULL; + int run_argc = 0, i; + + if (base < 0 || base >= src_argc) + return NULL; + for (i = base; i < src_argc; ++i) { + wchar_t *copy; + + if (!src_argv[i]) + continue; + copy = dup_run_wstr_from_tchar(src_argv[i]); + if (!copy) + goto fail; + if (_dowildcard && i > base && run_has_wildcard(copy)) { + if (run_expand_wildcard_arg(copy, &run_argv, &run_argc)) { + free(copy); + continue; + } + } + if (!append_run_warg(&run_argv, &run_argc, copy)) { + free(copy); + goto fail; + } + } + *prun_argc = run_argc; + return run_argv; +fail: + free_run_wargv(run_argv); + return NULL; +} + +static wchar_t **build_run_wargv(int *prun_argc) +{ + wchar_t **cmd_argv = NULL, **run_argv = NULL; + int cmd_argc, run_argc = 0, base, i; + + if (!__rt_get_run_argstart) + return NULL; + base = __rt_get_run_argstart(); + if (base < 0) + return NULL; + /* The active CRT argv slice is process-wide and already reflects the + current host `tcc -run ...` context, including nested runs. Reuse it + first so nested `-run tcc.c ... -run ...` keeps advancing through the + current slice instead of restarting from the original process command + line. */ + run_argv = build_run_wargv_from_targv(__argc, __targv, base, &run_argc); + if (run_argv) { + *prun_argc = run_argc; + return run_argv; + } + cmd_argv = run_command_line_to_argv_w(&cmd_argc); + if (!cmd_argv || base >= cmd_argc) + goto fail; + for (i = base; i < cmd_argc; ++i) { + wchar_t *copy; + + if (_dowildcard && i > base && run_has_wildcard(cmd_argv[i])) { + if (run_expand_wildcard_arg(cmd_argv[i], &run_argv, &run_argc)) + continue; + } + copy = dup_run_wstr(cmd_argv[i]); + if (!copy || !append_run_warg(&run_argv, &run_argc, copy)) { + free(copy); + goto fail; + } + } + LocalFree(cmd_argv); + *prun_argc = run_argc; + return run_argv; +fail: + if (cmd_argv) + LocalFree(cmd_argv); + free_run_wargv(run_argv); + return NULL; +} + +#ifndef UNICODE +static _TCHAR **dup_run_targv_from_wchar(int argc, wchar_t **argv) +{ + int i; + _TCHAR **copy = malloc(sizeof(*copy) * (argc + 1)); + + if (!copy) + return NULL; + for (i = 0; i < argc; ++i) { + size_t len; + + if (!argv[i]) { + copy[i] = NULL; + continue; + } + len = wcstombs(NULL, argv[i], 0); + if ((size_t)-1 == len) + goto fail; + copy[i] = malloc(len + 1); + if (!copy[i] || (size_t)-1 == wcstombs(copy[i], argv[i], len + 1)) + goto fail; } + copy[argc] = NULL; + return copy; +fail: + copy[i] = NULL; + free_run_targv(copy); + return NULL; +} +#endif + +static void restore_run_targv(run_targv_state *state) +{ + if (!state || !state->active) + return; + __argc = state->saved_argc; + __targv = state->saved_argv; + free_run_targv(state->run_argv); + state->run_argv = NULL; + state->active = 0; + if (run_targv_tls == state) + run_targv_tls = state->prev; +} + +void __tcc_cleanup_run_targv(void) +{ + restore_run_targv(run_targv_tls); +} + +int _runtmain(int argc, /* as tcc passed in */ char **argv) +{ + int ret; + int run_argc = argc; + _TCHAR **env = run_get_tenviron(); + run_targv_state argv_state = { __argc, __targv, NULL, 0, NULL }; + wchar_t **run_wargv = NULL; + + run_wargv = build_run_wargv(&run_argc); + if (run_wargv) { +#ifdef UNICODE + argv_state.run_argv = dup_run_targv_from_tchar(run_argc, run_wargv); #else - __argc = argc; - __targv = argv; + argv_state.run_argv = dup_run_targv_from_wchar(run_argc, run_wargv); #endif + free_run_wargv(run_wargv); + } + if (!argv_state.run_argv) { + argv_state.run_argv = dup_run_targv_from_char(argc, argv); + run_argc = argc; + } + if (!argv_state.run_argv) + return 1; + argv_state.prev = run_targv_tls; + run_targv_tls = &argv_state; + __argc = run_argc; + __targv = argv_state.run_argv; + argv_state.active = 1; #if defined __i386__ || defined __x86_64__ _controlfp(_PC_53, _MCW_PC); #endif - run_ctors(__argc, __targv, _tenviron); - ret = _tmain(__argc, __targv, _tenviron); + run_stdio_init(); + run_ctors(__argc, __targv, env); + ret = _tmain(__argc, __targv, env); run_dtors(); - __run_on_exit(ret); + if (__run_on_exit) + __run_on_exit(ret); + __tcc_cleanup_run_targv(); return ret; } diff --git a/win32/lib/runmain-arm64.S b/win32/lib/runmain-arm64.S new file mode 100644 index 0000000000..5ca0597510 --- /dev/null +++ b/win32/lib/runmain-arm64.S @@ -0,0 +1,24 @@ +#ifdef __leading_underscore +# define _(s) _##s +#else +# define _(s) s +#endif + + .text + .p2align 2 + + .global _(__run_on_exit) +_(__run_on_exit): + b _(__tcc_run_on_exit) + + .global _(on_exit) +_(on_exit): + b _(__tcc_on_exit) + + .global _(atexit) +_(atexit): + b _(__tcc_atexit) + + .global _(exit) +_(exit): + b _(__tcc_exit) diff --git a/win32/lib/runrt.c b/win32/lib/runrt.c new file mode 100644 index 0000000000..46f5d05390 --- /dev/null +++ b/win32/lib/runrt.c @@ -0,0 +1,71 @@ +#include +#include + +#ifdef __leading_underscore +# define _(s) s +#else +# define _(s) _##s +#endif + +extern void (*_(_fini_array_start)[]) (void); +extern void (*_(_fini_array_end)[]) (void); + +typedef struct rt_frame { + void *ip, *fp, *sp; +} rt_frame; + +__attribute__((weak, noreturn)) void __rt_exit(rt_frame *, int); + +static void *rt_exitfunc[32]; +static void *rt_exitarg[32]; +static int __rt_nr_exit; + +static void run_dtors(void) +{ + int i = 0; + + while (&_(_fini_array_end)[i] != _(_fini_array_start)) + (*_(_fini_array_end)[--i])(); +} + +void __tcc_run_on_exit(int ret) +{ + int n = __rt_nr_exit; + + while (n) + --n, ((void (*)(int, void *))rt_exitfunc[n])(ret, rt_exitarg[n]); +} + +int __tcc_on_exit(void *function, void *arg) +{ + int n = __rt_nr_exit; + + if (n < 32) { + rt_exitfunc[n] = function; + rt_exitarg[n] = arg; + __rt_nr_exit = n + 1; + return 0; + } + return 1; +} + +int __tcc_atexit(void (*function)(void)) +{ + return __tcc_on_exit(function, 0); +} + +void __attribute__((noreturn)) __tcc_exit(int code) +{ + rt_frame f; + + run_dtors(); + __tcc_run_on_exit(code); + if (__rt_exit) { + f.fp = 0; + f.ip = __tcc_exit; + f.sp = 0; + __rt_exit(&f, code); + } + fflush(NULL); + _exit(code); +} diff --git a/win32/lib/wincrt1.c b/win32/lib/wincrt1.c index d74a0cf839..1ba4ae2153 100644 --- a/win32/lib/wincrt1.c +++ b/win32/lib/wincrt1.c @@ -4,8 +4,8 @@ #include #include +#include #include - #define __UNKNOWN_APP 0 #define __CONSOLE_APP 1 #define __GUI_APP 2 @@ -16,21 +16,95 @@ void _controlfp(unsigned a, unsigned b); #define __tgetmainargs __wgetmainargs #define _twinstart _wwinstart #define _runtwinmain _runwwinmain +#define get_tenviron _get_wenviron int APIENTRY wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int); #else #define __tgetmainargs __getmainargs #define _twinstart _winstart #define _runtwinmain _runwinmain +#define get_tenviron _get_environ #endif typedef struct { int newmode; } _startupinfo; int __cdecl __tgetmainargs(int *pargc, _TCHAR ***pargv, _TCHAR ***penv, int globb, _startupinfo*); +int __cdecl __getmainargs(int *pargc, char ***pargv, char ***penv, int globb, _startupinfo*); +int __cdecl __wgetmainargs(int *pargc, wchar_t ***pargv, wchar_t ***penv, int globb, _startupinfo*); +int __cdecl get_tenviron(_TCHAR ***penv); +int __cdecl _XcptFilter(unsigned long, struct _EXCEPTION_POINTERS *); +__attribute__((weak)) int __cdecl __rt_get_run_argstart(void); #include "crtinit.c" +typedef struct run_targv_state { + int saved_argc; + _TCHAR **saved_argv; + int active; + struct run_targv_state *prev; +} run_targv_state; + +static __declspec(thread) run_targv_state *run_targv_tls; + +static void restore_run_targv(run_targv_state *state) +{ + if (!state || !state->active) + return; + __argc = state->saved_argc; + __targv = state->saved_argv; + state->active = 0; + if (run_targv_tls == state) + run_targv_tls = state->prev; +} + +void __tcc_cleanup_run_targv(void) +{ + restore_run_targv(run_targv_tls); +} + +static int select_run_arg_start_t(int base, const _TCHAR *arg0, int full_argc, _TCHAR **full_argv) +{ + int i; + + if (base < 0 || base > full_argc) + return -1; + if (!arg0) + return base; + for (i = base; i < full_argc; ++i) { + if (full_argv[i] && 0 == _tcscmp(full_argv[i], arg0)) + return i; + } + return base; +} + +static int find_run_arg_slice(int globb, int *pstart, int *prun_argc) +{ + int literal_argc, full_argc, base, start, ret = 0; + _TCHAR **literal_argv = NULL, **full_argv = NULL; + _startupinfo start_info = {0}; + + if (!__rt_get_run_argstart) + return 0; + base = __rt_get_run_argstart(); + if (base < 0) + return 0; + if (__tgetmainargs(&literal_argc, &literal_argv, NULL, 0, &start_info)) + return 0; + if (base >= literal_argc) + return 0; + if (__tgetmainargs(&full_argc, &full_argv, NULL, globb, &start_info)) + return 0; + start = select_run_arg_start_t(base, literal_argv[base], full_argc, full_argv); + if (start < 0 || start > full_argc) + return 0; + *pstart = start; + *prun_argc = full_argc - start; + ret = 1; + return ret; +} + static int go_winmain(TCHAR *arg1) { STARTUPINFO si; + _TCHAR **env = NULL; _TCHAR *szCmd, *p; int fShow; int retval; @@ -51,7 +125,8 @@ static int go_winmain(TCHAR *arg1) #if defined __i386__ || defined __x86_64__ _controlfp(0x10000, 0x30000); #endif - run_ctors(__argc, __targv, _tenviron); + get_tenviron(&env); + run_ctors(__argc, __targv, env); retval = _tWinMain(GetModuleHandle(NULL), NULL, szCmd, fShow); run_dtors(); return retval; @@ -67,20 +142,49 @@ int _twinstart(void) _startupinfo start_info_con = {0}; SetUnhandledExceptionFilter(catch_sig); __set_app_type(__GUI_APP); - __tgetmainargs(&__argc, &__targv, &_tenviron, 0, &start_info_con); + __tgetmainargs(&__argc, &__targv, NULL, 0, &start_info_con); exit(go_winmain(__argc > 1 ? __targv[1] : NULL)); } int _runtwinmain(int argc, /* as tcc passed in */ char **argv) { + run_targv_state argv_state = { __argc, __targv, 0, NULL }; + int ret; + int run_arg_start = -1; #ifdef UNICODE - _startupinfo start_info = {0}; - __tgetmainargs(&__argc, &__targv, &_tenviron, 0, &start_info); - /* may be wrong when tcc has received wildcards (*.c) */ - if (argc < __argc) - __targv += __argc - argc, __argc = argc; + { + int full_argc; + _TCHAR **full_argv = NULL; + _startupinfo start_info = {0}; + + if (find_run_arg_slice(0, &run_arg_start, &__argc) + && !__tgetmainargs(&full_argc, &full_argv, NULL, 0, &start_info) + && run_arg_start <= full_argc) + __targv = full_argv + run_arg_start; + else { + __tgetmainargs(&__argc, &__targv, NULL, 0, &start_info); + if (argc < __argc) + __targv += __argc - argc, __argc = argc; + } + } #else - __argc = argc, __targv = argv; + { + int full_argc; + _TCHAR **full_argv = NULL; + _startupinfo start_info = {0}; + + if (find_run_arg_slice(0, &run_arg_start, &__argc) + && !__tgetmainargs(&full_argc, &full_argv, NULL, 0, &start_info) + && run_arg_start <= full_argc) + __targv = full_argv + run_arg_start; + else + __argc = argc, __targv = argv; + } #endif - return go_winmain(__argc > 1 ? __targv[1] : NULL); + argv_state.prev = run_targv_tls; + run_targv_tls = &argv_state; + argv_state.active = 1; + ret = go_winmain(__argc > 1 ? __targv[1] : NULL); + __tcc_cleanup_run_targv(); + return ret; } diff --git a/win32/lib/winex.c b/win32/lib/winex.c new file mode 100644 index 0000000000..d84d8d7565 --- /dev/null +++ b/win32/lib/winex.c @@ -0,0 +1,27 @@ +/* ------------------------------------------------------------------------- */ +/* winex.c : extra stuff */ + +#if __aarch64__ +#include +/* 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 diff --git a/win32/test_arm64.c b/win32/test_arm64.c new file mode 100644 index 0000000000..651461f6f5 --- /dev/null +++ b/win32/test_arm64.c @@ -0,0 +1,12 @@ +/* Simple ARM64 Windows test program - no inline asm */ +#include + +int main(int argc, char **argv) { + int x = 42; + int y = x * 2; + + printf("Hello from ARM64 Windows!\n"); + printf("x = %d, y = %d\n", x, y); + + return 0; +} diff --git a/win32/test_arm64_backtrace.c b/win32/test_arm64_backtrace.c new file mode 100644 index 0000000000..de531adf0b --- /dev/null +++ b/win32/test_arm64_backtrace.c @@ -0,0 +1,51 @@ +#include + +static volatile int crash_value; + +__attribute__((noinline)) +static void crash4(double a0, double a1, double a2, double a3, + double a4, double a5, double a6, double a7, + int i0, int i1, int i2, int i3, + int i4, int i5, int i6, int i7) +{ + volatile char frame[8192]; + + frame[0] = (char)(a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7 + + i0 + i1 + i2 + i3 + i4 + i5 + i6 + i7); + crash_value = frame[0]; + *(volatile int *)0 = crash_value; +} + +__attribute__((noinline)) +static void crash3(void) +{ + puts("crash3"); + fflush(stdout); + crash4(1.0, 2.0, 3.0, 4.0, + 5.0, 6.0, 7.0, 8.0, + 9, 10, 11, 12, 13, 14, 15, 16); +} + +__attribute__((noinline)) +static void crash2(void) +{ + puts("crash2"); + fflush(stdout); + crash3(); +} + +__attribute__((noinline)) +static void crash1(void) +{ + puts("crash1"); + fflush(stdout); + crash2(); +} + +int main(void) +{ + puts("main"); + fflush(stdout); + crash1(); + return 0; +} diff --git a/win32/test_arm64_inline_asm.c b/win32/test_arm64_inline_asm.c new file mode 100644 index 0000000000..1e95ac6233 --- /dev/null +++ b/win32/test_arm64_inline_asm.c @@ -0,0 +1,50 @@ +#include + +static void plain_inline_asm(void) +{ + asm("nop"); +} + +static int inline_asm_goto(void) +{ + asm goto("b %l[target]" : : : : target); + return 1; +target: + return 0; +} + +#if defined(TEST_OPERANDS) +int main(void) +{ + int input = 1; + int output = 0; + + asm("add %w0, %w1, #1" : "=r"(output) : "r"(input)); + printf("%d\n", output); + return output != 2; +} +#elif defined(TEST_CLOBBERS) +int main(void) +{ + asm volatile("nop" : : : "x0"); + return 0; +} +#elif defined(TEST_GOTO) +int main(void) +{ + int rc = inline_asm_goto(); + + printf("%s\n", rc ? "asm goto wrong" : "asm goto ok"); + return rc; +} +#else +int main(void) +{ + int rc; + + plain_inline_asm(); + rc = inline_asm_goto(); + printf("%s\n", rc ? "inline asm wrong" : "inline asm ok"); + return rc; +} +#endif diff --git a/win32/test_arm64_libtcc_context.S b/win32/test_arm64_libtcc_context.S new file mode 100644 index 0000000000..ffaf41217e --- /dev/null +++ b/win32/test_arm64_libtcc_context.S @@ -0,0 +1,65 @@ + .text + .p2align 2 + .global arm64_call_with_dregs + +arm64_call_with_dregs: + .seh_proc arm64_call_with_dregs + sub sp, sp, 80 + .seh_stackalloc 80 + str x30, [sp, 0] + .seh_save_reg x30, 0 + str x19, [sp, 8] + .seh_save_reg x19, 8 + str d8, [sp, 16] + .seh_save_freg d8, 16 + str d9, [sp, 24] + .seh_save_freg d9, 24 + str d10, [sp, 32] + .seh_save_freg d10, 32 + str d11, [sp, 40] + .seh_save_freg d11, 40 + str d12, [sp, 48] + .seh_save_freg d12, 48 + str d13, [sp, 56] + .seh_save_freg d13, 56 + str d14, [sp, 64] + .seh_save_freg d14, 64 + str d15, [sp, 72] + .seh_save_freg d15, 72 + .seh_endprologue + mov x19, x1 + + ldr d8, [x0, 0] + ldr d9, [x0, 8] + ldr d10, [x0, 16] + ldr d11, [x0, 24] + ldr d12, [x0, 32] + ldr d13, [x0, 40] + ldr d14, [x0, 48] + ldr d15, [x0, 56] + + mov x0, x3 + blr x2 + + str d8, [x19, 0] + str d9, [x19, 8] + str d10, [x19, 16] + str d11, [x19, 24] + str d12, [x19, 32] + str d13, [x19, 40] + str d14, [x19, 48] + str d15, [x19, 56] + + ldr d8, [sp, 16] + ldr d9, [sp, 24] + ldr d10, [sp, 32] + ldr d11, [sp, 40] + ldr d12, [sp, 48] + ldr d13, [sp, 56] + ldr d14, [sp, 64] + ldr d15, [sp, 72] + ldr x19, [sp, 8] + ldr x30, [sp, 0] + add sp, sp, 80 + ret + .seh_endproc diff --git a/win32/test_arm64_libtcc_context.c b/win32/test_arm64_libtcc_context.c new file mode 100644 index 0000000000..6e69db23fd --- /dev/null +++ b/win32/test_arm64_libtcc_context.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include + +#include "libtcc.h" + +typedef struct libtcc_api { + TCCState *(*tcc_new)(void); + void (*tcc_delete)(TCCState *s); + void (*tcc_set_lib_path)(TCCState *s, const char *path); + void (*tcc_set_error_func)(TCCState *s, void *opaque, TCCErrorFunc *error_func); + int (*tcc_set_output_type)(TCCState *s, int output_type); + int (*tcc_add_include_path)(TCCState *s, const char *pathname); + int (*tcc_add_library_path)(TCCState *s, const char *pathname); + int (*tcc_compile_string)(TCCState *s, const char *buf); + int (*tcc_run)(TCCState *s, int argc, char **argv); +} libtcc_api; + +typedef struct test_context { + libtcc_api api; +} test_context; + +extern int arm64_call_with_dregs(const double *expected, double *actual, + int (*fn)(void *opaque), void *opaque); + +static const char run_program[] = + "void exit(int);\n" + "int main(int argc, char **argv)\n" + "{\n" + " if (argc != 2)\n" + " return 11;\n" + " if (argv[1][0] != 'x' || argv[1][1] != '\\0')\n" + " return 12;\n" + " exit(42);\n" + "}\n"; + +static void handle_error(void *opaque, const char *msg) +{ + fprintf((FILE *)opaque, "%s\n", msg); +} + +static FARPROC load_symbol(HMODULE dll, const char *name) +{ + FARPROC proc = GetProcAddress(dll, name); + if (!proc) { + fprintf(stderr, "missing libtcc symbol: %s\n", name); + exit(1); + } + return proc; +} + +static int run_once(void *opaque) +{ + test_context *ctx = (test_context *)opaque; + TCCState *s; + char *argv[] = { "arm64_libtcc_context", "x" }; + int ret; + + s = ctx->api.tcc_new(); + if (!s) { + fprintf(stderr, "tcc_new failed\n"); + return 1; + } + + ctx->api.tcc_set_error_func(s, stderr, handle_error); + ctx->api.tcc_set_lib_path(s, "."); + ctx->api.tcc_add_include_path(s, ".\\include"); + ctx->api.tcc_add_library_path(s, ".\\lib"); + + ret = ctx->api.tcc_set_output_type(s, TCC_OUTPUT_MEMORY); + if (!ret) + ret = ctx->api.tcc_compile_string(s, run_program); + if (!ret) + ret = ctx->api.tcc_run(s, 2, argv); + + ctx->api.tcc_delete(s); + return ret; +} + +int main(void) +{ + static const double expected[8] = { + 1.25, -2.5, 3.75, -4.875, + 5.5, -6.625, 7.75, -8.875 + }; + double actual[8]; + HMODULE dll; + test_context ctx; + int i; + + memset(&ctx, 0, sizeof(ctx)); + dll = LoadLibraryA("libtcc.dll"); + if (!dll) { + fprintf(stderr, "failed to load libtcc.dll\n"); + return 1; + } + + ctx.api.tcc_new = (void *)load_symbol(dll, "tcc_new"); + ctx.api.tcc_delete = (void *)load_symbol(dll, "tcc_delete"); + ctx.api.tcc_set_lib_path = (void *)load_symbol(dll, "tcc_set_lib_path"); + ctx.api.tcc_set_error_func = (void *)load_symbol(dll, "tcc_set_error_func"); + ctx.api.tcc_set_output_type = (void *)load_symbol(dll, "tcc_set_output_type"); + ctx.api.tcc_add_include_path = (void *)load_symbol(dll, "tcc_add_include_path"); + ctx.api.tcc_add_library_path = (void *)load_symbol(dll, "tcc_add_library_path"); + ctx.api.tcc_compile_string = (void *)load_symbol(dll, "tcc_compile_string"); + ctx.api.tcc_run = (void *)load_symbol(dll, "tcc_run"); + + for (i = 0; i < 32; ++i) { + memset(actual, 0, sizeof(actual)); + if (arm64_call_with_dregs(expected, actual, run_once, &ctx) != 42) { + fprintf(stderr, "tcc_run did not return 42 on iteration %d\n", i); + FreeLibrary(dll); + return 1; + } + if (memcmp(expected, actual, sizeof(expected)) != 0) { + fprintf(stderr, "nonvolatile FP registers were not restored on iteration %d\n", i); + FreeLibrary(dll); + return 1; + } + } + + puts("arm64 libtcc context ok"); + FreeLibrary(dll); + return 0; +} diff --git a/win32/test_pe_field_alignment.c b/win32/test_pe_field_alignment.c new file mode 100644 index 0000000000..8d1be8a8c0 --- /dev/null +++ b/win32/test_pe_field_alignment.c @@ -0,0 +1,16 @@ +#include +#include + +struct pe_field_align_min { + char c; + double d __attribute__((aligned(1))); +}; + +int main(void) +{ + printf("size=%u align=%u off=%u\n", + (unsigned)sizeof(struct pe_field_align_min), + (unsigned)__alignof__(struct pe_field_align_min), + (unsigned)offsetof(struct pe_field_align_min, d)); + return 0; +} diff --git a/win32/test_pe_field_alignment.ref b/win32/test_pe_field_alignment.ref new file mode 100644 index 0000000000..88a88ef27a --- /dev/null +++ b/win32/test_pe_field_alignment.ref @@ -0,0 +1 @@ +size=16 align=8 off=8 diff --git a/win32/test_rstdin.c b/win32/test_rstdin.c new file mode 100644 index 0000000000..5e84c1852e --- /dev/null +++ b/win32/test_rstdin.c @@ -0,0 +1,11 @@ +#include + +int main(void) +{ + char buf[64]; + + if (!fgets(buf, sizeof buf, stdin)) + return 1; + printf("%s", buf); + return 0; +} diff --git a/win32/test_run_arg_cleanup.c b/win32/test_run_arg_cleanup.c new file mode 100644 index 0000000000..60c0871786 --- /dev/null +++ b/win32/test_run_arg_cleanup.c @@ -0,0 +1,131 @@ +#include +#include +#include +#include + +#include "..\tcc.h" + +typedef struct libtcc_api { + TCCState *(*tcc_new)(void); + void (*tcc_delete)(TCCState *s); + int (*tcc_set_options)(TCCState *s, const char *str); + void (*tcc_set_lib_path)(TCCState *s, const char *path); + void (*tcc_set_error_func)(TCCState *s, void *opaque, TCCErrorFunc *error_func); + int (*tcc_set_output_type)(TCCState *s, int output_type); + int (*tcc_add_include_path)(TCCState *s, const char *pathname); + int (*tcc_add_library_path)(TCCState *s, const char *pathname); + int (*tcc_compile_string)(TCCState *s, const char *buf); + int (*tcc_run)(TCCState *s, int argc, char **argv); +} libtcc_api; + +static const char crash_program[] = + "int main(void)\n" + "{\n" + " *(volatile int *)0 = 1;\n" + " return 0;\n" + "}\n"; + +static const char argv_program[] = + "#include \n" + "int main(int argc, char **argv)\n" + "{\n" + " if (argc != 2)\n" + " return 10 + argc;\n" + " if (strcmp(argv[0], \"beta\") || strcmp(argv[1], \"gamma\"))\n" + " return 20;\n" + " return 0;\n" + "}\n"; + +static void handle_error(void *opaque, const char *msg) +{ + fprintf((FILE *)opaque, "%s\n", msg); +} + +static FARPROC load_symbol(HMODULE dll, const char *name) +{ + FARPROC proc = GetProcAddress(dll, name); + + if (!proc) { + fprintf(stderr, "missing libtcc symbol: %s\n", name); + exit(1); + } + return proc; +} + +static int run_program(const libtcc_api *api, const char *source, int run_arg_start) +{ + TCCState *s; + char *argv[] = { "unused0", "unused1", "unused2", NULL }; + int ret; + + s = api->tcc_new(); + if (!s) { + fprintf(stderr, "tcc_new failed\n"); + return 100; + } + + api->tcc_set_error_func(s, stderr, handle_error); + api->tcc_set_options(s, "-bt"); + api->tcc_set_lib_path(s, "."); + api->tcc_add_include_path(s, ".\\include"); + api->tcc_add_include_path(s, ".\\win32\\include"); + api->tcc_add_library_path(s, ".\\win32\\lib"); + s->run_arg_start = run_arg_start; + + ret = api->tcc_set_output_type(s, TCC_OUTPUT_MEMORY); + if (!ret) + ret = api->tcc_compile_string(s, source); + if (!ret) + ret = api->tcc_run(s, 3, argv); + + api->tcc_delete(s); + return ret; +} + +int main(int argc, char **argv) +{ + HMODULE dll; + libtcc_api api; + int ret; + + if (argc < 4) { + fprintf(stderr, "usage: %s alpha beta gamma\n", argv[0]); + return 1; + } + + dll = LoadLibraryA("libtcc.dll"); + if (!dll) { + fprintf(stderr, "failed to load libtcc.dll\n"); + return 1; + } + + memset(&api, 0, sizeof(api)); + api.tcc_new = (void *)load_symbol(dll, "tcc_new"); + api.tcc_delete = (void *)load_symbol(dll, "tcc_delete"); + api.tcc_set_options = (void *)load_symbol(dll, "tcc_set_options"); + api.tcc_set_lib_path = (void *)load_symbol(dll, "tcc_set_lib_path"); + api.tcc_set_error_func = (void *)load_symbol(dll, "tcc_set_error_func"); + api.tcc_set_output_type = (void *)load_symbol(dll, "tcc_set_output_type"); + api.tcc_add_include_path = (void *)load_symbol(dll, "tcc_add_include_path"); + api.tcc_add_library_path = (void *)load_symbol(dll, "tcc_add_library_path"); + api.tcc_compile_string = (void *)load_symbol(dll, "tcc_compile_string"); + api.tcc_run = (void *)load_symbol(dll, "tcc_run"); + + ret = run_program(&api, crash_program, 1); + if (ret != 255) { + fprintf(stderr, "crash program returned %d instead of 255\n", ret); + FreeLibrary(dll); + return 2; + } + + ret = run_program(&api, argv_program, 2); + if (ret != 0) { + fprintf(stderr, "argv cleanup test returned %d\n", ret); + FreeLibrary(dll); + return 3; + } + + puts("run argv cleanup ok"); + FreeLibrary(dll); + return 0; +} diff --git a/win32/test_run_argv.c b/win32/test_run_argv.c new file mode 100644 index 0000000000..f052adf0e8 --- /dev/null +++ b/win32/test_run_argv.c @@ -0,0 +1,13 @@ +#include + +int _dowildcard = 1; + +int main(int argc, char **argv) +{ + int i; + + printf("argc=%d\n", argc); + for (i = 1; i < argc; ++i) + printf("arg%d=<%s>\n", i, argv[i]); + return 0; +} diff --git a/win32/test_run_argv.ref b/win32/test_run_argv.ref new file mode 100644 index 0000000000..6092f2005d --- /dev/null +++ b/win32/test_run_argv.ref @@ -0,0 +1,4 @@ +argc=4 +arg1= +arg2= +arg3= diff --git a/win32/test_run_env.c b/win32/test_run_env.c new file mode 100644 index 0000000000..ae2b7f779d --- /dev/null +++ b/win32/test_run_env.c @@ -0,0 +1,12 @@ +#include + +int main(int argc, char **argv, char **envp) +{ + (void)argc; + (void)argv; + if (!envp || !envp[0]) { + fputs("missing envp\n", stderr); + return 1; + } + return 0; +} diff --git a/win32/test_run_exit.c b/win32/test_run_exit.c new file mode 100644 index 0000000000..e7c58f8454 --- /dev/null +++ b/win32/test_run_exit.c @@ -0,0 +1,6 @@ +#include + +int main(void) +{ + return 27; +} diff --git a/win32/test_run_stdin.c b/win32/test_run_stdin.c new file mode 100644 index 0000000000..69e550fd05 --- /dev/null +++ b/win32/test_run_stdin.c @@ -0,0 +1,11 @@ +#include + +int main(void) +{ + char buf[128]; + + if (!fgets(buf, sizeof(buf), stdin)) + return 1; + fputs(buf, stdout); + return 0; +} diff --git a/win32/test_run_stdin.ref b/win32/test_run_stdin.ref new file mode 100644 index 0000000000..364885a243 --- /dev/null +++ b/win32/test_run_stdin.ref @@ -0,0 +1 @@ +temp exe stdin diff --git a/win32/test_run_wargv.c b/win32/test_run_wargv.c new file mode 100644 index 0000000000..422e50d6ee --- /dev/null +++ b/win32/test_run_wargv.c @@ -0,0 +1,14 @@ +#include +#include + +int _dowildcard = 1; + +int wmain(int argc, wchar_t **argv) +{ + int i; + + printf("argc=%d\n", argc); + for (i = 1; i < argc; ++i) + printf("arg%d=<%ls>\n", i, argv[i]); + return 0; +} diff --git a/win32/test_run_wargv.ref b/win32/test_run_wargv.ref new file mode 100644 index 0000000000..6092f2005d --- /dev/null +++ b/win32/test_run_wargv.ref @@ -0,0 +1,4 @@ +argc=4 +arg1= +arg2= +arg3= diff --git a/win32/test_run_wenv.c b/win32/test_run_wenv.c new file mode 100644 index 0000000000..cf1a2cb3f6 --- /dev/null +++ b/win32/test_run_wenv.c @@ -0,0 +1,13 @@ +#include +#include + +int wmain(int argc, wchar_t **argv, wchar_t **envp) +{ + (void)argc; + (void)argv; + if (!envp || !envp[0]) { + fputws(L"missing envp\n", stderr); + return 1; + } + return 0; +}