kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 04a7d100199049a9550ce78eaee8a5835c571c64
parent f5937502ab2ef9f881c5611c98f62ce0288b7202
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Mon, 18 May 2026 21:12:24 -0700

Build runtime with cfree

Diffstat:
Minclude/cfree/cg.h | 2++
Mlang/c/parse/parse.c | 8+++++++-
Mlang/c/parse/parse_expr.c | 44++++++++++++++++++++++++++++++++++++++++++++
Mrt/Makefile | 38+++++++++++++++++++++-----------------
Mrt/lib/atomic/atomic_common.inc | 22+++++++++-------------
Mrt/lib/atomic/atomic_freestanding.c | 6++----
Mrt/lib/fp_tf/fp_tf.c | 4++++
Msrc/api/cg.c | 15+++++++++++++++
Msrc/arch/rv64/arch.c | 3++-
Asrc/arch/rv64/asm.c | 167+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/arch/rv64/asm.h | 8++++++++
Msrc/arch/x64/arch.c | 15+++++++++++++--
Asrc/arch/x64/asm.c | 253+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/arch/x64/asm.h | 8++++++++
Msrc/obj/macho.h | 4++++
Asrc/obj/macho_reloc_x86_64.c | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
16 files changed, 643 insertions(+), 38 deletions(-)

diff --git a/include/cfree/cg.h b/include/cfree/cg.h @@ -888,6 +888,8 @@ typedef struct CfreeCgInlineAsm { * are pre-interned strings. clobber_abi_sets names target-defined ABI register * sets such as all caller-saved registers. */ void cfree_cg_inline_asm(CfreeCg*, CfreeCgInlineAsm asm_block); +void cfree_cg_file_scope_asm(CfreeCg*, const char* asm_source, + size_t asm_source_len); /* ============================================================ * Data Definitions diff --git a/lang/c/parse/parse.c b/lang/c/parse/parse.c @@ -1141,6 +1141,8 @@ static void parse_external_decl(Parser* p) { } static void parse_file_scope_asm(Parser* p) { + u8* bytes; + size_t nbytes; advance(p); /* asm / __asm__ */ for (;;) { if (is_kw(p, &p->cur, KW_VOLATILE)) { @@ -1157,10 +1159,14 @@ static void parse_file_scope_asm(Parser* p) { if (p->cur.kind != TOK_STR) { perr(p, "expected string literal in file-scope asm"); } + bytes = decode_string_literal(p, &p->cur, &nbytes); advance(p); expect_punct(p, ')', "')' after file-scope asm"); expect_punct(p, ';', "';' after file-scope asm"); - perr(p, "file-scope asm is disabled"); + if (nbytes > 0) --nbytes; /* drop decode_string_literal's trailing NUL */ + if (pcg_emit_enabled(p)) { + cfree_cg_file_scope_asm(p->cg, (const char*)bytes, nbytes); + } } static void parse_translation_unit(Parser* p) { diff --git a/lang/c/parse/parse_expr.c b/lang/c/parse/parse_expr.c @@ -458,6 +458,7 @@ static const Type* offsetof_designator(Parser* p, const Type* base, u32* off); static u32 cint_bits(Parser* p, const Type* ty) { u32 sz = ty ? c_abi_sizeof(p->abi, ty) : 8u; + if (ty && (ty->kind == TY_INT128 || ty->kind == TY_UINT128)) return 128; if (sz >= 8) return 64; return sz * 8u; } @@ -2390,6 +2391,29 @@ static void coerce_fp_cmp_operands(Parser* p, const Type* common) { cg_swap(p->cg); } +static void coerce_arith_operands(Parser* p, const Type* common) { + if (!common) return; + if (cg_top_type(p->cg) != common) cg_convert(p->cg, common); + cg_swap(p->cg); + if (cg_top_type(p->cg) != common) cg_convert(p->cg, common); + cg_swap(p->cg); +} + +static CmpOp unsigned_rel_cmp(CmpOp cop) { + switch (cop) { + case CMP_LT_S: + return CMP_LT_U; + case CMP_LE_S: + return CMP_LE_U; + case CMP_GT_S: + return CMP_GT_U; + case CMP_GE_S: + return CMP_GE_U; + default: + return cop; + } +} + static void parse_mul(Parser* p) { parse_unary(p); for (;;) { @@ -2420,6 +2444,8 @@ static void parse_mul(Parser* p) { if (common) { emit_fp_binop(p, bop, common); } else { + const Type* icommon = cint_common_type(p, lt, rt); + coerce_arith_operands(p, icommon); cg_binop(p->cg, bop); } } @@ -2487,6 +2513,8 @@ static void emit_add_or_sub(Parser* p, BinOp bop) { emit_fp_binop(p, bop, common); return; } + common = cint_common_type(p, lt, rt); + coerce_arith_operands(p, common); cg_binop(p->cg, bop); } @@ -2573,6 +2601,10 @@ static void parse_rel(Parser* p) { case CMP_GE_S: cop = CMP_GE_F; break; default: break; } + } else if (type_is_arith(lt) && type_is_arith(rt)) { + common = cint_common_type(p, lt, rt); + coerce_arith_operands(p, common); + if (!cint_signed(p, common)) cop = unsigned_rel_cmp(cop); } } cg_cmp(p->cg, cop); @@ -2613,6 +2645,10 @@ static void parse_eq(Parser* p) { perr(p, "equality operator requires scalar operands"); } if (common) coerce_fp_cmp_operands(p, common); + else if (type_is_arith(lt) && type_is_arith(rt)) { + common = cint_common_type(p, lt, rt); + coerce_arith_operands(p, common); + } } cg_cmp(p->cg, cop); } @@ -2628,6 +2664,8 @@ static void parse_band(Parser* p) { if (!type_is_int(cg_top2_type(p->cg)) || !type_is_int(cg_top_type(p->cg))) { perr(p, "bitwise operator requires integer operands"); } + coerce_arith_operands(p, cint_common_type(p, cg_top2_type(p->cg), + cg_top_type(p->cg))); cg_binop(p->cg, BO_AND); } } @@ -2642,6 +2680,8 @@ static void parse_bxor(Parser* p) { if (!type_is_int(cg_top2_type(p->cg)) || !type_is_int(cg_top_type(p->cg))) { perr(p, "bitwise operator requires integer operands"); } + coerce_arith_operands(p, cint_common_type(p, cg_top2_type(p->cg), + cg_top_type(p->cg))); cg_binop(p->cg, BO_XOR); } } @@ -2656,6 +2696,8 @@ static void parse_bor(Parser* p) { if (!type_is_int(cg_top2_type(p->cg)) || !type_is_int(cg_top_type(p->cg))) { perr(p, "bitwise operator requires integer operands"); } + coerce_arith_operands(p, cint_common_type(p, cg_top2_type(p->cg), + cg_top_type(p->cg))); cg_binop(p->cg, BO_OR); } } @@ -2949,6 +2991,8 @@ void parse_assign_expr(Parser* p) { } else { cg_binop(p->cg, compound); } + coerce_top_to_type(p, lhs); + if (p->cg_type_sp >= 2) p->cg_type_stack[p->cg_type_sp - 2u] = lhs; cg_store(p->cg); } diff --git a/rt/Makefile b/rt/Makefile @@ -1,11 +1,12 @@ # Included by the repository-root Makefile. Paths are root-relative. -RT_AR ?= llvm-ar -RT_AS ?= $(CC) -RT_AS_COMPILE_FLAGS ?= -c +RT_CC ?= $(BIN) cc +RT_AR ?= $(BIN) ar +RT_AS ?= $(BIN) as +RT_AS_COMPILE_FLAGS ?= RT_BUILD_DIR = rt/build -RT_COMMON_CFLAGS = -ffreestanding -fno-builtin -std=c11 -Wpedantic -Wall -Wextra -Werror +RT_COMMON_CFLAGS = -isystem rt/include -isystem rt/include/libc -Werror RT_LIB_INCS = -Irt/lib/include/common -Irt/lib/impl RT_VARIANTS = \ @@ -157,8 +158,7 @@ RT_CORO_SRCS_riscv32 = rt/lib/coro/riscv32.c rt/lib/coro/coro.c RT_CORO_SRCS_riscv64 = rt/lib/coro/riscv64.c rt/lib/coro/coro.c RT_LDBL128_SRCS = rt/lib/fp_tf/fp_tf.c rt/lib/fp_ti/fp_ti.c -RT_LDBL128_FLAGS = -Irt/lib/include/lp64_le_ldbl128 \ - -include rt/lib/include/lp64_le_ldbl128/tf_supplement.h +RT_LDBL128_FLAGS = -Irt/lib/include/lp64_le_ldbl128 -DCFREERT_LDBL128=1 RT_SAVE_RESTORE_SRCS_lp64 = rt/lib/riscv/rv64.S RT_SAVE_RESTORE_SRCS_ilp32 = rt/lib/riscv/rv32.S @@ -183,30 +183,34 @@ RT_CFLAGS_$(1) := \ -target $$(RT_$(1)_TARGET) \ -DHAS_INT128=$$(RT_$(1)_INT128) \ $$(RT_ABI_INC_$$(RT_$(1)_ABI)) \ - $$(RT_$(1)_ARCH_FLAGS) \ $$(if $$(RT_$(1)_LDBL128),$$(RT_LDBL128_FLAGS)) \ - $$(if $$(RT_$(1)_CORO),-Irt/include) \ - $$(if $$(RT_$(1)_SAVE_RESTORE),$$(RT_SAVE_RESTORE_FLAGS)) \ - $$(RT_AEABI_FLAGS_$$(RT_$(1)_AEABI)) + $$(if $$(RT_$(1)_CORO),-Irt/include) +RT_ASFLAGS_$(1) := \ + -target $$(RT_$(1)_TARGET) \ + -DHAS_INT128=$$(RT_$(1)_INT128) \ + -D__ASSEMBLER__=1 \ + $$(RT_ABI_INC_$$(RT_$(1)_ABI)) \ + $$(if $$(RT_$(1)_LDBL128),$$(RT_LDBL128_FLAGS)) \ + $$(if $$(RT_$(1)_CORO),-Irt/include) RT_OBJS_$(1) := $$(patsubst rt/lib/%,$$(RT_BUILD_DIR)/$(1)/%.o,$$(RT_SRCS_$(1))) rt-$(1): $$(RT_BUILD_DIR)/$(1)/libcfree_rt.a -$$(RT_BUILD_DIR)/$(1)/libcfree_rt.a: $$(RT_OBJS_$(1)) +$$(RT_BUILD_DIR)/$(1)/libcfree_rt.a: $$(RT_OBJS_$(1)) | $$(BIN) @mkdir -p $$(dir $$@) $$(RT_AR) rcs $$@ $$^ -$$(RT_BUILD_DIR)/$(1)/%.s.o: rt/lib/%.s +$$(RT_BUILD_DIR)/$(1)/%.s.o: rt/lib/%.s | $$(BIN) @mkdir -p $$(dir $$@) - $$(RT_AS) $$(RT_CFLAGS_$(1)) $$(RT_AS_COMPILE_FLAGS) $$< -o $$@ + $$(RT_AS) $$(RT_ASFLAGS_$(1)) $$(RT_AS_COMPILE_FLAGS) $$< -o $$@ -$$(RT_BUILD_DIR)/$(1)/%.S.o: rt/lib/%.S +$$(RT_BUILD_DIR)/$(1)/%.S.o: rt/lib/%.S | $$(BIN) @mkdir -p $$(dir $$@) - $$(RT_AS) $$(RT_CFLAGS_$(1)) $$(RT_AS_COMPILE_FLAGS) $$< -o $$@ + $$(RT_AS) $$(RT_ASFLAGS_$(1)) $$(RT_AS_COMPILE_FLAGS) $$< -o $$@ -$$(RT_BUILD_DIR)/$(1)/%.o: rt/lib/% +$$(RT_BUILD_DIR)/$(1)/%.o: rt/lib/% | $$(BIN) @mkdir -p $$(dir $$@) - $$(CC) $$(RT_CFLAGS_$(1)) -c $$< -o $$@ + $$(RT_CC) $$(RT_CFLAGS_$(1)) -c $$< -o $$@ endef $(foreach variant,$(RT_VARIANTS),$(eval $(call RT_VARIANT_template,$(variant)))) diff --git a/rt/lib/atomic/atomic_common.inc b/rt/lib/atomic/atomic_common.inc @@ -108,11 +108,8 @@ void __atomic_store(int size, void *dest, void *src, int model) { int __atomic_compare_exchange(int size, void *ptr, void *expected, void *desired, int success, int failure) { -#define LOCK_FREE_ACTION(type) \ - return __atomic_compare_exchange_n((type *)ptr, (type *)expected, \ - *(type *)desired, 0, success, failure); - LOCK_FREE_CASES(ptr); -#undef LOCK_FREE_ACTION + (void)success; + (void)failure; Lock *l = lock_for_pointer(ptr); lock(l); if (__builtin_memcmp(ptr, expected, size) == 0) { @@ -174,14 +171,13 @@ OPTIMISED_CASES OPTIMISED_CASES #undef OPTIMISED_CASE -#define OPTIMISED_CASE(n, lockfree, type) \ - bool __atomic_compare_exchange_##n(type *ptr, type *expected, type desired, \ - int success, int failure) { \ - if (lockfree(ptr)) \ - return __atomic_compare_exchange_n(ptr, expected, desired, 0, success, \ - failure); \ - Lock *l = lock_for_pointer(ptr); \ - lock(l); \ + #define OPTIMISED_CASE(n, lockfree, type) \ + bool __atomic_compare_exchange_##n(type *ptr, type *expected, type desired, \ + int success, int failure) { \ + (void)success; \ + (void)failure; \ + Lock *l = lock_for_pointer(ptr); \ + lock(l); \ if (*ptr == *expected) { \ *ptr = desired; \ unlock(l); \ diff --git a/rt/lib/atomic/atomic_freestanding.c b/rt/lib/atomic/atomic_freestanding.c @@ -27,10 +27,8 @@ static inline void unlock(Lock* l) { } static inline void lock(Lock* l) { - uintptr_t old = 0; - while (!__atomic_compare_exchange_n((uintptr_t*)l, &old, 1, 1, - __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) - old = 0; + while (__atomic_exchange_n((uintptr_t*)l, 1, __ATOMIC_ACQUIRE)) + ; } #pragma clang diagnostic pop diff --git a/rt/lib/fp_tf/fp_tf.c b/rt/lib/fp_tf/fp_tf.c @@ -9,6 +9,10 @@ // ============================================================ // Section 1: QUAD precision arith / compare / conv / fix // ============================================================ +#ifdef CFREERT_LDBL128 +#include "tf_supplement.h" +#endif + // ---- addtf3.c ---- #define QUAD_PRECISION #include "fp_add_impl.inc" diff --git a/src/api/cg.c b/src/api/cg.c @@ -9,6 +9,8 @@ #include "api/cg_type.h" #include "arch/arch.h" #include "arch/regalloc.h" +#include "asm/asm.h" +#include "asm/asm_lex.h" #include "core/arena.h" #include "core/heap.h" #include "core/pool.h" @@ -4806,6 +4808,19 @@ void cfree_cg_inline_asm(CfreeCg *g, CfreeCgInlineAsm asm_block) { h->free(h, out_reg_owned, noutputs); } +void cfree_cg_file_scope_asm(CfreeCg *g, const char *asm_source, + size_t asm_source_len) { + AsmLexer *lex; + if (!g || !asm_source) + return; + api_local_const_memory_boundary(g); + lex = asm_lex_open_mem(g->c, "<file-scope asm>", asm_source, asm_source_len); + if (!lex) + compiler_panic(g->c, api_no_loc(), "CfreeCg: file-scope asm out of memory"); + asm_parse(g->c, lex, g->mc); + asm_lex_close(lex); +} + /* ============================================================ * Labels / branches * ============================================================ */ diff --git a/src/arch/rv64/arch.c b/src/arch/rv64/arch.c @@ -1,6 +1,7 @@ #include "arch/arch.h" #include "abi/abi_internal.h" +#include "arch/rv64/asm.h" #include "arch/rv64/rv64.h" #include "core/bytes.h" #include "link/link_arch.h" @@ -65,7 +66,7 @@ const ArchImpl arch_impl_rv64 = { .name = "rv64", .abi_vtable = rv64_abi_vtable, .cgtarget_new = rv64_cgtarget_new, - .asm_new = NULL, + .asm_new = rv64_arch_asm_new, .disasm_new = NULL, .apply_label_fixup = rv64_apply_label_fixup, .link = &link_arch_rv64, diff --git a/src/arch/rv64/asm.c b/src/arch/rv64/asm.c @@ -0,0 +1,167 @@ +#include "arch/rv64/asm.h" + +#include <string.h> + +#include "arch/rv64/internal.h" +#include "asm/asm_helpers.h" +#include "core/arena.h" + +typedef struct Rv64Asm { + ArchAsm base; + Compiler* c; +} Rv64Asm; + +typedef struct Rv64Mem { + u32 base; + i32 disp; +} Rv64Mem; + +static int sym_eq(AsmDriver* d, Sym s, const char* lit) { + size_t n = 0; + const char* p = pool_str(asm_driver_pool(d), s, &n); + return p && strlen(lit) == n && memcmp(p, lit, n) == 0; +} + +static int rv_reg_from_name(AsmDriver* d, Sym s, u32* reg_out, int* fp_out) { + size_t n = 0; + const char* p = pool_str(asm_driver_pool(d), s, &n); + u32 r; + int fp = 0; + if (!p || !n) return 0; + if (n == 2 && p[0] == 'r' && p[1] == 'a') + r = RV_RA; + else if (n == 2 && p[0] == 's' && p[1] == 'p') + r = RV_SP; + else if (n == 2 && p[0] == 't' && p[1] == '0') + r = RV_T0; + else if (n == 2 && p[0] == 'a' && p[1] >= '0' && p[1] <= '7') + r = RV_A0 + (u32)(p[1] - '0'); + else if (n == 2 && p[0] == 's' && p[1] >= '0' && p[1] <= '1') + r = RV_S0 + (u32)(p[1] - '0'); + else if (n == 3 && p[0] == 's' && p[1] == '1' && p[2] >= '0' && + p[2] <= '1') + r = 26u + (u32)(p[2] - '0'); + else if (n == 2 && p[0] == 's' && p[1] >= '2' && p[1] <= '9') + r = RV_S2 + (u32)(p[1] - '2'); + else if (n == 3 && p[0] == 'f' && p[1] == 's' && p[2] >= '0' && + p[2] <= '1') { + r = RV_S0 + (u32)(p[2] - '0'); + fp = 1; + } else if (n == 4 && p[0] == 'f' && p[1] == 's' && p[2] == '1' && + p[3] >= '0' && p[3] <= '1') { + r = 26u + (u32)(p[3] - '0'); + fp = 1; + } else if (n == 3 && p[0] == 'f' && p[1] == 's' && p[2] >= '2' && + p[2] <= '9') { + r = RV_S2 + (u32)(p[2] - '2'); + fp = 1; + } else { + return 0; + } + if (reg_out) *reg_out = r; + if (fp_out) *fp_out = fp; + return 1; +} + +static u32 parse_reg(AsmDriver* d, int* fp_out) { + AsmTok t = asm_driver_next(d); + u32 r; + if (t.kind != ASM_TOK_IDENT || !rv_reg_from_name(d, t.v.ident, &r, fp_out)) + asm_driver_panic(d, "rv64 asm: bad register"); + return r; +} + +static Rv64Mem parse_mem(AsmDriver* d) { + Rv64Mem m; + m.disp = (i32)asm_driver_parse_const(d); + asm_driver_expect_punct(d, '(', "'(' in rv64 memory operand"); + m.base = parse_reg(d, NULL); + asm_driver_expect_punct(d, ')', "')' in rv64 memory operand"); + return m; +} + +static void expect_comma(AsmDriver* d) { + if (!asm_driver_eat_comma(d)) asm_driver_panic(d, "rv64 asm: expected ','"); +} + +static void rv64_arch_asm_insn(ArchAsm* base, AsmDriver* d, Sym mnemonic) { + MCEmitter* mc = asm_driver_mc(d); + u32 rd; + u32 rs1; + u32 rs2; + Rv64Mem mem; + int fp = 0; + (void)base; + (void)asm_driver_cur_section(d); + + if (sym_eq(d, mnemonic, "ret")) { + rv64_emit32(mc, rv_i(0, RV_RA, 0, RV_ZERO, RV_JALR)); + return; + } + if (sym_eq(d, mnemonic, "ebreak")) { + rv64_emit32(mc, 0x00100073u); + return; + } + + if (sym_eq(d, mnemonic, "li")) { + rd = parse_reg(d, NULL); + expect_comma(d); + rv64_emit_load_imm(mc, 1, rd, asm_driver_parse_const(d)); + return; + } + if (sym_eq(d, mnemonic, "seqz")) { + rd = parse_reg(d, NULL); + expect_comma(d); + rs1 = parse_reg(d, NULL); + rv64_emit32(mc, rv_sltiu(rd, rs1, 1)); + return; + } + if (sym_eq(d, mnemonic, "mv")) { + rd = parse_reg(d, NULL); + expect_comma(d); + rs1 = parse_reg(d, NULL); + rv64_emit32(mc, rv_addi(rd, rs1, 0)); + return; + } + if (sym_eq(d, mnemonic, "add")) { + rd = parse_reg(d, NULL); + expect_comma(d); + rs1 = parse_reg(d, NULL); + expect_comma(d); + rs2 = parse_reg(d, NULL); + rv64_emit32(mc, rv_add(rd, rs1, rs2)); + return; + } + if (sym_eq(d, mnemonic, "jalr")) { + rs1 = parse_reg(d, NULL); + rv64_emit32(mc, rv_i(0, rs1, 0, RV_RA, RV_JALR)); + return; + } + if (sym_eq(d, mnemonic, "sd") || sym_eq(d, mnemonic, "fsd")) { + rs2 = parse_reg(d, &fp); + expect_comma(d); + mem = parse_mem(d); + rv64_emit32(mc, rv_s(mem.disp, rs2, mem.base, 0x3, fp ? RV_STORE_FP : RV_STORE)); + return; + } + if (sym_eq(d, mnemonic, "ld") || sym_eq(d, mnemonic, "fld")) { + rd = parse_reg(d, &fp); + expect_comma(d); + mem = parse_mem(d); + rv64_emit32(mc, rv_i(mem.disp, mem.base, 0x3, rd, fp ? RV_LOAD_FP : RV_LOAD)); + return; + } + + asm_driver_panic(d, "rv64 asm: unsupported instruction"); +} + +static void rv64_arch_asm_destroy(ArchAsm* base) { (void)base; } + +ArchAsm* rv64_arch_asm_new(Compiler* c) { + Rv64Asm* a = arena_new(c->tu, Rv64Asm); + memset(a, 0, sizeof *a); + a->base.insn = rv64_arch_asm_insn; + a->base.destroy = rv64_arch_asm_destroy; + a->c = c; + return &a->base; +} diff --git a/src/arch/rv64/asm.h b/src/arch/rv64/asm.h @@ -0,0 +1,8 @@ +#ifndef CFREE_ARCH_RV64_ASM_H +#define CFREE_ARCH_RV64_ASM_H + +#include "arch/arch.h" + +ArchAsm* rv64_arch_asm_new(Compiler*); + +#endif diff --git a/src/arch/x64/arch.c b/src/arch/x64/arch.c @@ -1,10 +1,12 @@ #include "arch/arch.h" #include "abi/abi_internal.h" +#include "arch/x64/asm.h" #include "arch/x64/x64.h" #include "core/bytes.h" #include "link/link_arch.h" #include "obj/elf.h" +#include "obj/macho.h" #include "obj/obj.h" extern const LinkArchDesc link_arch_x64; @@ -22,6 +24,15 @@ static const ArchElfOps x64_elf_ops = { .reloc_from = elf_x86_64_reloc_from, }; +static const ArchMachoOps x64_macho_ops = { + .cputype = CPU_TYPE_X86_64, + .cpusubtype = CPU_SUBTYPE_X86_64_ALL, + .reloc_to = macho_x86_64_reloc_to, + .reloc_pcrel = macho_x86_64_reloc_pcrel, + .reloc_length = macho_x86_64_reloc_length, + .reloc_from = macho_x86_64_reloc_from, +}; + static int x64_apply_label_fixup(Compiler* c, const ArchLabelFixup* fx) { (void)c; if (!fx || fx->kind != R_PC32 || fx->width != 4) return 1; @@ -38,12 +49,12 @@ const ArchImpl arch_impl_x64 = { .name = "x64", .abi_vtable = x64_abi_vtable, .cgtarget_new = x64_cgtarget_new, - .asm_new = NULL, + .asm_new = x64_arch_asm_new, .disasm_new = NULL, .apply_label_fixup = x64_apply_label_fixup, .link = &link_arch_x64, .elf = &x64_elf_ops, - .macho = NULL, + .macho = &x64_macho_ops, .register_name = NULL, .register_index = NULL, .register_count = NULL, diff --git a/src/arch/x64/asm.c b/src/arch/x64/asm.c @@ -0,0 +1,253 @@ +#include "arch/x64/asm.h" + +#include <string.h> + +#include "arch/x64/internal.h" +#include "asm/asm_helpers.h" +#include "core/arena.h" + +typedef struct X64Asm { + ArchAsm base; + Compiler* c; +} X64Asm; + +typedef enum X64AsmOperandKind { + X64_ASM_OP_REG, + X64_ASM_OP_IMM, + X64_ASM_OP_MEM, + X64_ASM_OP_IND_REG, +} X64AsmOperandKind; + +typedef struct X64AsmOperand { + u8 kind; + u8 width; + u8 reg; + u8 base; + i64 imm; + i32 disp; +} X64AsmOperand; + +static int sym_eq(AsmDriver* d, Sym s, const char* lit) { + size_t n = 0; + const char* p = pool_str(asm_driver_pool(d), s, &n); + return p && strlen(lit) == n && memcmp(p, lit, n) == 0; +} + +static int x64_reg_from_name(AsmDriver* d, Sym s, u32* reg_out, + u32* width_out) { + size_t n = 0; + const char* p = pool_str(asm_driver_pool(d), s, &n); + u32 width = 8; + u32 reg; + if (!p || n < 2) return 0; + if (p[0] == 'e') { + width = 4; + ++p; + --n; + } else if (p[0] == 'r') { + width = 8; + ++p; + --n; + } else { + return 0; + } + if (n == 2 && p[0] == 'a' && p[1] == 'x') + reg = X64_RAX; + else if (n == 2 && p[0] == 'c' && p[1] == 'x') + reg = X64_RCX; + else if (n == 2 && p[0] == 'd' && p[1] == 'x') + reg = X64_RDX; + else if (n == 2 && p[0] == 'b' && p[1] == 'x') + reg = X64_RBX; + else if (n == 2 && p[0] == 's' && p[1] == 'p') + reg = X64_RSP; + else if (n == 2 && p[0] == 'b' && p[1] == 'p') + reg = X64_RBP; + else if (n == 2 && p[0] == 's' && p[1] == 'i') + reg = X64_RSI; + else if (n == 2 && p[0] == 'd' && p[1] == 'i') + reg = X64_RDI; + else if (n >= 2 && p[0] == '1' && p[1] >= '2' && p[1] <= '5' && + (n == 2 || (n == 3 && p[2] == 'd'))) + reg = X64_R12 + (u32)(p[1] - '2'); + else + return 0; + if (reg_out) *reg_out = reg; + if (width_out) *width_out = width; + return 1; +} + +static u32 parse_reg(AsmDriver* d, u32* width_out) { + AsmTok t; + u32 reg; + if (!asm_driver_eat_punct(d, '%')) asm_driver_panic(d, "x64 asm: expected register"); + t = asm_driver_next(d); + if (t.kind != ASM_TOK_IDENT || + !x64_reg_from_name(d, t.v.ident, &reg, width_out)) { + asm_driver_panic(d, "x64 asm: bad register"); + } + return reg; +} + +static X64AsmOperand parse_operand(AsmDriver* d) { + X64AsmOperand op; + AsmTok t; + memset(&op, 0, sizeof op); + t = asm_driver_peek(d); + if (asm_driver_eat_punct(d, '*')) { + op.kind = X64_ASM_OP_IND_REG; + op.reg = (u8)parse_reg(d, NULL); + return op; + } + if (asm_driver_eat_punct(d, '$')) { + op.kind = X64_ASM_OP_IMM; + op.imm = asm_driver_parse_const(d); + return op; + } + if (asm_driver_tok_is_punct(t, '%')) { + u32 width = 8; + op.kind = X64_ASM_OP_REG; + op.reg = (u8)parse_reg(d, &width); + op.width = (u8)width; + return op; + } + op.kind = X64_ASM_OP_MEM; + op.disp = 0; + if (!asm_driver_tok_is_punct(t, '(')) { + op.disp = (i32)asm_driver_parse_const(d); + } + asm_driver_expect_punct(d, '(', "'(' in x64 memory operand"); + op.base = (u8)parse_reg(d, NULL); + asm_driver_expect_punct(d, ')', "')' in x64 memory operand"); + return op; +} + +static void expect_comma(AsmDriver* d) { + if (!asm_driver_eat_comma(d)) asm_driver_panic(d, "x64 asm: expected ','"); +} + +static void emit_cmov_eq(MCEmitter* mc, u32 dst, u32 src) { + u8 op[2] = {0x0f, 0x44}; + emit_rex(mc, 1, dst, 0, src); + mc->emit_bytes(mc, op, 2); + { + u8 mr = modrm(3u, dst, src); + mc->emit_bytes(mc, &mr, 1); + } +} + +static void emit_indirect_branch(MCEmitter* mc, u32 sub, u32 reg) { + u8 op = 0xff; + emit_rex(mc, 0, 0, 0, reg); + mc->emit_bytes(mc, &op, 1); + { + u8 mr = modrm(3u, sub, reg); + mc->emit_bytes(mc, &mr, 1); + } +} + +static void emit_ud2(MCEmitter* mc) { + u8 op[2] = {0x0f, 0x0b}; + mc->emit_bytes(mc, op, 2); +} + +static void x64_arch_asm_insn(ArchAsm* base, AsmDriver* d, Sym mnemonic) { + X64Asm* a = (X64Asm*)base; + MCEmitter* mc = asm_driver_mc(d); + X64AsmOperand src; + X64AsmOperand dst; + (void)a; + (void)asm_driver_cur_section(d); + + if (sym_eq(d, mnemonic, "ret")) { + emit_ret(mc); + return; + } + if (sym_eq(d, mnemonic, "ud2")) { + emit_ud2(mc); + return; + } + + src = parse_operand(d); + if (sym_eq(d, mnemonic, "jmpq")) { + if (src.kind != X64_ASM_OP_IND_REG) asm_driver_panic(d, "x64 asm: jmpq form"); + emit_indirect_branch(mc, 4u, src.reg); + return; + } + if (sym_eq(d, mnemonic, "callq")) { + if (src.kind != X64_ASM_OP_IND_REG) asm_driver_panic(d, "x64 asm: callq form"); + emit_indirect_branch(mc, 2u, src.reg); + return; + } + + expect_comma(d); + dst = parse_operand(d); + + if (sym_eq(d, mnemonic, "movq")) { + if (src.kind == X64_ASM_OP_REG && dst.kind == X64_ASM_OP_MEM) { + emit_mov_store(mc, 8, src.reg, dst.base, dst.disp); + return; + } + if (src.kind == X64_ASM_OP_MEM && dst.kind == X64_ASM_OP_REG) { + emit_mov_load(mc, 8, 0, dst.reg, src.base, src.disp); + return; + } + if (src.kind == X64_ASM_OP_REG && dst.kind == X64_ASM_OP_REG) { + emit_mov_rr(mc, 1, dst.reg, src.reg); + return; + } + } else if (sym_eq(d, mnemonic, "movl")) { + if (src.kind == X64_ASM_OP_IMM && dst.kind == X64_ASM_OP_REG) { + x64_emit_load_imm(mc, 0, dst.reg, src.imm); + return; + } + } else if (sym_eq(d, mnemonic, "movslq")) { + if (src.kind == X64_ASM_OP_REG && dst.kind == X64_ASM_OP_REG) { + emit_extend_rr(mc, 1, 1, 4, dst.reg, src.reg); + return; + } + } else if (sym_eq(d, mnemonic, "leaq")) { + if (src.kind == X64_ASM_OP_MEM && dst.kind == X64_ASM_OP_REG) { + emit_lea(mc, dst.reg, src.base, src.disp); + return; + } + } else if (sym_eq(d, mnemonic, "xorl")) { + if (src.kind == X64_ASM_OP_REG && dst.kind == X64_ASM_OP_REG) { + emit_alu_rr(mc, 0, 0x31, dst.reg, src.reg); + return; + } + } else if (sym_eq(d, mnemonic, "testq")) { + if (src.kind == X64_ASM_OP_REG && dst.kind == X64_ASM_OP_REG) { + emit_alu_rr(mc, 1, 0x85, dst.reg, src.reg); + return; + } + } else if (sym_eq(d, mnemonic, "cmoveq")) { + if (src.kind == X64_ASM_OP_REG && dst.kind == X64_ASM_OP_REG) { + emit_cmov_eq(mc, dst.reg, src.reg); + return; + } + } else if (sym_eq(d, mnemonic, "andq")) { + if (src.kind == X64_ASM_OP_IMM && dst.kind == X64_ASM_OP_REG) { + if (imm_fits_i8(src.imm)) + emit_alu_imm8(mc, 1, 4u, dst.reg, (i8)src.imm); + else if (imm_fits_i32(src.imm)) + emit_alu_imm32(mc, 1, 4u, dst.reg, (i32)src.imm); + else + asm_driver_panic(d, "x64 asm: andq immediate out of range"); + return; + } + } + + asm_driver_panic(d, "x64 asm: unsupported instruction form"); +} + +static void x64_arch_asm_destroy(ArchAsm* base) { (void)base; } + +ArchAsm* x64_arch_asm_new(Compiler* c) { + X64Asm* a = arena_new(c->tu, X64Asm); + memset(a, 0, sizeof *a); + a->base.insn = x64_arch_asm_insn; + a->base.destroy = x64_arch_asm_destroy; + a->c = c; + return &a->base; +} diff --git a/src/arch/x64/asm.h b/src/arch/x64/asm.h @@ -0,0 +1,8 @@ +#ifndef CFREE_ARCH_X64_ASM_H +#define CFREE_ARCH_X64_ASM_H + +#include "arch/arch.h" + +ArchAsm* x64_arch_asm_new(Compiler*); + +#endif diff --git a/src/obj/macho.h b/src/obj/macho.h @@ -258,5 +258,9 @@ u32 macho_aarch64_reloc_to(u32 kind /* RelocKind */); u32 macho_aarch64_reloc_pcrel(u32 kind /* RelocKind */); u32 macho_aarch64_reloc_length(u32 kind /* RelocKind */); u32 macho_aarch64_reloc_from(u32 macho_type); +u32 macho_x86_64_reloc_to(u32 kind /* RelocKind */); +u32 macho_x86_64_reloc_pcrel(u32 kind /* RelocKind */); +u32 macho_x86_64_reloc_length(u32 kind /* RelocKind */); +u32 macho_x86_64_reloc_from(u32 macho_type); #endif diff --git a/src/obj/macho_reloc_x86_64.c b/src/obj/macho_reloc_x86_64.c @@ -0,0 +1,84 @@ +#include "core/util.h" +#include "obj/macho.h" + +u32 macho_x86_64_reloc_to(u32 kind /* RelocKind */) { + switch (kind) { + case R_NONE: + return (u32)-1; + case R_ABS64: + case R_ABS32: + return X86_64_RELOC_UNSIGNED; + case R_PC32: + case R_REL32: + case R_PC64: + case R_REL64: + case R_X64_PC8: + return X86_64_RELOC_SIGNED; + case R_PLT32: + case R_X64_PLT32: + return X86_64_RELOC_BRANCH; + case R_X64_GOTPCRELX: + case R_X64_REX_GOTPCRELX: + return X86_64_RELOC_GOT_LOAD; + case R_X64_GOTPCREL: + return X86_64_RELOC_GOT; + case R_X64_TPOFF32: + return X86_64_RELOC_TLV; + default: + return (u32)-1; + } +} + +u32 macho_x86_64_reloc_pcrel(u32 kind /* RelocKind */) { + switch (kind) { + case R_PC32: + case R_REL32: + case R_PC64: + case R_REL64: + case R_X64_PC8: + case R_PLT32: + case R_X64_PLT32: + case R_X64_GOTPCREL: + case R_X64_GOTPCRELX: + case R_X64_REX_GOTPCRELX: + case R_X64_TPOFF32: + return 1; + default: + return 0; + } +} + +u32 macho_x86_64_reloc_length(u32 kind /* RelocKind */) { + switch (kind) { + case R_ABS64: + case R_PC64: + case R_REL64: + return 3; + case R_X64_PC8: + return 0; + default: + return 2; + } +} + +u32 macho_x86_64_reloc_from(u32 macho_type) { + switch (macho_type) { + case X86_64_RELOC_UNSIGNED: + return R_ABS64; + case X86_64_RELOC_SIGNED: + case X86_64_RELOC_SIGNED_1: + case X86_64_RELOC_SIGNED_2: + case X86_64_RELOC_SIGNED_4: + return R_PC32; + case X86_64_RELOC_BRANCH: + return R_X64_PLT32; + case X86_64_RELOC_GOT_LOAD: + return R_X64_REX_GOTPCRELX; + case X86_64_RELOC_GOT: + return R_X64_GOTPCREL; + case X86_64_RELOC_TLV: + return R_X64_TPOFF32; + default: + return (u32)-1; + } +}