kit

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

commit ea2a1668f6dca6154f43339eb70975ff292462f9
parent 9d435bb0ab6d807c382d87031faef722fdbba8a6
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 20 May 2026 06:43:23 -0700

Fix self-hosted stage build without clang fallback

Diffstat:
Mdriver/cc.c | 14++++++++++++--
Mdriver/hosted.c | 163++++---------------------------------------------------------------------------
Mdriver/hosted.h | 1+
Mdriver/runtime.c | 7+++++++
Mdriver/runtime.h | 2++
Minclude/cfree/cg.h | 1+
Mlang/c/parse/cg_adapter.c | 1+
Mlang/c/parse/parse.c | 14++++++++++++--
Mlang/c/parse/parse_expr.c | 32++++++++++++++++++++++++++++++++
Mlang/c/parse/parse_priv.h | 14++++++++++++++
Mlang/c/parse/parse_type.c | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Mlang/c/pp/pp.c | 4++++
Mlang/c/pp/pp.h | 3+++
Mlang/c/pp/pp_directive.c | 88++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Mlang/c/pp/pp_priv.h | 5+++++
Mlang/c/type/type.c | 6++++++
Mlang/c/type/type.h | 4+++-
Mlang/toy/asm.c | 29+++++++++++++++++------------
Mlang/toy/data.c | 40++++++++++++++++++++--------------------
Mlang/toy/decls.c | 40+++++++++++++++++++++++-----------------
Mlang/toy/expr.c | 1-
Mlang/toy/internal.h | 3+++
Mlang/toy/parser.c | 28++++++++++++++--------------
Mlang/toy/parser_core.c | 77++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Mlang/toy/symbols.c | 14+++++++-------
Mlang/toy/types.c | 17++++++++++-------
Art/include/stdio.h | 15+++++++++++++++
Art/include/stdlib.h | 47+++++++++++++++++++++++++++++++++++++++++++++++
Art/include/string.h | 25+++++++++++++++++++++++++
Mscripts/stage2_link.sh | 49++++++++++++-------------------------------------
Msrc/arch/aa64/arch.c | 6++++++
Msrc/arch/aa64/asm.c | 20+++++++++++++-------
Msrc/arch/aa64/isa.c | 8++++++++
Msrc/arch/aa64/isa.h | 2+-
Msrc/arch/aa64/ops.c | 86++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Msrc/arch/rv64/arch.c | 4++++
Msrc/arch/x64/arch.c | 6++++++
Msrc/cg/type.c | 4++++
Msrc/cg/type.h | 1+
Msrc/jit/tlv_thunk_aarch64.S | 12++++--------
Atest/asm/encode/aa64_stp_ldp_q.expected.hex | 1+
Atest/asm/encode/aa64_stp_ldp_q.s | 7+++++++
Atest/parse/cases/attr_asm_label_decl.c | 6++++++
Atest/parse/cases/attr_asm_label_decl.expected | 1+
Atest/parse/cases/builtin_clear_cache_01.c | 5+++++
Atest/parse/cases/builtin_clear_cache_01.expected | 1+
Atest/parse/cases/gnu_inline_alias_01.c | 11+++++++++++
Atest/parse/cases/gnu_inline_alias_01.expected | 1+
Atest/parse/cases/gnu_thread_storage_01.c | 6++++++
Atest/parse/cases/gnu_thread_storage_01.expected | 1+
Atest/parse/cases/inline_external_01_local.c | 9+++++++++
Atest/parse/cases/inline_external_01_local.expected | 1+
Atest/parse/cases/large_struct_assign_01.c | 11+++++++++++
Atest/parse/cases/large_struct_assign_01.expected | 1+
Atest/parse/cases/pragma_pack_01_struct_size.c | 12++++++++++++
Atest/parse/cases/pragma_pack_01_struct_size.expected | 1+
Atest/pp/cases/8e_if_defined_from_macro_expansion.c | 6++++++
Atest/pp/cases/8e_if_defined_from_macro_expansion.expected | 1+
58 files changed, 689 insertions(+), 376 deletions(-)

diff --git a/driver/cc.c b/driver/cc.c @@ -739,7 +739,7 @@ static int cc_apply_hosted_profile(CcOptions* o) { int link_action = !o->compile_only && !o->preprocess_only && !o->dump_tokens && o->dep_mode != CC_DEP_M && o->dep_mode != CC_DEP_MM; - if (!link_action || !o->wants_hosted_libc || o->shared) return 0; + if (!o->wants_hosted_libc || o->shared) return 0; if (o->no_stdlib || o->no_defaultlibs) { driver_errf(CC_TOOL, "-lc hosted expansion is disabled by -nostdlib/-nodefaultlibs"); @@ -756,6 +756,7 @@ static int cc_apply_hosted_profile(CcOptions* o) { req.support_dir = o->support_dir; req.driver_path = o->driver_path; req.static_link = o->static_link; + req.link_inputs = link_action; if (driver_hosted_resolve(&req, &o->hosted) != 0) return 1; for (i = 0; i < o->hosted.nsystem_includes; ++i) { o->cf.system_include_dirs[o->cf.nsystem_include_dirs++] = @@ -764,6 +765,7 @@ static int cc_apply_hosted_profile(CcOptions* o) { for (i = 0; i < o->hosted.ndefines; ++i) { o->cf.defines[o->cf.ndefines++] = o->hosted.defines[i]; } + if (!link_action) return 0; for (i = 0; i < o->hosted.nbefore; ++i) { if (o->no_startfiles) break; if (cc_append_hosted_input(o, &o->hosted.before[i], insert_pos, 1) != 0) @@ -2195,7 +2197,15 @@ int driver_cc(int argc, char** argv) { 0) { runtime_resolved = 1; if (co.nsource_files || co.nsource_memory) { - if (driver_runtime_add_freestanding_headers(&runtime, &co.cf) != 0) { + int add_headers; + if (co.hosted.profile_name) { + add_headers = driver_runtime_append_freestanding_headers(&runtime, + &co.cf); + } else { + add_headers = driver_runtime_add_freestanding_headers(&runtime, + &co.cf); + } + if (add_headers != 0) { driver_errf(CC_TOOL, "failed to add freestanding headers"); driver_runtime_support_fini(&env, &runtime); cc_options_release(&co); diff --git a/driver/hosted.c b/driver/hosted.c @@ -220,19 +220,6 @@ static int hosted_add_linux_defines(DriverHostedPlan* plan, int gnu) { return 0; } -static const char* hosted_linux_arch_suffix(CfreeArchKind arch) { - switch (arch) { - case CFREE_ARCH_ARM_64: - return "aarch64"; - case CFREE_ARCH_X86_64: - return "x64"; - case CFREE_ARCH_RV64: - return "rv64"; - default: - return NULL; - } -} - static const char* hosted_glibc_interp(CfreeArchKind arch) { switch (arch) { case CFREE_ARCH_ARM_64: @@ -259,139 +246,12 @@ static const char* hosted_musl_interp(CfreeArchKind arch) { } } -static int hosted_add_linux_shim(DriverHostedPlan* plan, - const DriverHostedRequest* req, - const char* support_root) { - const char* suffix = hosted_linux_arch_suffix(req->target.arch); - char rel[64]; - char devrel[64]; - if (!suffix) { - driver_errf(req->tool, "no hosted Linux profile for target architecture"); - return 1; - } - rel[0] = '\0'; - devrel[0] = '\0'; - { - const char prefix[] = "rt/lib/cfree_hosted/linux-"; - const char obj[] = ".o"; - size_t off = 0; - size_t n = driver_strlen(prefix); - size_t s = driver_strlen(suffix); - driver_memcpy(rel + off, prefix, n); - off += n; - driver_memcpy(rel + off, suffix, s); - off += s; - driver_memcpy(rel + off, obj, sizeof(obj)); - } - { - const char prefix[] = "build/cfree_hosted/linux-"; - const char obj[] = ".o"; - size_t off = 0; - size_t n = driver_strlen(prefix); - size_t s = driver_strlen(suffix); - driver_memcpy(devrel + off, prefix, n); - off += n; - driver_memcpy(devrel + off, suffix, s); - off += s; - driver_memcpy(devrel + off, obj, sizeof(obj)); - } - { - size_t size = 0; - char* path = hosted_join2(req->env, support_root, rel, &size); - if (!path) { - driver_errf(req->tool, "out of memory"); - return 1; - } - if (driver_path_exists(path)) { - return hosted_add_input(plan->after, &plan->nafter, - DRIVER_HOSTED_MAX_AFTER, - DRIVER_HOSTED_INPUT_OBJECT, path, size); - } - driver_free(req->env, path, size); - } - { - size_t size = 0; - char* path = hosted_join2(req->env, support_root, devrel, &size); - if (!path) { - driver_errf(req->tool, "out of memory"); - return 1; - } - if (driver_path_exists(path)) { - return hosted_add_input(plan->after, &plan->nafter, - DRIVER_HOSTED_MAX_AFTER, - DRIVER_HOSTED_INPUT_OBJECT, path, size); - } - driver_errf(req->tool, "hosted profile missing required file: %s", path); - driver_free(req->env, path, size); - } - return 1; -} - -static int hosted_add_macos_shim(DriverHostedPlan* plan, - const DriverHostedRequest* req, - const char* support_root) { - { - size_t size = 0; - char* path = - hosted_join2(req->env, support_root, - "rt/lib/cfree_hosted/libcfree_hosted_macos.a", &size); - if (!path) { - driver_errf(req->tool, "out of memory"); - return 1; - } - if (driver_path_exists(path)) { - return hosted_add_input(plan->after, &plan->nafter, - DRIVER_HOSTED_MAX_AFTER, - DRIVER_HOSTED_INPUT_ARCHIVE, path, size); - } - driver_free(req->env, path, size); - } - { - size_t size = 0; - char* path = hosted_join2(req->env, support_root, - "build/libcfree_hosted_macos.a", &size); - if (!path) { - driver_errf(req->tool, "out of memory"); - return 1; - } - if (driver_path_exists(path)) { - return hosted_add_input(plan->after, &plan->nafter, - DRIVER_HOSTED_MAX_AFTER, - DRIVER_HOSTED_INPUT_ARCHIVE, path, size); - } - driver_free(req->env, path, size); - } - { - size_t dir_size = 0; - size_t size = 0; - char* dir = hosted_driver_dir(req->env, req->driver_path, &dir_size); - char* path; - if (!dir) { - driver_errf(req->tool, "out of memory"); - return 1; - } - path = hosted_join2(req->env, dir, "libcfree_hosted_macos.a", &size); - driver_free(req->env, dir, dir_size); - if (!path) { - driver_errf(req->tool, "out of memory"); - return 1; - } - if (driver_path_exists(path)) { - return hosted_add_input(plan->after, &plan->nafter, - DRIVER_HOSTED_MAX_AFTER, - DRIVER_HOSTED_INPUT_ARCHIVE, path, size); - } - driver_errf(req->tool, "hosted profile missing required file: %s", path); - driver_free(req->env, path, size); - } - return 1; -} - static int hosted_resolve_darwin(const DriverHostedRequest* req, DriverHostedPlan* plan, const char* support_root) { size_t size = 0; char* libsystem = NULL; + (void)support_root; if (!req->sysroot || !req->sysroot[0]) { driver_errf(req->tool, "Darwin hosted profile requires --sysroot; try: --sysroot " @@ -400,16 +260,12 @@ static int hosted_resolve_darwin(const DriverHostedRequest* req, } plan->profile_name = "macos-libSystem"; if (hosted_add_darwin_defines(plan) != 0 || - hosted_add_existing_include(plan, req->env, support_root, - "rt/include/libc") != 0 || hosted_add_existing_include(plan, req->env, req->sysroot, "usr/include") != 0) { driver_errf(req->tool, "out of memory"); return 1; } - if (hosted_add_macos_shim(plan, req, support_root) != 0) { - return 1; - } + if (!req->link_inputs) return 0; libsystem = hosted_join2(req->env, req->sysroot, "usr/lib/libSystem.tbd", &size); if (!libsystem) { @@ -443,15 +299,15 @@ static int hosted_resolve_darwin(const DriverHostedRequest* req, static int hosted_resolve_linux_musl_static(const DriverHostedRequest* req, DriverHostedPlan* plan, const char* support_root) { + (void)support_root; plan->profile_name = "linux-musl-static"; if (hosted_add_linux_defines(plan, 0) != 0 || - hosted_add_existing_include(plan, req->env, support_root, - "rt/include/libc") != 0 || hosted_add_existing_include(plan, req->env, req->sysroot, "include") != 0) { driver_errf(req->tool, "out of memory"); return 1; } + if (!req->link_inputs) return 0; if (hosted_add_required(plan->before, &plan->nbefore, DRIVER_HOSTED_MAX_BEFORE, req, req->sysroot, "lib/crt1.o", DRIVER_HOSTED_INPUT_OBJECT) != 0 || @@ -459,7 +315,6 @@ static int hosted_resolve_linux_musl_static(const DriverHostedRequest* req, DRIVER_HOSTED_MAX_BEFORE, req, req->sysroot, "lib/crti.o", DRIVER_HOSTED_INPUT_OBJECT) != 0) return 1; - if (hosted_add_linux_shim(plan, req, support_root) != 0) return 1; if (hosted_add_required(plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, req->sysroot, "lib/libc.a", DRIVER_HOSTED_INPUT_ARCHIVE) != 0) @@ -475,6 +330,7 @@ static int hosted_resolve_linux_musl_dynamic(const DriverHostedRequest* req, DriverHostedPlan* plan, const char* support_root) { const char* interp = hosted_musl_interp(req->target.arch); + (void)support_root; if (!interp) { driver_errf(req->tool, "no hosted musl profile for target architecture"); return 1; @@ -482,13 +338,12 @@ static int hosted_resolve_linux_musl_dynamic(const DriverHostedRequest* req, plan->profile_name = "linux-musl-dynamic"; plan->interp_path = interp; if (hosted_add_linux_defines(plan, 0) != 0 || - hosted_add_existing_include(plan, req->env, support_root, - "rt/include/libc") != 0 || hosted_add_existing_include(plan, req->env, req->sysroot, "include") != 0) { driver_errf(req->tool, "out of memory"); return 1; } + if (!req->link_inputs) return 0; if (hosted_add_required(plan->before, &plan->nbefore, DRIVER_HOSTED_MAX_BEFORE, req, req->sysroot, "lib/Scrt1.o", DRIVER_HOSTED_INPUT_OBJECT) != 0 || @@ -496,7 +351,6 @@ static int hosted_resolve_linux_musl_dynamic(const DriverHostedRequest* req, DRIVER_HOSTED_MAX_BEFORE, req, req->sysroot, "lib/crti.o", DRIVER_HOSTED_INPUT_OBJECT) != 0) return 1; - if (hosted_add_linux_shim(plan, req, support_root) != 0) return 1; if (hosted_add_required(plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, req->sysroot, "lib/libc.so", DRIVER_HOSTED_INPUT_DSO) != 0) @@ -512,6 +366,7 @@ static int hosted_resolve_linux_glibc_dynamic(const DriverHostedRequest* req, DriverHostedPlan* plan, const char* support_root) { const char* interp = hosted_glibc_interp(req->target.arch); + (void)support_root; if (!interp) { driver_errf(req->tool, "no hosted glibc profile for target architecture"); return 1; @@ -519,8 +374,6 @@ static int hosted_resolve_linux_glibc_dynamic(const DriverHostedRequest* req, plan->profile_name = "linux-glibc-dynamic"; plan->interp_path = interp; if (hosted_add_linux_defines(plan, 1) != 0 || - hosted_add_existing_include(plan, req->env, support_root, - "rt/include/libc") != 0 || hosted_add_existing_include(plan, req->env, req->sysroot, "include") != 0) { driver_errf(req->tool, "out of memory"); @@ -545,6 +398,7 @@ static int hosted_resolve_linux_glibc_dynamic(const DriverHostedRequest* req, default: break; } + if (!req->link_inputs) return 0; if (hosted_add_required(plan->before, &plan->nbefore, DRIVER_HOSTED_MAX_BEFORE, req, req->sysroot, "lib/Scrt1.o", DRIVER_HOSTED_INPUT_OBJECT) != 0 || @@ -552,7 +406,6 @@ static int hosted_resolve_linux_glibc_dynamic(const DriverHostedRequest* req, DRIVER_HOSTED_MAX_BEFORE, req, req->sysroot, "lib/crti.o", DRIVER_HOSTED_INPUT_OBJECT) != 0) return 1; - if (hosted_add_linux_shim(plan, req, support_root) != 0) return 1; if (hosted_add_required(plan->after, &plan->nafter, DRIVER_HOSTED_MAX_AFTER, req, req->sysroot, "lib/libc.so.6", DRIVER_HOSTED_INPUT_DSO) != 0 || diff --git a/driver/hosted.h b/driver/hosted.h @@ -52,6 +52,7 @@ typedef struct DriverHostedRequest { const char* support_dir; const char* driver_path; int static_link; + int link_inputs; } DriverHostedRequest; int driver_hosted_resolve(const DriverHostedRequest* req, diff --git a/driver/runtime.c b/driver/runtime.c @@ -274,6 +274,13 @@ int driver_runtime_add_freestanding_headers(const DriverRuntimeSupport* s, return 0; } +int driver_runtime_append_freestanding_headers(const DriverRuntimeSupport* s, + DriverCflags* cf) { + if (!s || !s->include_dir || !cf) return 1; + cf->system_include_dirs[cf->nsystem_include_dirs++] = s->include_dir; + return 0; +} + static const RuntimeVariant* rt_variant_for_target(CfreeTarget target) { uint32_t i; for (i = 0; i < (uint32_t)(sizeof(kRtVariants) / sizeof(kRtVariants[0])); diff --git a/driver/runtime.h b/driver/runtime.h @@ -22,6 +22,8 @@ void driver_runtime_support_fini(DriverEnv* env, DriverRuntimeSupport* s); int driver_runtime_add_freestanding_headers(const DriverRuntimeSupport* s, DriverCflags* cf); +int driver_runtime_append_freestanding_headers(const DriverRuntimeSupport* s, + DriverCflags* cf); int driver_runtime_ensure_archive(DriverEnv* env, const DriverRuntimeSupport* support, diff --git a/include/cfree/cg.h b/include/cfree/cg.h @@ -111,6 +111,7 @@ typedef struct CfreeCgField { CfreeSym name; /* 0 for anonymous fields/tuple elements */ CfreeCgTypeId type; uint32_t align_override; /* 0 = natural, 1 = packed, >1 explicit align */ + uint32_t max_align; /* 0 = natural, otherwise cap field alignment */ uint32_t flags; /* CfreeCgFieldFlag */ uint16_t bit_width; /* valid for bit-fields, may be 0 for barriers */ uint16_t bit_offset; /* filled by record-field queries */ diff --git a/lang/c/parse/cg_adapter.c b/lang/c/parse/cg_adapter.c @@ -325,6 +325,7 @@ FrameSlot pcg_local(Parser* p, const FrameSlotDesc* fsd) { FrameSlot pcg_param_slot(Parser* p, u32 index, const FrameSlotDesc* fsd) { CfreeCgLocalAttrs attrs; + if (!pcg_emit_enabled(p)) return FRAME_SLOT_NONE; memset(&attrs, 0, sizeof attrs); attrs.name = fsd->name; attrs.align = fsd->align; diff --git a/lang/c/parse/parse.c b/lang/c/parse/parse.c @@ -1089,8 +1089,13 @@ static SymEntry* declare_function(Parser* p, Sym fname, const Type* fn_ty, decl_in.name = fname; decl_in.type = fn_ty; decl_in.loc = fname_loc; - decl_in.storage = (specs->storage == DS_STATIC) ? DS_STATIC : DS_EXTERN; - decl_in.linkage = (specs->storage == DS_STATIC) ? DL_INTERNAL : DL_EXTERNAL; + decl_in.storage = + (specs->storage == DS_STATIC || + ((specs->flags & DF_INLINE) && specs->storage != DS_EXTERN)) + ? DS_STATIC + : DS_EXTERN; + decl_in.linkage = + (decl_in.storage == DS_STATIC) ? DL_INTERNAL : DL_EXTERNAL; decl_in.visibility = SV_DEFAULT; attr_list_to_decl(p->c, p->decls, specs->attrs, &decl_in); attr_list_to_decl(p->c, p->decls, dattrs, &decl_in); @@ -1482,6 +1487,7 @@ void parse_c(Compiler* c, Pool* pool, Pp* pp, DeclTable* decls, CG* cg) { p.sym_b_memmove = pool_intern_cstr(p.pool, "__builtin_memmove"); p.sym_b_memcmp = pool_intern_cstr(p.pool, "__builtin_memcmp"); p.sym_b_memset = pool_intern_cstr(p.pool, "__builtin_memset"); + p.sym_b_clear_cache = pool_intern_cstr(p.pool, "__builtin___clear_cache"); p.sym_b_isnan = pool_intern_cstr(p.pool, "__builtin_isnan"); p.sym_func = pool_intern_cstr(p.pool, "__func__"); p.sym_func_gcc = pool_intern_cstr(p.pool, "__FUNCTION__"); @@ -1496,6 +1502,10 @@ void parse_c(Compiler* c, Pool* pool, Pp* pp, DeclTable* decls, CG* cg) { p.sym_attribute = pool_intern_cstr(p.pool, "__attribute__"); p.sym_volatile_alias = pool_intern_cstr(p.pool, "__volatile__"); p.sym_alignof_alias = pool_intern_cstr(p.pool, "__alignof__"); + p.sym_asm_alias = pool_intern_cstr(p.pool, "__asm"); + p.sym_inline_alias = pool_intern_cstr(p.pool, "__inline"); + p.sym_inline_alias2 = pool_intern_cstr(p.pool, "__inline__"); + p.sym_thread_alias = pool_intern_cstr(p.pool, "__thread"); p.sym_int128 = pool_intern_cstr(p.pool, "__int128"); p.sym_int128_t = pool_intern_cstr(p.pool, "__int128_t"); p.sym_uint128_t = pool_intern_cstr(p.pool, "__uint128_t"); diff --git a/lang/c/parse/parse_expr.c b/lang/c/parse/parse_expr.c @@ -1217,6 +1217,37 @@ static int parse_builtin_mem_call(Parser* p, Sym name, SrcLoc loc) { return 1; } +static int parse_builtin_clear_cache_call(Parser* p, Sym name, SrcLoc loc) { + const Type* void_ty = type_void(p->pool); + const Type* void_ptr_ty = type_ptr(p->pool, void_ty); + const Type* params[2]; + const Type* fn_ty; + CfreeCgSym sym; + + if (name != p->sym_b_clear_cache) return 0; + + advance(p); /* IDENT */ + expect_punct(p, '(', "'(' after __builtin___clear_cache"); + parse_assign_expr(p); + to_rvalue(p); + coerce_top_to_type(p, void_ptr_ty); + expect_punct(p, ',', "',' in __builtin___clear_cache"); + parse_assign_expr(p); + to_rvalue(p); + coerce_top_to_type(p, void_ptr_ty); + expect_punct(p, ')', "')' after __builtin___clear_cache"); + + params[0] = void_ptr_ty; + params[1] = void_ptr_ty; + fn_ty = type_func(p->pool, void_ty, params, 2, 0); + sym = pcg_emit_enabled(p) ? builtin_libcall_sym(p, "__clear_cache", fn_ty) + : CFREE_CG_SYM_NONE; + cg_set_loc(p->cg, loc); + pcg_call_symbol(p, sym, 2, fn_ty); + cg_push_int(p->cg, 0, ty_int(p)); + return 1; +} + static MemOrder parse_atomic_mem_order(Parser* p) { if (p->cur.kind == TOK_NUM) { return (MemOrder)eval_const_int(p, p->cur.loc); @@ -1444,6 +1475,7 @@ static int try_parse_builtin_call(Parser* p) { if (parse_builtin_overflow_call(p, name, loc)) return 1; if (parse_builtin_isnan_call(p, name, loc)) return 1; + if (parse_builtin_clear_cache_call(p, name, loc)) return 1; if (name != p->sym_b_alloca && name != p->sym_b_ctz && name != p->sym_b_ctzl && name != p->sym_b_ctzll && name != p->sym_b_clz && diff --git a/lang/c/parse/parse_priv.h b/lang/c/parse/parse_priv.h @@ -221,6 +221,7 @@ typedef struct Parser { Sym sym_b_memmove; Sym sym_b_memcmp; Sym sym_b_memset; + Sym sym_b_clear_cache; Sym sym_b_isnan; Sym sym_func; /* __func__ */ Sym sym_func_gcc; /* __FUNCTION__ */ @@ -238,6 +239,10 @@ typedef struct Parser { Sym sym_attribute; Sym sym_volatile_alias; Sym sym_alignof_alias; + Sym sym_asm_alias; + Sym sym_inline_alias; + Sym sym_inline_alias2; + Sym sym_thread_alias; Sym sym_int128; /* __int128 */ Sym sym_int128_t; /* __int128_t */ Sym sym_uint128_t; /* __uint128_t */ @@ -375,6 +380,10 @@ static inline CKw ident_kw_inline(const Parser* p, Sym name) { if (p->kw_sym[i] == name) return i; } if (name == p->sym_alignof_alias) return KW_ALIGNOF; + if (name == p->sym_asm_alias) return KW_BUILTIN_ASM; + if (name == p->sym_inline_alias || name == p->sym_inline_alias2) + return KW_INLINE; + if (name == p->sym_thread_alias) return KW_THREAD_LOCAL; return KW_NONE; } @@ -382,6 +391,11 @@ static inline int is_kw(const Parser* p, const Tok* t, CKw k) { if (t->kind != TOK_IDENT) return 0; if (t->v.ident == p->kw_sym[k]) return 1; if (k == KW_ALIGNOF && t->v.ident == p->sym_alignof_alias) return 1; + if (k == KW_BUILTIN_ASM && t->v.ident == p->sym_asm_alias) return 1; + if (k == KW_INLINE && + (t->v.ident == p->sym_inline_alias || t->v.ident == p->sym_inline_alias2)) + return 1; + if (k == KW_THREAD_LOCAL && t->v.ident == p->sym_thread_alias) return 1; return 0; } diff --git a/lang/c/parse/parse_type.c b/lang/c/parse/parse_type.c @@ -102,6 +102,54 @@ int starts_attr(const Parser* p) { return p->cur.kind == TOK_IDENT && p->cur.v.ident == p->sym_attribute; } +static int starts_asm_label(const Parser* p) { + return is_kw(p, &p->cur, KW_ASM) || is_kw(p, &p->cur, KW_BUILTIN_ASM); +} + +static void parse_asm_label(Parser* p) { + advance(p); /* asm / __asm / __asm__ */ + expect_punct(p, '(', "'(' after asm label"); + if (p->cur.kind != TOK_STR) { + perr(p, "expected string literal in asm label"); + } + do { + advance(p); + } while (p->cur.kind == TOK_STR); + expect_punct(p, ')', "')' after asm label"); +} + +static void parse_attrs_and_asm_into(Parser* p, Attr** attrs_out, + Attr** local_attrs) { + for (;;) { + if (starts_attr(p)) { + if (attrs_out) + parse_attrs_into(p, attrs_out); + else + parse_attrs_into(p, local_attrs); + continue; + } + if (starts_asm_label(p)) { + parse_asm_label(p); + continue; + } + break; + } +} + +static void parse_and_discard_attrs_or_asm(Parser* p) { + for (;;) { + if (starts_attr(p)) { + parse_and_discard_attributes(p); + continue; + } + if (starts_asm_label(p)) { + parse_asm_label(p); + continue; + } + break; + } +} + static void attr_canon_range(const char* s, size_t len, const char** out_p, size_t* out_len) { if (len >= 4 && s[0] == '_' && s[1] == '_' && s[len - 1] == '_' && @@ -986,8 +1034,17 @@ const Type* parse_struct_or_union(Parser* p, TypeKind kind, } } expect_punct(p, '{', "'{' to start aggregate body"); + TypeRecordOpts begin_opts; + memset(&begin_opts, 0, sizeof begin_opts); + { + u32 pack_align = pp_pack_alignment(p->pp); + if (pack_align > 65535u) pack_align = 65535u; + begin_opts.max_align = (u16)pack_align; + } + attrs_to_record_opts(rec_attrs, &begin_opts); TypeRecordBuilder* b = - type_record_begin(p->pool, kind, target->rec.tag_id, tag_name); + type_record_begin_ex(p->pool, kind, target->rec.tag_id, tag_name, + begin_opts); parse_member_decls(p, b); expect_punct(p, '}', "'}' after aggregate body"); parse_attrs_into(p, &rec_attrs); @@ -998,6 +1055,9 @@ const Type* parse_struct_or_union(Parser* p, TypeKind kind, } { const Type* fresh = type_record_end(p->pool, b); + target->rec.packed = fresh->rec.packed; + target->rec.max_align = fresh->rec.max_align; + target->rec.align_override = fresh->rec.align_override; type_record_install(target, (Field*)fresh->rec.fields, fresh->rec.nfields); } { @@ -1429,8 +1489,8 @@ const Type* parse_declarator_full_info(Parser* p, const Type* base, q |= Q_ATOMIC; continue; } - if (starts_attr(p)) { - parse_and_discard_attributes(p); + if (starts_attr(p) || starts_asm_label(p)) { + parse_and_discard_attrs_or_asm(p); continue; } break; @@ -1468,8 +1528,8 @@ const Type* parse_declarator_full_info(Parser* p, const Type* base, q |= Q_ATOMIC; continue; } - if (starts_attr(p)) { - parse_and_discard_attributes(p); + if (starts_attr(p) || starts_asm_label(p)) { + parse_and_discard_attrs_or_asm(p); continue; } break; @@ -1483,17 +1543,17 @@ const Type* parse_declarator_full_info(Parser* p, const Type* base, } else if (!allow_abstract) { perr(p, "expected declarator name"); } - if (starts_attr(p)) parse_and_discard_attributes(p); + parse_and_discard_attrs_or_asm(p); expect_punct(p, ')', "')' after nested declarator"); } else if (!allow_abstract) { perr(p, "expected declarator name"); } after_inner_name: - if (starts_attr(p)) parse_and_discard_attributes(p); + parse_and_discard_attrs_or_asm(p); while (n_inner_suffs < 8) { if (!parse_decl_suffix(p, &inner_suffs[n_inner_suffs])) break; ++n_inner_suffs; - if (starts_attr(p)) parse_and_discard_attributes(p); + parse_and_discard_attrs_or_asm(p); } expect_punct(p, ')', "')' after inner declarator"); } @@ -1509,12 +1569,7 @@ after_inner_name: } } - if (starts_attr(p)) { - if (attrs_out) - parse_attrs_into(p, attrs_out); - else - parse_attrs_into(p, &local_attrs); - } + parse_attrs_and_asm_into(p, attrs_out, &local_attrs); DeclSuffix suffs[8]; int nsuffs = 0; @@ -1523,12 +1578,7 @@ after_inner_name: while (nsuffs < 8) { if (!parse_decl_suffix(p, &suffs[nsuffs])) break; ++nsuffs; - if (starts_attr(p)) { - if (attrs_out) - parse_attrs_into(p, attrs_out); - else - parse_attrs_into(p, &local_attrs); - } + parse_attrs_and_asm_into(p, attrs_out, &local_attrs); } base = attrs_apply_type_mode(p, base, attrs_out ? *attrs_out : local_attrs); if (nsuffs == 8 && (is_punct(&p->cur, '[') || is_punct(&p->cur, '('))) { diff --git a/lang/c/pp/pp.c b/lang/c/pp/pp.c @@ -623,6 +623,10 @@ void pp_undef(Pp* pp, const char* name) { mt_del(pp, s); } +uint32_t pp_pack_alignment(const Pp* pp) { + return pp ? pp->pack_align : 0; +} + void pp_add_include_edge(Pp* pp, u32 includer, u32 included, SrcLoc include_loc, int system) { cfree_source_add_include(pp->c, includer, included, include_loc, system); diff --git a/lang/c/pp/pp.h b/lang/c/pp/pp.h @@ -1,6 +1,8 @@ #ifndef CFREE_PP_H #define CFREE_PP_H +#include <stdint.h> + #include "lex/lex.h" typedef struct Pp Pp; @@ -13,6 +15,7 @@ void pp_free(Pp*); void pp_add_include_dir(Pp*, const char* dir, int system); void pp_define(Pp*, const char* name, const char* body); /* -D */ void pp_undef(Pp*, const char* name); /* -U */ +uint32_t pp_pack_alignment(const Pp*); /* Pushes a Lexer onto the include stack. PP takes ownership of the Lexer: * it is closed when the input hits EOF and is popped, or in pp_free if it diff --git a/lang/c/pp/pp_directive.c b/lang/c/pp/pp_directive.c @@ -130,24 +130,24 @@ static void prepass_defined(Pp* pp, const Tok* in, u32 nin, TokVec* out) { } } -/* Macro-expand a sequence of pre-#if tokens to completion. Wraps the - * fixed-buffer arg pre-expansion machinery with TOK_IDENT → 0 - * substitution per §6.10.1 ¶4. */ +/* Macro-expand a sequence of pre-#if tokens to completion. */ static void expand_for_if(Pp* pp, const Tok* in, u32 nin, TokVec* out) { Tok* slice; if (nin == 0) return; slice = arena_array(pp->arena, Tok, nin); memcpy(slice, in, sizeof(Tok) * nin); expand_arg_to_eof(pp, slice, NULL, nin, out); - /* Replace remaining identifiers with `0`. */ - { - u32 i; - Sym zero = pool_intern_cstr(pp->pool, "0"); - for (i = 0; i < out->n; ++i) { - if (out->data[i].kind == TOK_IDENT) { - out->data[i].kind = TOK_NUM; - out->data[i].spelling = zero; - } +} + +/* Replace remaining identifiers with `0` per §6.10.1 ¶4, after `defined` + * has been handled. */ +static void replace_remaining_if_identifiers(Pp* pp, TokVec* toks) { + u32 i; + Sym zero = pool_intern_cstr(pp->pool, "0"); + for (i = 0; i < toks->n; ++i) { + if (toks->data[i].kind == TOK_IDENT) { + toks->data[i].kind = TOK_NUM; + toks->data[i].spelling = zero; } } } @@ -384,15 +384,18 @@ static i64 ee_ternary(EE* e) { i64 eval_if_expr(Pp* pp, const Tok* line, u32 n, SrcLoc loc) { TokVec defs = {0}; TokVec exp = {0}; + TokVec defs2 = {0}; EE e; i64 v; prepass_defined(pp, line, n, &defs); expand_for_if(pp, defs.data, defs.n, &exp); + prepass_defined(pp, exp.data, exp.n, &defs2); + replace_remaining_if_identifiers(pp, &defs2); e.pp = pp; - e.toks = exp.data; - e.n = exp.n; + e.toks = defs2.data; + e.n = defs2.n; e.pos = 0; e.loc = loc; v = ee_ternary(&e); @@ -921,9 +924,66 @@ void emit_pragma_line(Pp* pp, const Tok* line, u32 n, SrcLoc loc) { push_buf(pp, out.data, hids, out.n); } +static int pragma_num_u32(Pp* pp, const Tok* t, u32* out) { + size_t len = 0; + const char* s; + u32 v = 0; + if (!t || t->kind != TOK_NUM || !out) return 0; + s = pool_str(pp->pool, t->spelling, &len); + if (!s || len == 0) return 0; + for (size_t i = 0; i < len; ++i) { + if (s[i] < '0' || s[i] > '9') break; + v = v * 10u + (u32)(s[i] - '0'); + } + *out = v; + return 1; +} + +static void handle_pragma_pack(Pp* pp, const Tok* line, u32 n) { + u32 i = 0; + if (n < 3 || line[0].kind != TOK_IDENT) return; + { + size_t len = 0; + const char* s = pool_str(pp->pool, line[0].v.ident, &len); + if (!s || len != 4 || memcmp(s, "pack", 4) != 0) return; + } + if (line[1].kind != TOK_PUNCT || line[1].v.punct != '(') return; + i = 2; + if (i < n && line[i].kind == TOK_PUNCT && line[i].v.punct == ')') { + pp->pack_align = 0; + return; + } + if (i < n && line[i].kind == TOK_IDENT) { + size_t len = 0; + const char* s = pool_str(pp->pool, line[i].v.ident, &len); + if (s && len == 4 && memcmp(s, "push", 4) == 0) { + if (pp->pack_stack_n < + (u32)(sizeof pp->pack_stack / sizeof pp->pack_stack[0])) { + pp->pack_stack[pp->pack_stack_n++] = pp->pack_align; + } + ++i; + if (i < n && line[i].kind == TOK_PUNCT && line[i].v.punct == ',') { + u32 v = 0; + ++i; + if (i < n && pragma_num_u32(pp, &line[i], &v)) pp->pack_align = v; + } + return; + } + if (s && len == 3 && memcmp(s, "pop", 3) == 0) { + if (pp->pack_stack_n) pp->pack_align = pp->pack_stack[--pp->pack_stack_n]; + return; + } + } + { + u32 v = 0; + if (pragma_num_u32(pp, &line[i], &v)) pp->pack_align = v; + } +} + static void do_pragma(Pp* pp, const Tok* line, u32 n, SrcLoc loc) { /* Forward unrecognised pragmas to the output. STDC pragmas pass * through too; we don't act on them yet. */ + handle_pragma_pack(pp, line, n); emit_pragma_line(pp, line, n, loc); } diff --git a/lang/c/pp/pp_priv.h b/lang/c/pp/pp_priv.h @@ -121,6 +121,11 @@ struct Pp { u32 ninc_dirs; u32 inc_dirs_cap; + /* Current #pragma pack maximum field alignment. 0 means natural. */ + u32 pack_align; + u32 pack_stack[16]; + u32 pack_stack_n; + /* Internal arena: macro bodies, hidesets, expansion buffers, file * data for #include. Lives until pp_free. */ CfreeArena* arena; diff --git a/lang/c/type/type.c b/lang/c/type/type.c @@ -250,6 +250,7 @@ const Type* type_record_end(Pool* p, TypeRecordBuilder* b) { t->rec.nfields = (u16)b->nfields; t->rec.incomplete = 0; t->rec.packed = b->opts.packed; + t->rec.max_align = b->opts.max_align; t->rec.align_override = b->opts.align_override; return t; } @@ -267,6 +268,7 @@ Type* type_record_forward(Pool* p, TypeKind kind, TagId tag_id, Sym tag) { t->rec.nfields = 0; t->rec.incomplete = 1; t->rec.packed = 0; + t->rec.max_align = 0; t->rec.align_override = 0; return t; } @@ -596,6 +598,10 @@ static CfreeCgTypeId type_cg_record_layout(TypeCgLower* l, const Type* t) { fields[i].type = type_cg_lower(l, t->rec.fields[i].type, TYPE_CG_RECORD_FIELD); fields[i].align_override = t->rec.fields[i].align_override; + fields[i].max_align = t->rec.fields[i].max_align; + if (t->rec.max_align && + (fields[i].max_align == 0 || t->rec.max_align < fields[i].max_align)) + fields[i].max_align = t->rec.max_align; if (t->rec.fields[i].flags & FIELD_BITFIELD) { fields[i].flags |= CFREE_CG_FIELD_BITFIELD; fields[i].bit_width = t->rec.fields[i].bitfield_width; diff --git a/lang/c/type/type.h b/lang/c/type/type.h @@ -79,8 +79,8 @@ typedef struct Field { * carries __attribute__((aligned(N))) / ((packed)). Zero means "no * override"; c_abi_record_layout interprets them. */ u16 align_override; + u16 max_align; u8 packed; - u8 pad; } Field; struct Type { @@ -111,6 +111,7 @@ struct Type { * __attribute__((packed)) / ((aligned(N))). Both zero means * "natural layout". c_abi_record_layout honors them. */ u8 packed; + u16 max_align; u16 align_override; } rec; /* struct / union */ struct { @@ -143,6 +144,7 @@ TypeRecordBuilder* type_record_begin(Pool*, TypeKind kind, TagId, * options (e.g. transparent_union) don't churn the call sites. */ typedef struct TypeRecordOpts { u8 packed; + u16 max_align; u16 align_override; } TypeRecordOpts; diff --git a/lang/toy/asm.c b/lang/toy/asm.c @@ -1,6 +1,5 @@ #include "internal.h" -#include <stdlib.h> #include <string.h> typedef struct ToyAsmOperandList { @@ -346,6 +345,7 @@ int toy_parse_typed_asm_tail(ToyParser* p, CfreeCgTypeId result_ty, ToyAsmOperandList inputs = {0}; ToyAsmClobberList clobbers = {0}; uint32_t* record_field_indexes = NULL; + uint32_t record_field_count = 0; uint32_t flags = 0; uint32_t clobber_abi_sets = 0; int have_outputs = 0; @@ -416,14 +416,17 @@ int toy_parse_typed_asm_tail(ToyParser* p, CfreeCgTypeId result_ty, } else if (cfree_cg_type_kind(p->c, result_ty) == CFREE_CG_TYPE_RECORD) { uint32_t i, nfields = cfree_cg_type_record_nfields(p->c, result_ty); uint8_t* seen; + record_field_count = nfields; if (outputs.count != nfields) { toy_error(p, p->cur.loc, "asm record result output count mismatch"); goto done; } - record_field_indexes = (uint32_t*)calloc(nfields, sizeof *record_field_indexes); - seen = (uint8_t*)calloc(nfields, sizeof *seen); + record_field_indexes = (uint32_t*)toy_parser_zalloc( + p, nfields, sizeof *record_field_indexes, "asm record outputs"); + seen = (uint8_t*)toy_parser_zalloc(p, nfields, sizeof *seen, + "asm record outputs"); if (!record_field_indexes || !seen) { - free(seen); + toy_parser_free_mem(p, seen, nfields * sizeof *seen); toy_error(p, p->cur.loc, "out of memory growing asm record outputs"); goto done; } @@ -433,24 +436,24 @@ int toy_parse_typed_asm_tail(ToyParser* p, CfreeCgTypeId result_ty, if (outputs.items[i].name) { if (!toy_asm_record_field_by_name(p, result_ty, outputs.items[i].name, &field_index, &field)) { - free(seen); + toy_parser_free_mem(p, seen, nfields * sizeof *seen); toy_error(p, p->cur.loc, "asm record result output mismatch"); goto done; } } else if (cfree_cg_type_record_field(p->c, result_ty, i, &field, NULL) != 0) { - free(seen); + toy_parser_free_mem(p, seen, nfields * sizeof *seen); goto done; } if (outputs.items[i].type != field.type || seen[field_index]) { - free(seen); + toy_parser_free_mem(p, seen, nfields * sizeof *seen); toy_error(p, p->cur.loc, "asm record result output mismatch"); goto done; } seen[field_index] = 1u; record_field_indexes[i] = field_index; } - free(seen); + toy_parser_free_mem(p, seen, nfields * sizeof *seen); } else { if (outputs.count != 1 || outputs.items[0].type != result_ty) { toy_error(p, p->cur.loc, "asm result type must match single output"); @@ -485,9 +488,11 @@ int toy_parse_typed_asm_tail(ToyParser* p, CfreeCgTypeId result_ty, ok = 1; done: - free(record_field_indexes); - free(outputs.items); - free(inputs.items); - free(clobbers.items); + toy_parser_free_mem(p, record_field_indexes, + record_field_count * sizeof *record_field_indexes); + toy_parser_free_mem(p, outputs.items, outputs.cap * sizeof *outputs.items); + toy_parser_free_mem(p, inputs.items, inputs.cap * sizeof *inputs.items); + toy_parser_free_mem(p, clobbers.items, + clobbers.cap * sizeof *clobbers.items); return ok; } diff --git a/lang/toy/data.c b/lang/toy/data.c @@ -1,6 +1,5 @@ #include "internal.h" -#include <stdlib.h> #include <string.h> int toy_parse_data_array_builtin(ToyParser* p, CfreeCgTypeId elem_ty, @@ -214,17 +213,18 @@ int toy_parse_global_record_initializer(ToyParser* p, CfreeCgSym sym, uint64_t pos = 0; uint64_t total_size = cfree_cg_type_size(p->c, record_ty); uint32_t nfields = cfree_cg_type_record_nfields(p->c, record_ty); + size_t seen_size = (size_t)(nfields ? nfields : 1u) * sizeof *seen; ToyNamedType* named = toy_find_named_type_by_type(p, record_ty); int positional = named && named->kind == TOY_NAMED_TUPLE; - seen = (uint8_t*)calloc(nfields ? nfields : 1u, sizeof *seen); + seen = (uint8_t*)toy_parser_zalloc(p, nfields ? nfields : 1u, sizeof *seen, + "record initializer"); if (!seen) { - toy_error(p, p->cur.loc, "out of memory growing record initializer"); return 0; } if (p->cur.kind == TOK_IDENT) toy_parser_advance(p); if (!toy_parser_expect(p, TOK_LBRACE)) { toy_error(p, p->cur.loc, "expected record initializer"); - free(seen); + toy_parser_free_mem(p, seen, seen_size); return 0; } cfree_cg_data_begin(p->cg, sym, data_attrs); @@ -235,12 +235,12 @@ int toy_parse_global_record_initializer(ToyParser* p, CfreeCgSym sym, uint64_t field_off; if (field_index >= nfields) { toy_error(p, p->cur.loc, "too many tuple initializer elements"); - free(seen); + toy_parser_free_mem(p, seen, seen_size); return 0; } if (cfree_cg_type_record_field(p->c, record_ty, field_index, &field, &field_off) != 0) { - free(seen); + toy_parser_free_mem(p, seen, seen_size); return 0; } if (field_off > pos) { @@ -249,12 +249,12 @@ int toy_parse_global_record_initializer(ToyParser* p, CfreeCgSym sym, } if (p->cur.kind == TOK_AT) { if (!toy_parse_data_array_builtin(p, field.type, total_size, &pos)) { - free(seen); + toy_parser_free_mem(p, seen, seen_size); return 0; } } else if (p->cur.kind != TOK_NUMBER || p->cur.is_float) { toy_error(p, p->cur.loc, "expected constant tuple initializer"); - free(seen); + toy_parser_free_mem(p, seen, seen_size); return 0; } else { cfree_cg_data_int(p->cg, (uint64_t)p->cur.int_value, field.type); @@ -266,12 +266,12 @@ int toy_parse_global_record_initializer(ToyParser* p, CfreeCgSym sym, } if (!toy_parser_expect(p, TOK_RBRACE)) { toy_error(p, p->cur.loc, "expected '}' after tuple initializer"); - free(seen); + toy_parser_free_mem(p, seen, seen_size); return 0; } if (pos < total_size) cfree_cg_data_zero(p->cg, total_size - pos); cfree_cg_data_end(p->cg); - free(seen); + toy_parser_free_mem(p, seen, seen_size); return 1; } while (p->cur.kind != TOK_RBRACE && p->cur.kind != TOK_EOF) { @@ -281,36 +281,36 @@ int toy_parse_global_record_initializer(ToyParser* p, CfreeCgSym sym, uint64_t field_off; if (p->cur.kind != TOK_IDENT) { toy_error(p, p->cur.loc, "expected field name"); - free(seen); + toy_parser_free_mem(p, seen, seen_size); return 0; } field_name = toy_tok_sym(p, p->cur); toy_parser_advance(p); if (!toy_parser_expect(p, TOK_COLON)) { toy_error(p, p->cur.loc, "expected ':' in record initializer"); - free(seen); + toy_parser_free_mem(p, seen, seen_size); return 0; } if (!toy_record_field_index(p, record_ty, field_name, &field_index, &field)) { toy_error(p, p->cur.loc, "unknown record field"); - free(seen); + toy_parser_free_mem(p, seen, seen_size); return 0; } if (field_index >= nfields || seen[field_index]) { toy_error(p, p->cur.loc, "duplicate record field initializer"); - free(seen); + toy_parser_free_mem(p, seen, seen_size); return 0; } seen[field_index] = 1; if (cfree_cg_type_record_field(p->c, record_ty, field_index, NULL, &field_off) != 0) { - free(seen); + toy_parser_free_mem(p, seen, seen_size); return 0; } if (field_off < pos) { toy_error(p, p->cur.loc, "record initializer fields out of order"); - free(seen); + toy_parser_free_mem(p, seen, seen_size); return 0; } if (field_off > pos) { @@ -319,12 +319,12 @@ int toy_parse_global_record_initializer(ToyParser* p, CfreeCgSym sym, } if (p->cur.kind == TOK_AT) { if (!toy_parse_data_array_builtin(p, field.type, total_size, &pos)) { - free(seen); + toy_parser_free_mem(p, seen, seen_size); return 0; } } else if (p->cur.kind != TOK_NUMBER || p->cur.is_float) { toy_error(p, p->cur.loc, "expected constant record initializer"); - free(seen); + toy_parser_free_mem(p, seen, seen_size); return 0; } else { cfree_cg_data_int(p->cg, (uint64_t)p->cur.int_value, field.type); @@ -335,12 +335,12 @@ int toy_parse_global_record_initializer(ToyParser* p, CfreeCgSym sym, } if (!toy_parser_expect(p, TOK_RBRACE)) { toy_error(p, p->cur.loc, "expected '}' after record initializer"); - free(seen); + toy_parser_free_mem(p, seen, seen_size); return 0; } if (pos < total_size) cfree_cg_data_zero(p->cg, total_size - pos); cfree_cg_data_end(p->cg); - free(seen); + toy_parser_free_mem(p, seen, seen_size); return 1; } diff --git a/lang/toy/decls.c b/lang/toy/decls.c @@ -1,7 +1,6 @@ #include "internal.h" #include <stdio.h> -#include <stdlib.h> #include <string.h> int toy_parse_type_alias_decl(ToyParser* p) { @@ -119,8 +118,9 @@ int toy_parse_record_decl(ToyParser* p) { ok = named && toy_set_named_type_fields(p, named, field_infos, nfields); done: - free(fields); - free(field_infos); + toy_parser_free_mem(p, fields, cap_fields * sizeof *fields); + toy_parser_free_mem(p, field_infos, + cap_field_infos * sizeof *field_infos); return ok; } @@ -180,8 +180,9 @@ int toy_parse_tuple_decl(ToyParser* p) { ok = named && toy_set_named_type_fields(p, named, field_infos, nfields); done: - free(fields); - free(field_infos); + toy_parser_free_mem(p, fields, cap_fields * sizeof *fields); + toy_parser_free_mem(p, field_infos, + cap_field_infos * sizeof *field_infos); return ok; } @@ -255,8 +256,8 @@ int toy_parse_enum_decl(ToyParser* p) { ok = named && toy_set_named_type_enum_values(p, named, toy_values, nvalues); done: - free(values); - free(toy_values); + toy_parser_free_mem(p, values, cap_values * sizeof *values); + toy_parser_free_mem(p, toy_values, cap_toy_values * sizeof *toy_values); return ok; } @@ -470,11 +471,15 @@ int toy_parse_fn(ToyParser* p, int is_extern, int is_pub) { toy_error(p, p->cur.loc, "expected ';' after extern function"); return -1; } - free(param_types); - free(param_toy_types); - free(param_attrs); - free(sig_params); - free(param_names); + toy_parser_free_mem(p, param_types, + cap_param_types * sizeof *param_types); + toy_parser_free_mem(p, param_toy_types, + cap_param_toy_types * sizeof *param_toy_types); + toy_parser_free_mem(p, param_attrs, + cap_param_attrs * sizeof *param_attrs); + toy_parser_free_mem(p, sig_params, cap_sig_params * sizeof *sig_params); + toy_parser_free_mem(p, param_names, + cap_param_names * sizeof *param_names); return 1; } @@ -502,10 +507,11 @@ int toy_parse_fn(ToyParser* p, int is_extern, int is_pub) { p->nlabels = 0; p->cur_fn_ret = toy_builtin_type(p, CFREE_CG_BUILTIN_VOID); p->cur_fn_ret_toy = toy_type_from_cg(p, p->cur_fn_ret); - free(param_types); - free(param_toy_types); - free(param_attrs); - free(sig_params); - free(param_names); + toy_parser_free_mem(p, param_types, cap_param_types * sizeof *param_types); + toy_parser_free_mem(p, param_toy_types, + cap_param_toy_types * sizeof *param_toy_types); + toy_parser_free_mem(p, param_attrs, cap_param_attrs * sizeof *param_attrs); + toy_parser_free_mem(p, sig_params, cap_sig_params * sizeof *sig_params); + toy_parser_free_mem(p, param_names, cap_param_names * sizeof *param_names); return 1; } diff --git a/lang/toy/expr.c b/lang/toy/expr.c @@ -4,7 +4,6 @@ #include <stddef.h> #include <stdint.h> #include <stdio.h> -#include <stdlib.h> #include <string.h> /* Public CG API coverage goals for this frontend are tracked in diff --git a/lang/toy/internal.h b/lang/toy/internal.h @@ -219,6 +219,9 @@ CfreeCgTypeId toy_builtin_type(ToyParser* p, CfreeCgBuiltinType ty); void toy_parser_init(ToyParser* p, CfreeCompiler* c, CfreeCg* cg, const uint8_t* data, size_t len); void toy_parser_dispose(ToyParser* p); +void* toy_parser_zalloc(ToyParser* p, size_t count, size_t elem_size, + const char* what); +void toy_parser_free_mem(ToyParser* p, void* items, size_t size); int toy_parser_reserve(ToyParser* p, void** items, size_t* cap, size_t want, size_t elem_size, const char* what); void toy_parser_advance(ToyParser* p); diff --git a/lang/toy/parser.c b/lang/toy/parser.c @@ -4,7 +4,6 @@ #include <stddef.h> #include <stdint.h> #include <stdio.h> -#include <stdlib.h> #include <string.h> /* Public CG API coverage goals for this frontend are tracked in @@ -421,6 +420,7 @@ static int toy_parse_switch_initializer(ToyParser* p, CfreeCgLocal slot, CfreeCgLabel end_label; ToyNamedType* selector_enum; unsigned char* enum_seen = NULL; + size_t enum_seen_size = 0; int saw_default = 0; size_t enum_seen_count = 0; @@ -441,18 +441,18 @@ static int toy_parse_switch_initializer(ToyParser* p, CfreeCgLocal slot, if (!selector_enum || selector_enum->kind != TOY_NAMED_ENUM) selector_enum = NULL; if (selector_enum) { - enum_seen = (unsigned char*)calloc( - selector_enum->nenum_values ? selector_enum->nenum_values : 1u, - sizeof *enum_seen); - if (!enum_seen) { - toy_error(p, p->cur.loc, "out of memory growing enum switch state"); - return 0; - } + enum_seen_size = + (selector_enum->nenum_values ? selector_enum->nenum_values : 1u) * + sizeof *enum_seen; + enum_seen = (unsigned char*)toy_parser_zalloc( + p, selector_enum->nenum_values ? selector_enum->nenum_values : 1u, + sizeof *enum_seen, "enum switch state"); + if (!enum_seen) return 0; } if (!toy_parser_expect(p, TOK_LBRACE)) { toy_error(p, p->cur.loc, "expected '{' after switch selector"); - free(enum_seen); + toy_parser_free_mem(p, enum_seen, enum_seen_size); return 0; } while (p->cur.kind != TOK_RBRACE && p->cur.kind != TOK_EOF) { @@ -467,7 +467,7 @@ static int toy_parse_switch_initializer(ToyParser* p, CfreeCgLocal slot, int64_t value; size_t i; if (!toy_parse_switch_label_value(p, selector_ty, &value)) { - free(enum_seen); + toy_parser_free_mem(p, enum_seen, enum_seen_size); return 0; } if (selector_enum) { @@ -492,7 +492,7 @@ static int toy_parse_switch_initializer(ToyParser* p, CfreeCgLocal slot, } cfree_cg_label_place(p->cg, arm_label); if (!toy_parse_value_block_to_local(p, slot, result_ty, result_toy_type)) { - free(enum_seen); + toy_parser_free_mem(p, enum_seen, enum_seen_size); return 0; } cfree_cg_jump(p->cg, end_label); @@ -500,17 +500,17 @@ static int toy_parse_switch_initializer(ToyParser* p, CfreeCgLocal slot, } if (!toy_parser_expect(p, TOK_RBRACE)) { toy_error(p, p->cur.loc, "expected '}' after switch expression"); - free(enum_seen); + toy_parser_free_mem(p, enum_seen, enum_seen_size); return 0; } if (!saw_default && (!selector_enum || enum_seen_count != selector_enum->nenum_values)) { toy_error(p, p->cur.loc, "expression switch requires default"); - free(enum_seen); + toy_parser_free_mem(p, enum_seen, enum_seen_size); return 0; } cfree_cg_label_place(p->cg, end_label); - free(enum_seen); + toy_parser_free_mem(p, enum_seen, enum_seen_size); return 1; } diff --git a/lang/toy/parser_core.c b/lang/toy/parser_core.c @@ -2,13 +2,37 @@ #include <stdarg.h> #include <stdio.h> -#include <stdlib.h> #include <string.h> CfreeCgTypeId toy_builtin_type(ToyParser* p, CfreeCgBuiltinType ty) { return p->types.id[ty]; } +void* toy_parser_zalloc(ToyParser* p, size_t count, size_t elem_size, + const char* what) { + CfreeHeap* h = cfree_compiler_context(p->c)->heap; + size_t size = count * elem_size; + void* items; + if (count != 0 && size / count != elem_size) { + toy_error(p, p->cur.loc, "out of memory growing %s", what); + return NULL; + } + items = h->alloc(h, size ? size : 1u, 1); + if (!items) { + toy_error(p, p->cur.loc, "out of memory growing %s", what); + return NULL; + } + memset(items, 0, size ? size : 1u); + return items; +} + +void toy_parser_free_mem(ToyParser* p, void* items, size_t size) { + CfreeHeap* h; + if (!items) return; + h = cfree_compiler_context(p->c)->heap; + h->free(h, items, size ? size : 1u); +} + void toy_parser_init(ToyParser* p, CfreeCompiler* c, CfreeCg* cg, const uint8_t* data, size_t len) { toy_lexer_init(&p->lex, data, len); @@ -55,23 +79,35 @@ void toy_parser_init(ToyParser* p, CfreeCompiler* c, CfreeCg* cg, void toy_parser_dispose(ToyParser* p) { size_t i; - free(p->vars); + toy_parser_free_mem(p, p->vars, p->cap_vars * sizeof *p->vars); for (i = 0; i < p->nfns; ++i) { - free(p->fns[i].params); - free(p->fns[i].toy_params); + toy_parser_free_mem(p, p->fns[i].params, + p->fns[i].nparams * sizeof *p->fns[i].params); + toy_parser_free_mem(p, p->fns[i].toy_params, + p->fns[i].nparams * sizeof *p->fns[i].toy_params); } - free(p->fns); - free(p->globals); - for (i = 0; i < p->type_table.ntypes; ++i) free(p->type_table.types[i].params); - free(p->type_table.types); + toy_parser_free_mem(p, p->fns, p->cap_fns * sizeof *p->fns); + toy_parser_free_mem(p, p->globals, p->cap_globals * sizeof *p->globals); + for (i = 0; i < p->type_table.ntypes; ++i) + toy_parser_free_mem(p, p->type_table.types[i].params, + p->type_table.types[i].nparams * + sizeof *p->type_table.types[i].params); + toy_parser_free_mem(p, p->type_table.types, + p->type_table.cap_types * sizeof *p->type_table.types); for (i = 0; i < p->type_table.count; ++i) { - free(p->type_table.named[i].enum_values); - free(p->type_table.named[i].fields); + toy_parser_free_mem(p, p->type_table.named[i].enum_values, + p->type_table.named[i].cap_enum_values * + sizeof *p->type_table.named[i].enum_values); + toy_parser_free_mem(p, p->type_table.named[i].fields, + p->type_table.named[i].cap_fields * + sizeof *p->type_table.named[i].fields); } - free(p->type_table.named); - free(p->scopes); - free(p->labels); - free(p->goto_targets); + toy_parser_free_mem(p, p->type_table.named, + p->type_table.cap * sizeof *p->type_table.named); + toy_parser_free_mem(p, p->scopes, p->cap_scopes * sizeof *p->scopes); + toy_parser_free_mem(p, p->labels, p->cap_labels * sizeof *p->labels); + toy_parser_free_mem(p, p->goto_targets, + p->cap_goto_targets * sizeof *p->goto_targets); p->vars = NULL; p->fns = NULL; p->globals = NULL; @@ -94,12 +130,23 @@ void toy_parser_dispose(ToyParser* p) { int toy_parser_reserve(ToyParser* p, void** items, size_t* cap, size_t want, size_t elem_size, const char* what) { + CfreeHeap* h; size_t new_cap; + size_t old_size; + size_t new_size; void* new_items; if (want <= *cap) return 1; new_cap = *cap ? *cap * 2u : 8u; while (new_cap < want) new_cap *= 2u; - new_items = realloc(*items, new_cap * elem_size); + old_size = *cap * elem_size; + new_size = new_cap * elem_size; + if (new_cap != 0 && new_size / new_cap != elem_size) { + toy_error(p, p->cur.loc, "out of memory growing %s", what); + return 0; + } + h = cfree_compiler_context(p->c)->heap; + new_items = h->realloc(h, *items, old_size ? old_size : 1u, + new_size ? new_size : 1u, 1); if (!new_items) { toy_error(p, p->cur.loc, "out of memory growing %s", what); return 0; diff --git a/lang/toy/symbols.c b/lang/toy/symbols.c @@ -1,7 +1,5 @@ #include "internal.h" -#include <stdlib.h> - ToyVar* toy_find_var(ToyParser* p, CfreeSym name) { size_t i; for (i = p->nvars; i > 0; --i) { @@ -91,14 +89,16 @@ ToyFn* toy_add_fn_typed(ToyParser* p, CfreeSym name, CfreeCgSym sym, fn->params = NULL; fn->toy_params = NULL; if (nparams != 0) { - fn->params = (CfreeCgTypeId*)calloc(nparams, sizeof *fn->params); - fn->toy_params = (ToyTypeId*)calloc(nparams, sizeof *fn->toy_params); + fn->params = (CfreeCgTypeId*)toy_parser_zalloc( + p, nparams, sizeof *fn->params, "function parameters"); + fn->toy_params = (ToyTypeId*)toy_parser_zalloc( + p, nparams, sizeof *fn->toy_params, "function parameters"); if (!fn->params || !fn->toy_params) { - free(fn->params); - free(fn->toy_params); + toy_parser_free_mem(p, fn->params, nparams * sizeof *fn->params); + toy_parser_free_mem(p, fn->toy_params, + nparams * sizeof *fn->toy_params); fn->params = NULL; fn->toy_params = NULL; - toy_error(p, p->cur.loc, "out of memory growing function parameters"); return NULL; } } diff --git a/lang/toy/types.c b/lang/toy/types.c @@ -1,6 +1,5 @@ #include "internal.h" -#include <stdlib.h> #include <string.h> static ToyType* toy_type_slot(ToyParser* p, ToyTypeId id) { @@ -175,9 +174,11 @@ CfreeCgTypeId toy_parse_type(ToyParser* p) { nparams, variadic)); } fn_done: - free(param_types); - free(param_toy_types); - free(sig_params); + toy_parser_free_mem(p, param_types, + cap_param_types * sizeof *param_types); + toy_parser_free_mem(p, param_toy_types, + cap_param_toy_types * sizeof *param_toy_types); + toy_parser_free_mem(p, sig_params, cap_sig_params * sizeof *sig_params); return result; } if (toy_parser_match(p, TOK_LBRACKET)) { @@ -277,7 +278,7 @@ fn_done: result = toy_type_finish(p, record_ty, toy_type_from_cg(p, record_ty)); } record_done: - free(fields); + toy_parser_free_mem(p, fields, cap_fields * sizeof *fields); return result; } if (p->cur.kind == TOK_INT) { @@ -607,13 +608,15 @@ ToyTypeId toy_type_register_func(ToyParser* p, CfreeCgTypeId cg, type.nparams = nparams; type.variadic = variadic; if (nparams) { - type.params = (ToyTypeId*)calloc(nparams, sizeof *type.params); + type.params = (ToyTypeId*)toy_parser_zalloc(p, nparams, + sizeof *type.params, + "function type parameters"); if (!type.params) return TOY_TYPE_NONE; memcpy(type.params, params, nparams * sizeof *type.params); } id = toy_type_add(p, &type); if (id == TOY_TYPE_NONE || p->type_table.types[id - 1u].params != type.params) - free(type.params); + toy_parser_free_mem(p, type.params, nparams * sizeof *type.params); return id; } diff --git a/rt/include/stdio.h b/rt/include/stdio.h @@ -0,0 +1,15 @@ +/* stdio.h -- minimal freestanding formatting declarations */ +#ifndef CFREE_STDIO_H +#define CFREE_STDIO_H + +#include <stdarg.h> +#include <stddef.h> + +#define EOF (-1) + +int snprintf(char* s, size_t n, const char* fmt, ...); +int vsnprintf(char* s, size_t n, const char* fmt, va_list ap); +int sprintf(char* s, const char* fmt, ...); +int vsprintf(char* s, const char* fmt, va_list ap); + +#endif diff --git a/rt/include/stdlib.h b/rt/include/stdlib.h @@ -0,0 +1,47 @@ +/* stdlib.h -- minimal freestanding declarations */ +#ifndef CFREE_STDLIB_H +#define CFREE_STDLIB_H + +#include <stddef.h> + +#define RAND_MAX 0x7fffffff + +typedef struct { + int quot; + int rem; +} div_t; + +typedef struct { + long quot; + long rem; +} ldiv_t; + +typedef struct { + long long quot; + long long rem; +} lldiv_t; + +int atoi(const char* nptr); +long atol(const char* nptr); +long long atoll(const char* nptr); +double strtod(const char* nptr, char** endptr); +float strtof(const char* nptr, char** endptr); +long double strtold(const char* nptr, char** endptr); +long strtol(const char* nptr, char** endptr, int base); +long long strtoll(const char* nptr, char** endptr, int base); +unsigned long strtoul(const char* nptr, char** endptr, int base); +unsigned long long strtoull(const char* nptr, char** endptr, int base); + +int abs(int j); +long labs(long j); +long long llabs(long long j); +div_t div(int numer, int denom); +ldiv_t ldiv(long numer, long denom); +lldiv_t lldiv(long long numer, long long denom); + +void qsort(void* base, size_t nmemb, size_t size, + int (*compar)(const void*, const void*)); +void* bsearch(const void* key, const void* base, size_t nmemb, size_t size, + int (*compar)(const void*, const void*)); + +#endif diff --git a/rt/include/string.h b/rt/include/string.h @@ -0,0 +1,25 @@ +/* string.h -- minimal freestanding declarations */ +#ifndef CFREE_STRING_H +#define CFREE_STRING_H + +#include <stddef.h> + +void* memcpy(void* dest, const void* src, size_t n); +void* memmove(void* dest, const void* src, size_t n); +void* memset(void* s, int c, size_t n); +int memcmp(const void* s1, const void* s2, size_t n); +void* memchr(const void* s, int c, size_t n); + +char* strcpy(char* dest, const char* src); +char* strncpy(char* dest, const char* src, size_t n); +char* strcat(char* dest, const char* src); +char* strncat(char* dest, const char* src, size_t n); +int strcmp(const char* s1, const char* s2); +int strncmp(const char* s1, const char* s2, size_t n); +char* strchr(const char* s, int c); +char* strrchr(const char* s, int c); +char* strstr(const char* haystack, const char* needle); +size_t strlen(const char* s); +size_t strnlen(const char* s, size_t maxlen); + +#endif diff --git a/scripts/stage2_link.sh b/scripts/stage2_link.sh @@ -2,8 +2,7 @@ # Stage-2 standalone link probe. # # 1. Compile every src/**/*.c with cfree-stage1. -# 2. Compile every driver/*.c with cfree-stage1; fall back to clang for the -# files cfree still can't ingest (env.c, ld.c, per doc/STAGE2.md A2). +# 2. Compile every driver/*.c with cfree-stage1. # 3. Link all the resulting objects with `cfree ld` against libSystem.B.tbd. # # Runs out-of-tree under build/stage2-probe/ so the Makefile's build/ tree @@ -29,19 +28,13 @@ DRV_OUT="$OUT/driver" LOG="$OUT/log" mkdir -p "$LIB_OUT" "$LANG_C_OUT" "$LANG_WASM_OUT" "$LANG_TOY_OUT" "$DRV_OUT" "$LOG" -CFREE_FLAGS="-isystem $ROOT/rt/include -isystem $ROOT/rt/include/libc -Iinclude -Isrc" -LANG_C_FLAGS="-isystem $ROOT/rt/include -isystem $ROOT/rt/include/libc -Iinclude -Ilang/c" -LANG_WASM_FLAGS="-isystem $ROOT/rt/include -isystem $ROOT/rt/include/libc -Iinclude -Ilang/wasm" -LANG_TOY_FLAGS="-isystem $ROOT/rt/include -isystem $ROOT/rt/include/libc -Iinclude" -DRIVER_FLAGS="-isystem $ROOT/rt/include -isystem $ROOT/rt/include/libc -Iinclude -I." - -# Driver files cfree still cannot parse (doc/STAGE2.md A2). Compile with -# clang in stage-2 flag style so the resulting objects are still arm64 -# Mach-O at a matching SDK level. -CLANG_FALLBACK=("env.c") +CFREE_FLAGS="--support-dir $ROOT -Iinclude -Isrc" +LANG_C_FLAGS="--support-dir $ROOT -Iinclude -Ilang/c" +LANG_WASM_FLAGS="--support-dir $ROOT -Iinclude -Ilang/wasm" +LANG_TOY_FLAGS="--support-dir $ROOT -Iinclude" +DRIVER_FLAGS="--support-dir $ROOT -Iinclude -I." cfree_objs=() -clang_objs=() fail_src=() fail_lang=() fail_driver=() @@ -58,7 +51,7 @@ compile_with_cfree() { echo "=== compiling src/ with cfree ===" while IFS= read -r src; do rel="${src#src/}" - obj="$LIB_OUT/${rel%.c}.o" + obj="$LIB_OUT/${rel%.*}.o" if compile_with_cfree "$src" "$obj" "$CFREE_FLAGS"; then cfree_objs+=("$obj") printf ' ok %s\n' "$src" @@ -66,7 +59,7 @@ while IFS= read -r src; do fail_src+=("$src") head -1 "$LOG/$(basename "$obj").log" | sed "s|^| FAIL $src: |" fi -done < <(find src -name '*.c' | sort) +done < <(find src \( -name '*.c' -o -name '*.S' \) | sort) echo echo "=== compiling lang/c with cfree ===" @@ -115,26 +108,9 @@ echo "=== compiling driver/ ===" for src in $(ls driver/*.c | sort); do base="$(basename "$src")" obj="$DRV_OUT/${base%.c}.o" - use_clang=0 - for skip in "${CLANG_FALLBACK[@]}"; do - if [ "$base" = "$skip" ]; then use_clang=1; fi - done - if [ "$use_clang" = 1 ]; then - # -fno-{,asynchronous-}unwind-tables: cfree's macho_read doesn't yet - # ingest the section-relative UNSIGNED relocs that clang emits in - # __LD,__compact_unwind. Suppress the section entirely. - if clang -arch arm64 -isysroot "$SDK" -Iinclude -I. \ - -fno-unwind-tables -fno-asynchronous-unwind-tables \ - -c "$src" -o "$obj" >"$LOG/$base.log" 2>&1; then - clang_objs+=("$obj") - printf ' CLG %s\n' "$src" - else - fail_driver+=("$src (clang)") - head -1 "$LOG/$base.log" | sed "s|^| FAIL $src: |" - fi - continue - fi - if compile_with_cfree "$src" "$obj" "$DRIVER_FLAGS"; then + extra_flags="" + if [ "$base" = "env.c" ]; then extra_flags="--sysroot $SDK -lc"; fi + if compile_with_cfree "$src" "$obj" "$DRIVER_FLAGS $extra_flags"; then cfree_objs+=("$obj") printf ' ok %s\n' "$src" else @@ -146,7 +122,6 @@ done echo echo "=== compile summary ===" echo " cfree objects: ${#cfree_objs[@]}" -echo " clang objects: ${#clang_objs[@]}" echo " src failures: ${#fail_src[@]}" echo " lang failures: ${#fail_lang[@]}" echo " driver failures: ${#fail_driver[@]}" @@ -170,7 +145,7 @@ fi set -x "$BIN" ld -o "$BIN_OUT" -pie \ - "${cfree_objs[@]}" "${clang_objs[@]}" \ + "${cfree_objs[@]}" \ -L "$LIBSYS_DIR" -lSystem status=$? set +x diff --git a/src/arch/aa64/arch.c b/src/arch/aa64/arch.c @@ -81,8 +81,14 @@ static int aa64_apply_label_fixup(Compiler* c, const ArchLabelFixup* fx) { static const CfreePredefinedMacro aa64_predefined_macros[] = { {"__aarch64__", "1"}, + {"__AARCH64EL__", "1"}, + {"__arm64", "1"}, {"__arm64__", "1"}, {"__LP64__", "1"}, + {"__ORDER_LITTLE_ENDIAN__", "1234"}, + {"__ORDER_BIG_ENDIAN__", "4321"}, + {"__BYTE_ORDER__", "__ORDER_LITTLE_ENDIAN__"}, + {"__LITTLE_ENDIAN__", "1"}, }; const ArchImpl arch_impl_aa64 = { diff --git a/src/arch/aa64/asm.c b/src/arch/aa64/asm.c @@ -119,7 +119,7 @@ typedef struct AA64Reg { u8 is64; u8 is_sp; /* 1 if the spelling was "sp" / "wsp" */ u8 is_fp; /* 1 for SIMD/FP register spellings accepted in FP forms */ - u8 pad; + u8 fp_bytes; /* 8 for Dn, 16 for Qn */ } AA64Reg; static int parse_reg_from_ident(AsmDriver* d, Sym ident, AA64Reg* out) { @@ -202,10 +202,13 @@ static int parse_reg_from_ident(AsmDriver* d, Sym ident, AA64Reg* out) { return 0; } -static int parse_fp_d_reg_from_ident(AsmDriver* d, Sym ident, AA64Reg* out) { +static int parse_fp_pair_reg_from_ident(AsmDriver* d, Sym ident, + AA64Reg* out) { size_t n = 0; const char* p = pool_str(asm_driver_pool(d), ident, &n); - if (!p || n < 2 || (p[0] != 'd' && p[0] != 'D')) return 0; + if (!p || n < 2 || + (p[0] != 'd' && p[0] != 'D' && p[0] != 'q' && p[0] != 'Q')) + return 0; u32 r = 0; for (size_t i = 1; i < n; ++i) { char c = p[i]; @@ -217,6 +220,7 @@ static int parse_fp_d_reg_from_ident(AsmDriver* d, Sym ident, AA64Reg* out) { out->is64 = 1; out->is_sp = 0; out->is_fp = 1; + out->fp_bytes = (p[0] == 'q' || p[0] == 'Q') ? 16u : 8u; return 1; } @@ -235,7 +239,7 @@ static AA64Reg parse_ldstp_reg(AsmDriver* d) { memset(&r, 0, sizeof r); if (t.kind != ASM_TOK_IDENT || (!parse_reg_from_ident(d, t.v.ident, &r) && - !parse_fp_d_reg_from_ident(d, t.v.ident, &r))) { + !parse_fp_pair_reg_from_ident(d, t.v.ident, &r))) { asm_driver_panic(d, "asm: expected register"); } return r; @@ -894,16 +898,18 @@ static void p_ldp_stp(AsmDriver* d, int is_load) { expect_comma(d, "ldp/stp"); reject_sp_reg(d, rt, "ldp/stp"); reject_sp_reg(d, rt2, "ldp/stp"); - if (rt.is64 != rt2.is64 || rt.is_fp != rt2.is_fp) + if (rt.is64 != rt2.is64 || rt.is_fp != rt2.is_fp || + rt.fp_bytes != rt2.fp_bytes) asm_driver_panic(d, "asm: ldp/stp: width mismatch"); AA64Mem m = parse_mem(d); - u32 scale = rt.is64 ? 8u : 4u; + u32 scale = rt.is_fp ? (u32)rt.fp_bytes : (rt.is64 ? 8u : 4u); if ((i64)((u64)m.imm % scale) != 0) asm_driver_panic(d, "asm: ldp/stp: imm not scale-aligned"); i64 imm7 = m.imm / (i64)scale; if (imm7 < -64 || imm7 > 63) asm_driver_panic(d, "asm: ldp/stp: imm7 out of range"); - AA64LdStPPre f = {.opc = rt.is_fp ? 1u : (rt.is64 ? 2u : 0u), + AA64LdStPPre f = {.opc = rt.is_fp ? (rt.fp_bytes == 16u ? 2u : 1u) + : (rt.is64 ? 2u : 0u), .V = rt.is_fp ? 1u : 0u, .L = is_load ? 1u : 0u, .imm7 = (u32)imm7 & 0x7fu, diff --git a/src/arch/aa64/isa.c b/src/arch/aa64/isa.c @@ -156,6 +156,10 @@ const AA64InsnDesc aa64_insn_table[] = { {0, 0}}, {"stp", 0x6D800000u, 0xFFC00000u, AA64_FMT_LDSTP_PRE, 0, {0, 0}}, /* D */ {"ldp", 0x6DC00000u, 0xFFC00000u, AA64_FMT_LDSTP_PRE, 0, {0, 0}}, + {"stp", 0xAD800000u, 0xFFC00000u, AA64_FMT_LDSTP_PRE, AA64_ASMFL_SF1, + {0, 0}}, /* Q */ + {"ldp", 0xADC00000u, 0xFFC00000u, AA64_FMT_LDSTP_PRE, AA64_ASMFL_SF1, + {0, 0}}, /* ----- Load/store pair, signed-offset ----- */ {"stp", 0xA9000000u, 0xFFC00000u, AA64_FMT_LDSTP_SOFF, AA64_ASMFL_SF1, @@ -164,6 +168,10 @@ const AA64InsnDesc aa64_insn_table[] = { {0, 0}}, {"stp", 0x6D000000u, 0xFFC00000u, AA64_FMT_LDSTP_SOFF, 0, {0, 0}}, /* D */ {"ldp", 0x6D400000u, 0xFFC00000u, AA64_FMT_LDSTP_SOFF, 0, {0, 0}}, + {"stp", 0xAD000000u, 0xFFC00000u, AA64_FMT_LDSTP_SOFF, AA64_ASMFL_SF1, + {0, 0}}, /* Q */ + {"ldp", 0xAD400000u, 0xFFC00000u, AA64_FMT_LDSTP_SOFF, AA64_ASMFL_SF1, + {0, 0}}, /* ----- Unconditional branch (immediate) ----- */ {"b", 0x14000000u, 0xFC000000u, AA64_FMT_BR_IMM, 0, {0, 0}}, diff --git a/src/arch/aa64/isa.h b/src/arch/aa64/isa.h @@ -702,7 +702,7 @@ static inline u32 aa64_str64_uimm12(u32 Rt, u32 Rn, u32 imm12_scaled) { * sign-extended in i32. * ==================================================================== */ -#define AA64_LDSTP_PRE_FAMILY_MATCH 0xA9800000u +#define AA64_LDSTP_PRE_FAMILY_MATCH 0x29800000u #define AA64_LDSTP_PRE_FAMILY_MASK 0x7FC00000u /* bits 30:23 */ typedef struct AA64LdStPPre { diff --git a/src/arch/aa64/ops.c b/src/arch/aa64/ops.c @@ -386,6 +386,20 @@ static u32 agg_addr_reg(CGTarget* t, Operand op, u32 scratch) { "aarch64 agg: address kind %d unsupported", (int)op.kind); } +static void aa_emit_load_at(MCEmitter* mc, u32 size, u32 rt, u32 rn, u32 off) { + if (off <= 255u) + aa64_emit32(mc, aa64_ldur(size, rt, rn, (i32)off)); + else + aa64_emit32(mc, aa64_ldr_uimm(size, rt, rn, off)); +} + +static void aa_emit_store_at(MCEmitter* mc, u32 size, u32 rt, u32 rn, u32 off) { + if (off <= 255u) + aa64_emit32(mc, aa64_stur(size, rt, rn, (i32)off)); + else + aa64_emit32(mc, aa64_str_uimm(size, rt, rn, off)); +} + static void aa_copy_bytes(CGTarget* t, Operand dst_addr, Operand src_addr, AggregateAccess agg) { MCEmitter* mc = t->mc; @@ -395,23 +409,23 @@ static void aa_copy_bytes(CGTarget* t, Operand dst_addr, Operand src_addr, u32 nbytes = agg.size; u32 i = 0; while (i + 8 <= nbytes) { - aa64_emit32(mc, aa64_ldur(3, AA_TMP2, sr, (i32)i)); - aa64_emit32(mc, aa64_stur(3, AA_TMP2, dr, (i32)i)); + aa_emit_load_at(mc, 3, AA_TMP2, sr, i); + aa_emit_store_at(mc, 3, AA_TMP2, dr, i); i += 8; } while (i + 4 <= nbytes) { - aa64_emit32(mc, aa64_ldur(2, AA_TMP2, sr, (i32)i)); - aa64_emit32(mc, aa64_stur(2, AA_TMP2, dr, (i32)i)); + aa_emit_load_at(mc, 2, AA_TMP2, sr, i); + aa_emit_store_at(mc, 2, AA_TMP2, dr, i); i += 4; } while (i + 2 <= nbytes) { - aa64_emit32(mc, aa64_ldur(1, AA_TMP2, sr, (i32)i)); - aa64_emit32(mc, aa64_stur(1, AA_TMP2, dr, (i32)i)); + aa_emit_load_at(mc, 1, AA_TMP2, sr, i); + aa_emit_store_at(mc, 1, AA_TMP2, dr, i); i += 2; } while (i < nbytes) { - aa64_emit32(mc, aa64_ldur(0, AA_TMP2, sr, (i32)i)); - aa64_emit32(mc, aa64_stur(0, AA_TMP2, dr, (i32)i)); + aa_emit_load_at(mc, 0, AA_TMP2, sr, i); + aa_emit_store_at(mc, 0, AA_TMP2, dr, i); i += 1; } } @@ -433,19 +447,19 @@ static void aa_set_bytes(CGTarget* t, Operand dst_addr, Operand byte_value, if (byte == 0) { u32 i = 0; while (i + 8 <= nbytes) { - aa64_emit32(mc, aa64_stur(3, 31, dr, (i32)i)); + aa_emit_store_at(mc, 3, 31, dr, i); i += 8; } while (i + 4 <= nbytes) { - aa64_emit32(mc, aa64_stur(2, 31, dr, (i32)i)); + aa_emit_store_at(mc, 2, 31, dr, i); i += 4; } while (i + 2 <= nbytes) { - aa64_emit32(mc, aa64_stur(1, 31, dr, (i32)i)); + aa_emit_store_at(mc, 1, 31, dr, i); i += 2; } while (i < nbytes) { - aa64_emit32(mc, aa64_stur(0, 31, dr, (i32)i)); + aa_emit_store_at(mc, 0, 31, dr, i); i += 1; } return; @@ -459,19 +473,19 @@ static void aa_set_bytes(CGTarget* t, Operand dst_addr, Operand byte_value, u32 i = 0; while (i + 8 <= nbytes) { - aa64_emit32(mc, aa64_stur(3, AA_TMP1, dr, (i32)i)); + aa_emit_store_at(mc, 3, AA_TMP1, dr, i); i += 8; } while (i + 4 <= nbytes) { - aa64_emit32(mc, aa64_stur(2, AA_TMP1, dr, (i32)i)); + aa_emit_store_at(mc, 2, AA_TMP1, dr, i); i += 4; } while (i + 2 <= nbytes) { - aa64_emit32(mc, aa64_stur(1, AA_TMP1, dr, (i32)i)); + aa_emit_store_at(mc, 1, AA_TMP1, dr, i); i += 2; } while (i < nbytes) { - aa64_emit32(mc, aa64_stur(0, AA_TMP1, dr, (i32)i)); + aa_emit_store_at(mc, 0, AA_TMP1, dr, i); i += 1; } } @@ -2092,46 +2106,46 @@ static void aa_intrinsic(CGTarget* t, IntrinKind kind, Operand* dsts, u32 nd, if (kind == INTRIN_MEMCPY) { u32 i = 0; while (i + 8 <= n) { - aa64_emit32(mc, aa64_ldur(3, AA_TMP2, sr, (i32)i)); - aa64_emit32(mc, aa64_stur(3, AA_TMP2, dr, (i32)i)); + aa_emit_load_at(mc, 3, AA_TMP2, sr, i); + aa_emit_store_at(mc, 3, AA_TMP2, dr, i); i += 8; } while (i + 4 <= n) { - aa64_emit32(mc, aa64_ldur(2, AA_TMP2, sr, (i32)i)); - aa64_emit32(mc, aa64_stur(2, AA_TMP2, dr, (i32)i)); + aa_emit_load_at(mc, 2, AA_TMP2, sr, i); + aa_emit_store_at(mc, 2, AA_TMP2, dr, i); i += 4; } while (i + 2 <= n) { - aa64_emit32(mc, aa64_ldur(1, AA_TMP2, sr, (i32)i)); - aa64_emit32(mc, aa64_stur(1, AA_TMP2, dr, (i32)i)); + aa_emit_load_at(mc, 1, AA_TMP2, sr, i); + aa_emit_store_at(mc, 1, AA_TMP2, dr, i); i += 2; } while (i < n) { - aa64_emit32(mc, aa64_ldur(0, AA_TMP2, sr, (i32)i)); - aa64_emit32(mc, aa64_stur(0, AA_TMP2, dr, (i32)i)); + aa_emit_load_at(mc, 0, AA_TMP2, sr, i); + aa_emit_store_at(mc, 0, AA_TMP2, dr, i); i += 1; } } else { u32 i = n; while (i >= 8) { i -= 8; - aa64_emit32(mc, aa64_ldur(3, AA_TMP2, sr, (i32)i)); - aa64_emit32(mc, aa64_stur(3, AA_TMP2, dr, (i32)i)); + aa_emit_load_at(mc, 3, AA_TMP2, sr, i); + aa_emit_store_at(mc, 3, AA_TMP2, dr, i); } while (i >= 4) { i -= 4; - aa64_emit32(mc, aa64_ldur(2, AA_TMP2, sr, (i32)i)); - aa64_emit32(mc, aa64_stur(2, AA_TMP2, dr, (i32)i)); + aa_emit_load_at(mc, 2, AA_TMP2, sr, i); + aa_emit_store_at(mc, 2, AA_TMP2, dr, i); } while (i >= 2) { i -= 2; - aa64_emit32(mc, aa64_ldur(1, AA_TMP2, sr, (i32)i)); - aa64_emit32(mc, aa64_stur(1, AA_TMP2, dr, (i32)i)); + aa_emit_load_at(mc, 1, AA_TMP2, sr, i); + aa_emit_store_at(mc, 1, AA_TMP2, dr, i); } while (i >= 1) { i -= 1; - aa64_emit32(mc, aa64_ldur(0, AA_TMP2, sr, (i32)i)); - aa64_emit32(mc, aa64_stur(0, AA_TMP2, dr, (i32)i)); + aa_emit_load_at(mc, 0, AA_TMP2, sr, i); + aa_emit_store_at(mc, 0, AA_TMP2, dr, i); } } return; @@ -2170,19 +2184,19 @@ static void aa_intrinsic(CGTarget* t, IntrinKind kind, Operand* dsts, u32 nd, } u32 i = 0; while (i + 8 <= n) { - aa64_emit32(mc, aa64_stur(3, src_reg, dr, (i32)i)); + aa_emit_store_at(mc, 3, src_reg, dr, i); i += 8; } while (i + 4 <= n) { - aa64_emit32(mc, aa64_stur(2, src_reg, dr, (i32)i)); + aa_emit_store_at(mc, 2, src_reg, dr, i); i += 4; } while (i + 2 <= n) { - aa64_emit32(mc, aa64_stur(1, src_reg, dr, (i32)i)); + aa_emit_store_at(mc, 1, src_reg, dr, i); i += 2; } while (i < n) { - aa64_emit32(mc, aa64_stur(0, src_reg, dr, (i32)i)); + aa_emit_store_at(mc, 0, src_reg, dr, i); i += 1; } return; diff --git a/src/arch/rv64/arch.c b/src/arch/rv64/arch.c @@ -66,6 +66,10 @@ static const CfreePredefinedMacro rv64_predefined_macros[] = { {"__riscv_xlen", "64"}, {"__riscv_float_abi_double", "1"}, {"__LP64__", "1"}, + {"__ORDER_LITTLE_ENDIAN__", "1234"}, + {"__ORDER_BIG_ENDIAN__", "4321"}, + {"__BYTE_ORDER__", "__ORDER_LITTLE_ENDIAN__"}, + {"__LITTLE_ENDIAN__", "1"}, }; const ArchImpl arch_impl_rv64 = { diff --git a/src/arch/x64/arch.c b/src/arch/x64/arch.c @@ -45,9 +45,15 @@ static int x64_apply_label_fixup(Compiler* c, const ArchLabelFixup* fx) { } static const CfreePredefinedMacro x64_predefined_macros[] = { + {"__x86_64", "1"}, {"__x86_64__", "1"}, + {"__amd64", "1"}, {"__amd64__", "1"}, {"__LP64__", "1"}, + {"__ORDER_LITTLE_ENDIAN__", "1234"}, + {"__ORDER_BIG_ENDIAN__", "4321"}, + {"__BYTE_ORDER__", "__ORDER_LITTLE_ENDIAN__"}, + {"__LITTLE_ENDIAN__", "1"}, }; const ArchImpl arch_impl_x64 = { diff --git a/src/cg/type.c b/src/cg/type.c @@ -404,6 +404,7 @@ static CgTypeField* copy_cg_fields(Compiler* c, const CfreeCgField* src, dst[i].name = src[i].name; dst[i].type = src[i].type; dst[i].align_override = src[i].align_override; + dst[i].max_align = src[i].max_align; dst[i].flags = src[i].flags; dst[i].bit_width = src[i].bit_width; dst[i].bit_signed = src[i].bit_signed != 0; @@ -422,6 +423,7 @@ static int cg_type_layout_record(Compiler* c, CgType* cg) { u64 fsize = cg_type_size(c, f->type); u32 falign = cg_type_align(c, f->type); if (!falign) return 0; + if (f->max_align && falign > f->max_align) falign = f->max_align; if (f->align_override == 1u) { falign = 1; } else if (f->align_override > falign) { @@ -449,6 +451,7 @@ static int cg_type_layout_record(Compiler* c, CgType* cg) { u64 fsize = cg_type_size(c, f->type); u32 falign = cg_type_align(c, f->type); if (!falign) return 0; + if (f->max_align && falign > f->max_align) falign = f->max_align; if (f->align_override == 1u) { falign = 1; } else if (f->align_override > falign) { @@ -879,6 +882,7 @@ CfreeStatus cfree_cg_type_record_field(CfreeCompiler* c, CfreeCgTypeId id, out->name = f->name; out->type = f->type; out->align_override = f->align_override; + out->max_align = f->max_align; out->flags = f->flags; out->bit_width = f->bit_width; out->bit_offset = f->bit_offset; diff --git a/src/cg/type.h b/src/cg/type.h @@ -10,6 +10,7 @@ typedef struct CgTypeField { CfreeCgTypeId type; u64 offset; u32 align_override; + u32 max_align; u32 flags; u16 bit_width; u16 bit_offset; diff --git a/src/jit/tlv_thunk_aarch64.S b/src/jit/tlv_thunk_aarch64.S @@ -13,16 +13,12 @@ #if defined(__aarch64__) -#if defined(__APPLE__) -#define CFREE_TLV_THUNK_SYM _cfree_jit_tlv_thunk -#else -#define CFREE_TLV_THUNK_SYM cfree_jit_tlv_thunk -#endif - .text .p2align 2 - .globl CFREE_TLV_THUNK_SYM -CFREE_TLV_THUNK_SYM: + .globl _cfree_jit_tlv_thunk + .globl cfree_jit_tlv_thunk +_cfree_jit_tlv_thunk: +cfree_jit_tlv_thunk: /* Frame layout (544 bytes, 16-byte aligned): * sp+ 0 .. sp+127 : x1-x16 (eight stp pairs) * sp+128 : x17 diff --git a/test/asm/encode/aa64_stp_ldp_q.expected.hex b/test/asm/encode/aa64_stp_ldp_q.expected.hex @@ -0,0 +1 @@ +e00705ade00745ad00008052c0035fd6 diff --git a/test/asm/encode/aa64_stp_ldp_q.s b/test/asm/encode/aa64_stp_ldp_q.s @@ -0,0 +1,7 @@ +.text +.globl test_main +test_main: + stp q0, q1, [sp, #160] + ldp q0, q1, [sp, #160] + mov w0, #0 + ret diff --git a/test/parse/cases/attr_asm_label_decl.c b/test/parse/cases/attr_asm_label_decl.c @@ -0,0 +1,6 @@ +extern int cfree_darwin_open(const char*, int, ...) __asm("_open"); +extern int cfree_darwin_close(int) __asm__("_close"); + +int test_main(void) { + return 42; +} diff --git a/test/parse/cases/attr_asm_label_decl.expected b/test/parse/cases/attr_asm_label_decl.expected @@ -0,0 +1 @@ +42 diff --git a/test/parse/cases/builtin_clear_cache_01.c b/test/parse/cases/builtin_clear_cache_01.c @@ -0,0 +1,5 @@ +int test_main(void) { + char buf[16]; + __builtin___clear_cache(buf, buf + sizeof buf); + return 42; +} diff --git a/test/parse/cases/builtin_clear_cache_01.expected b/test/parse/cases/builtin_clear_cache_01.expected @@ -0,0 +1 @@ +42 diff --git a/test/parse/cases/gnu_inline_alias_01.c b/test/parse/cases/gnu_inline_alias_01.c @@ -0,0 +1,11 @@ +static __inline__ int f1(void) { + return 20; +} + +static __inline int f2(void) { + return 22; +} + +int test_main(void) { + return f1() + f2(); +} diff --git a/test/parse/cases/gnu_inline_alias_01.expected b/test/parse/cases/gnu_inline_alias_01.expected @@ -0,0 +1 @@ +42 diff --git a/test/parse/cases/gnu_thread_storage_01.c b/test/parse/cases/gnu_thread_storage_01.c @@ -0,0 +1,6 @@ +static __thread int tls_value; + +int test_main(void) { + tls_value = 42; + return tls_value; +} diff --git a/test/parse/cases/gnu_thread_storage_01.expected b/test/parse/cases/gnu_thread_storage_01.expected @@ -0,0 +1 @@ +42 diff --git a/test/parse/cases/inline_external_01_local.c b/test/parse/cases/inline_external_01_local.c @@ -0,0 +1,9 @@ +inline int external_inline_add1(int x) { + return x + 1; +} + +int external_inline_add1(int); + +int test_main(void) { + return external_inline_add1(41); +} diff --git a/test/parse/cases/inline_external_01_local.expected b/test/parse/cases/inline_external_01_local.expected @@ -0,0 +1 @@ +42 diff --git a/test/parse/cases/large_struct_assign_01.c b/test/parse/cases/large_struct_assign_01.c @@ -0,0 +1,11 @@ +struct Big { + long v[80]; +}; + +int test_main(void) { + struct Big a = {0}; + struct Big b = {0}; + a.v[70] = 7; + b = a; + return (int)(b.v[0] + b.v[70] + b.v[79]); +} diff --git a/test/parse/cases/large_struct_assign_01.expected b/test/parse/cases/large_struct_assign_01.expected @@ -0,0 +1 @@ +7 diff --git a/test/parse/cases/pragma_pack_01_struct_size.c b/test/parse/cases/pragma_pack_01_struct_size.c @@ -0,0 +1,12 @@ +#pragma pack(push, 4) +struct S { + int a; + unsigned long long b; +}; +#pragma pack(pop) + +_Static_assert(sizeof(struct S) == 12, "pack4 size"); + +int test_main(void) { + return 42; +} diff --git a/test/parse/cases/pragma_pack_01_struct_size.expected b/test/parse/cases/pragma_pack_01_struct_size.expected @@ -0,0 +1 @@ +42 diff --git a/test/pp/cases/8e_if_defined_from_macro_expansion.c b/test/pp/cases/8e_if_defined_from_macro_expansion.c @@ -0,0 +1,6 @@ +#define COMPAT() defined(SWIFT_CLASS_EXTRA) && (!defined(SWIFT_EPOCH) || (SWIFT_EPOCH < 1)) +#if !COMPAT() +ok +#else +bad +#endif diff --git a/test/pp/cases/8e_if_defined_from_macro_expansion.expected b/test/pp/cases/8e_if_defined_from_macro_expansion.expected @@ -0,0 +1 @@ +ok