kit

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

commit f136952600e679611fe15d4b1b35fa01c05ad0ef
parent 7cdfa2286fcf7310e20656c9873ad62f59661971
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed,  3 Jun 2026 11:13:57 -0700

Introduce target feature model

Diffstat:
Mdriver/cmd/as.c | 26+++++++++++++++++++++-----
Mdriver/cmd/cc.c | 51++++++++++++++++++++++++++++++++++++++++++++-------
Mdriver/cmd/compile.c | 32+++++++++++++++++++++++---------
Mdriver/cmd/cpp.c | 26+++++++++++++++++++++-----
Mdriver/cmd/dbg.c | 25+++++++++++++++++--------
Mdriver/cmd/disas.c | 25+++++++++++++++++++++++--
Mdriver/cmd/emu.c | 19++++++++++++++-----
Mdriver/cmd/ld.c | 18+++++++++++++-----
Mdriver/cmd/mc.c | 25+++++++++++++++++++++----
Mdriver/cmd/nm.c | 4++--
Mdriver/cmd/objdump.c | 14++++++++++----
Mdriver/cmd/run.c | 24++++++++++++++++++++++--
Mdriver/driver.h | 33+++++++++++++++++++++++++++++----
Mdriver/env.h | 14++++++++------
Mdriver/env/common.c | 2+-
Mdriver/env/env_posix.h | 4++--
Mdriver/env/freebsd.c | 4++--
Mdriver/env/linux.c | 4++--
Mdriver/env/macos.c | 4++--
Mdriver/env/posix.c | 4++--
Mdriver/env/windows.c | 6+++---
Mdriver/lib/hosted.c | 13++++++++++---
Mdriver/lib/hosted.h | 2+-
Mdriver/lib/runtime.c | 22+++++++++++++++-------
Mdriver/lib/runtime.h | 4++--
Mdriver/lib/target.c | 205+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Minclude/kit/core.h | 30++++++++++++++++++++++++++----
Minclude/kit/disasm.h | 2+-
Minclude/kit/emu.h | 2+-
Minclude/kit/object.h | 4++--
Mlang/c/abi/c_abi.c | 6+++---
Mlang/c/parse/parse_expr.c | 4++--
Mlang/c/type/type.c | 2+-
Mlang/cpp/cpp_support.h | 2+-
Mlang/cpp/pp/pp.c | 2+-
Mlang/toy/internal.h | 2+-
Mlang/toy/parser_core.c | 4++--
Msrc/api/core.c | 140++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/api/disasm.c | 31++++++++++++++++++++++++++-----
Msrc/api/object_detect.c | 15++++++++-------
Msrc/api/object_file.c | 51++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/arch/aa64/arch.c | 22++++++++++++++++++++++
Msrc/arch/arch.h | 14++++++++++++++
Msrc/arch/registry.c | 28++++++++++++++++++++++++++++
Msrc/arch/rv64/arch.c | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/arch/wasm/arch.c | 48++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/arch/x64/arch.c | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/cg/session.c | 2+-
Msrc/core/core.c | 17+++++++++++++++--
Msrc/core/core.h | 12++++++++++--
Msrc/emu/emu.c | 6+++---
Msrc/emu/emu.h | 4++--
Msrc/link/link_jit.c | 4++--
Msrc/obj/elf/emu_load.c | 2+-
Msrc/obj/format.h | 2+-
Mtest/api/abi_classify_test.c | 2+-
Mtest/api/cg_fp_cmp_test.c | 5+++--
Mtest/api/cg_switch_test.c | 2+-
Mtest/api/cg_type_test.c | 2+-
Atest/api/target_test.c | 129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/arch/aa64_isa_test.c | 2+-
Mtest/arch/aa64_sweep_gen.c | 3++-
Mtest/arch/inline_public_test.h | 24+++++++++++++++++++++---
Mtest/arch/rv64_decode_test.c | 2+-
Mtest/asm/harness/asm_runner.c | 61+++++++++++++++++++++++++++++++++++++++++++++++++------------
Mtest/cg/ir_recorder_test.c | 2+-
Mtest/cg/native_direct_target_test.c | 2+-
Mtest/cg/strength_reduce_test.c | 20++++++++++++++------
Mtest/coff/kit-roundtrip-coff.c | 159+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mtest/coff/pe-dso-forwarder.c | 29++++++++++++++++++++++-------
Mtest/coff/pe-import-mingw.c | 31+++++++++++++++++++++++--------
Mtest/coff/pe-import-smoke.c | 31+++++++++++++++++++++++--------
Mtest/coff/pe-mixed-archive.c | 33++++++++++++++++++++++++---------
Mtest/compile/run.sh | 16+++++++++-------
Mtest/debug/cfi_unit.c | 2+-
Mtest/debug/roundtrip_unit.c | 4++--
Mtest/dwarf/dwarf_test.c | 2+-
Mtest/elf/kit-roundtrip.c | 2+-
Mtest/elf/run.sh | 4++--
Mtest/elf/unit/align_4k.c | 14++++++++++++--
Mtest/elf/unit/groupiter.c | 14++++++++++++--
Mtest/elf/unit/mutate.c | 14++++++++++++--
Mtest/elf/unit/smoke.c | 16+++++++++++++---
Mtest/elf/unit/x64_disasm_annotations.c | 20+++++++++++++++-----
Mtest/emu/rv64_interp_smoke_test.c | 4++--
Mtest/emu/rv64_smoke_test.c | 4++--
Mtest/emu/rv64_vm_unit_test.c | 9+++++----
Mtest/interp/interp_smoke_test.c | 2+-
Mtest/lib/kit_corpus.sh | 26++++++++++++++++++++++++++
Mtest/lib/kit_test_target.h | 4++--
Mtest/lib/kit_unit.h | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mtest/link/harness/jit_runner.c | 36+++++++++++++++++++++++++-----------
Mtest/link/harness/link_exe_runner.c | 28+++++++++++++++++++++-------
Mtest/link/rv64_jit_test.c | 5+++--
Mtest/macho/kit-roundtrip-macho.c | 2+-
Mtest/opt/cg_ir_lower_test.c | 2+-
Mtest/opt/tiny_inline_test.c | 2+-
Mtest/parse/harness/parse_runner.c | 37++++++++++++++++++++++++++++++++-----
Mtest/parse/run.sh | 2++
Mtest/wasm/harness/wasm_tool.c | 15+++++++++++++--
100 files changed, 1801 insertions(+), 368 deletions(-)

diff --git a/driver/cmd/as.c b/driver/cmd/as.c @@ -18,7 +18,7 @@ typedef struct AsOptions { int debug_info; /* -g */ const char* output_path; /* -o */ const char* source; /* single positional input */ - KitTarget target; /* -target TRIPLE; host default */ + KitTargetSpec target; /* -target TRIPLE; host default */ } AsOptions; static void as_usage(void) { @@ -71,7 +71,7 @@ void driver_help_as(void) { /* Returns 0 on success; non-zero on bad args (already reported). */ static int as_parse(int argc, char** argv, AsOptions* o, DriverEnv* env, - DriverCflags* cf) { + DriverCflags* cf, DriverTargetFeatures* tf) { int i; o->target = driver_host_target(); @@ -111,6 +111,13 @@ static int as_parse(int argc, char** argv, AsOptions* o, DriverEnv* env, continue; } + { + int tr = + driver_target_features_try_consume(tf, env, AS_TOOL, argc, argv, &i); + if (tr < 0) return 1; + if (tr > 0) continue; + } + if (a[0] == '-' && a[1] != '\0') { driver_errf(AS_TOOL, "unknown flag: %.*s", KIT_SLICE_ARG(kit_slice_cstr(a))); @@ -140,8 +147,10 @@ int driver_as(int argc, char** argv) { DriverEnv env; AsOptions o = {0}; DriverCflags cf = {0}; + DriverTargetFeatures tf = {0}; KitContext ctx; KitPreprocessOptions pp; + KitTarget* target = NULL; KitCompiler* compiler = NULL; KitWriter* writer = NULL; KitWriter* pp_writer = NULL; @@ -162,13 +171,17 @@ int driver_as(int argc, char** argv) { driver_env_init(&env); - if (driver_cflags_init(&cf, &env, argc) != 0) { + if (driver_cflags_init(&cf, &env, argc) != 0 || + driver_target_features_init(&tf, &env, argc) != 0) { driver_errf(AS_TOOL, "out of memory"); + driver_target_features_fini(&tf, &env); + driver_cflags_fini(&cf, &env); driver_env_fini(&env); return 2; } - if (as_parse(argc, argv, &o, &env, &cf) != 0) { + if (as_parse(argc, argv, &o, &env, &cf, &tf) != 0) { + driver_target_features_fini(&tf, &env); driver_cflags_fini(&cf, &env); driver_env_fini(&env); return 2; @@ -191,7 +204,8 @@ int driver_as(int argc, char** argv) { goto out; } - if (driver_compiler_new(o.target, &ctx, &compiler) != KIT_OK) { + if (driver_target_new(&ctx, o.target, &tf, AS_TOOL, &target) != KIT_OK || + driver_compiler_new(target, &ctx, &compiler) != KIT_OK) { driver_errf(AS_TOOL, "failed to initialize compiler"); goto out; } @@ -246,9 +260,11 @@ int driver_as(int argc, char** argv) { out: if (compiler) driver_compiler_free(compiler); + kit_target_free(target); if (pp_writer) kit_writer_close(pp_writer); if (writer) kit_writer_close(writer); if (loaded) ctx.file_io->release(ctx.file_io->user, &src); + driver_target_features_fini(&tf, &env); driver_cflags_fini(&cf, &env); driver_env_fini(&env); return rc; diff --git a/driver/cmd/cc.c b/driver/cmd/cc.c @@ -126,7 +126,7 @@ typedef struct CcOptions { int data_sections; /* -fdata-sections */ int warnings_are_errors; /* -Werror */ uint32_t max_errors; /* -fmax-errors=N */ - KitTarget target; /* -target / host */ + KitTargetSpec target; /* -target / host */ int target_set; /* did -target appear */ const char* output_path; /* -o */ int output_path_set; @@ -161,6 +161,7 @@ typedef struct CcOptions { /* Cflags via shared helper. */ DriverCflags cf; + DriverTargetFeatures target_features; /* Positional inputs split by suffix. */ const char** source_files; /* .c paths */ @@ -317,7 +318,8 @@ static int cc_alloc_arrays(CcOptions* o, int argc) { o->cur_link_mode = KIT_LM_DEFAULT; if (driver_cflags_init(&o->cf, o->env, (int)(bound + DRIVER_HOSTED_MAX_DEFINES + - DRIVER_HOSTED_MAX_INCLUDES)) != 0) { + DRIVER_HOSTED_MAX_INCLUDES)) != 0 || + driver_target_features_init(&o->target_features, o->env, argc) != 0) { driver_errf(CC_TOOL, "out of memory"); return 1; } @@ -353,6 +355,7 @@ static void cc_options_release(CcOptions* o) { driver_free(o->env, o->owned_sysroot_lib_dir, o->owned_sysroot_lib_dir_size); driver_hosted_plan_fini(o->env, &o->hosted); + driver_target_features_fini(&o->target_features, o->env); driver_cflags_fini(&o->cf, o->env); driver_free(o->env, o->source_files, bound * sizeof(*o->source_files)); driver_free(o->env, o->source_langs, bound * sizeof(*o->source_langs)); @@ -1515,6 +1518,12 @@ static int cc_parse(int argc, char** argv, CcOptions* o) { if (cc_record_mcmodel(o, a + 9) != 0) return 1; continue; } + { + int tr = driver_target_features_try_consume(&o->target_features, o->env, + CC_TOOL, argc, argv, &i); + if (tr < 0) return 1; + if (tr > 0) continue; + } if (driver_strneq(a, "--build-id=", 11)) { if (cc_record_build_id(o, a + 11) != 0) return 1; continue; @@ -1870,9 +1879,28 @@ static int cc_load_single_source(const KitContext* ctx, const CcOptions* o, return 0; } +static KitStatus cc_compiler_new(const CcOptions* o, const KitContext* ctx, + KitTarget** target_out, + KitCompiler** compiler_out) { + KitStatus st; + if (target_out) *target_out = NULL; + if (compiler_out) *compiler_out = NULL; + if (!o || !ctx || !target_out || !compiler_out) return KIT_INVALID; + st = driver_target_new(ctx, o->target, &o->target_features, CC_TOOL, + target_out); + if (st != KIT_OK) return st; + st = driver_compiler_new(*target_out, ctx, compiler_out); + if (st != KIT_OK) { + kit_target_free(*target_out); + *target_out = NULL; + } + return st; +} + static int cc_preprocess(DriverEnv* env, const CcOptions* o, const KitPreprocessOptions* pp_opts) { KitContext ctx = driver_env_to_context(env); + KitTarget* target = NULL; KitCompiler* compiler = NULL; KitWriter* writer = NULL; KitFileData fd = {0}; @@ -1897,7 +1925,7 @@ static int cc_preprocess(DriverEnv* env, const CcOptions* o, } } - if (driver_compiler_new(o->target, &ctx, &compiler) != KIT_OK) { + if (cc_compiler_new(o, &ctx, &target, &compiler) != KIT_OK) { driver_errf(CC_TOOL, "failed to initialize compiler"); goto out; } @@ -1910,6 +1938,7 @@ static int cc_preprocess(DriverEnv* env, const CcOptions* o, out: if (compiler) driver_compiler_free(compiler); + kit_target_free(target); if (writer) kit_writer_close(writer); if (loaded) ctx.file_io->release(ctx.file_io->user, &fd); return rc; @@ -2285,6 +2314,7 @@ static void cc_fill_c_opts(const CcOptions* o, KitCCompileOptions* copts) { static int cc_run_deps_only(DriverEnv* env, const CcOptions* o, const KitPreprocessOptions* pp) { KitContext ctx = driver_env_to_context(env); + KitTarget* target = NULL; KitCompiler* compiler = NULL; KitWriter* discard = NULL; KitFileData fd = {0}; @@ -2300,7 +2330,7 @@ static int cc_run_deps_only(DriverEnv* env, const CcOptions* o, goto out; } - if (driver_compiler_new(o->target, &ctx, &compiler) != KIT_OK) { + if (cc_compiler_new(o, &ctx, &target, &compiler) != KIT_OK) { driver_errf(CC_TOOL, "failed to initialize compiler"); goto out; } @@ -2314,6 +2344,7 @@ static int cc_run_deps_only(DriverEnv* env, const CcOptions* o, out: if (compiler) driver_compiler_free(compiler); + kit_target_free(target); if (discard) kit_writer_close(discard); if (loaded) ctx.file_io->release(ctx.file_io->user, &fd); return rc; @@ -2344,6 +2375,7 @@ static int cc_run_compile_one(DriverEnv* env, const CcOptions* o, const KitPreprocessOptions* pp, int is_memory, uint32_t index, const char* out_path) { KitContext ctx = driver_env_to_context(env); + KitTarget* target = NULL; KitCompiler* compiler = NULL; KitWriter* obj_w = NULL; KitFileData fd = {0}; @@ -2372,7 +2404,7 @@ static int cc_run_compile_one(DriverEnv* env, const CcOptions* o, goto out; } - if (driver_compiler_new(o->target, &ctx, &compiler) != KIT_OK) { + if (cc_compiler_new(o, &ctx, &target, &compiler) != KIT_OK) { driver_errf(CC_TOOL, "failed to initialize compiler"); goto out; } @@ -2409,6 +2441,7 @@ static int cc_run_compile_one(DriverEnv* env, const CcOptions* o, out: if (compiler) driver_compiler_free(compiler); + kit_target_free(target); if (obj_w) kit_writer_close(obj_w); if (loaded) ctx.file_io->release(ctx.file_io->user, &fd); return rc; @@ -2450,6 +2483,7 @@ static int cc_run_check(DriverEnv* env, const CcOptions* o, const KitPreprocessOptions* pp) { KitContext ctx = driver_env_to_context(env); const KitFileIO* io = ctx.file_io; + KitTarget* target = NULL; KitCompiler* compiler = NULL; DriverLoad* src_lf = NULL; KitSlice* src_bytes = NULL; @@ -2477,7 +2511,7 @@ static int cc_run_check(DriverEnv* env, const CcOptions* o, goto out; } - if (driver_compiler_new(o->target, &ctx, &compiler) != KIT_OK) { + if (cc_compiler_new(o, &ctx, &target, &compiler) != KIT_OK) { driver_errf(CC_TOOL, "failed to initialize compiler"); goto out; } @@ -2506,6 +2540,7 @@ static int cc_run_check(DriverEnv* env, const CcOptions* o, out: if (compiler) driver_compiler_free(compiler); + kit_target_free(target); if (src_lf) { for (i = 0; i < o->nsource_files; ++i) driver_release_bytes(io, &src_lf[i]); } @@ -2523,6 +2558,7 @@ static int cc_run_link_exe(DriverEnv* env, const CcOptions* o, const KitPreprocessOptions* pp) { KitContext ctx = driver_env_to_context(env); const KitFileIO* io = ctx.file_io; + KitTarget* target = NULL; KitCompiler* compiler = NULL; KitWriter* out_w = NULL; DriverLoad* src_lf = NULL; @@ -2627,7 +2663,7 @@ static int cc_run_link_exe(DriverEnv* env, const CcOptions* o, goto out; } - if (driver_compiler_new(o->target, &ctx, &compiler) != KIT_OK) { + if (cc_compiler_new(o, &ctx, &target, &compiler) != KIT_OK) { driver_errf(CC_TOOL, "failed to initialize compiler"); goto out; } @@ -2769,6 +2805,7 @@ out: } if (script) kit_link_script_free(&ctx, script); if (compiler) driver_compiler_free(compiler); + kit_target_free(target); driver_release_bytes(io, &script_lf); if (arch_lf) { for (i = 0; i < o->narchives; ++i) driver_release_bytes(io, &arch_lf[i]); diff --git a/driver/cmd/compile.c b/driver/cmd/compile.c @@ -49,7 +49,8 @@ typedef struct CompileOptions { const char* output_path; int warnings_are_errors; uint32_t max_errors; - KitTarget target; + KitTargetSpec target; + DriverTargetFeatures target_features; } CompileOptions; static void compile_usage(void) { @@ -275,6 +276,13 @@ static int compile_parse(int argc, char** argv, CompileOptions* o) { continue; } + { + int tr = driver_target_features_try_consume(&o->target_features, o->env, + COMPILE_TOOL, argc, argv, &i); + if (tr < 0) return 1; + if (tr > 0) continue; + } + if (driver_streq(a, "--")) { for (++i; i < argc; ++i) if (compile_classify_positional(o, argv[i]) != 0) return 1; @@ -369,8 +377,7 @@ static char* compile_default_out(DriverEnv* env, const CompileOptions* o, /* Compile one source. out_path is NULL for check-only. */ static int compile_one(const KitContext* ctx, KitCompiler* compiler, - KitLanguage lang, - const KitCodeOptions* code, + KitLanguage lang, const KitCodeOptions* code, const KitDiagnosticOptions* diag, const KitPreprocessOptions* pp, const void* lang_extra, const char* src, const char* out_path) { @@ -417,6 +424,7 @@ int driver_compile(int argc, char** argv) { DriverRuntimeSupport runtime = {0}; int runtime_resolved = 0; KitContext ctx; + KitTarget* target = NULL; KitCompiler* compiler = NULL; KitFrontendCaps caps = {0}; KitLanguage lang = KIT_LANG_UNKNOWN; @@ -438,9 +446,10 @@ int driver_compile(int argc, char** argv) { o.sources = driver_alloc_zeroed(&env, (size_t)argc * sizeof(*o.sources)); o.fe_args = driver_alloc_zeroed(&env, (size_t)argc * sizeof(*o.fe_args)); - if (!o.sources || !o.fe_args || driver_cflags_init(&o.cf, &env, argc) != 0) { + if (!o.sources || !o.fe_args || driver_cflags_init(&o.cf, &env, argc) != 0 || + driver_target_features_init(&o.target_features, &env, argc) != 0) { driver_errf(COMPILE_TOOL, "out of memory"); - goto done_early; + goto done; } if (compile_parse(argc, argv, &o) != 0) goto done; @@ -461,7 +470,9 @@ int driver_compile(int argc, char** argv) { } ctx = driver_env_to_context(&env); - if (driver_compiler_new(o.target, &ctx, &compiler) != KIT_OK) { + if (driver_target_new(&ctx, o.target, &o.target_features, COMPILE_TOOL, + &target) != KIT_OK || + driver_compiler_new(target, &ctx, &compiler) != KIT_OK) { driver_errf(COMPILE_TOOL, "failed to initialize compiler"); rc = 1; goto done; @@ -552,11 +563,14 @@ int driver_compile(int argc, char** argv) { done: if (lang_extra) kit_frontend_free_options(compiler, lang, lang_extra); if (compiler) driver_compiler_free(compiler); + kit_target_free(target); if (runtime_resolved) driver_runtime_support_fini(&env, &runtime); + driver_target_features_fini(&o.target_features, &env); driver_cflags_fini(&o.cf, &env); -done_early: - if (o.sources) driver_free(&env, o.sources, (size_t)argc * sizeof(*o.sources)); - if (o.fe_args) driver_free(&env, o.fe_args, (size_t)argc * sizeof(*o.fe_args)); + if (o.sources) + driver_free(&env, o.sources, (size_t)argc * sizeof(*o.sources)); + if (o.fe_args) + driver_free(&env, o.fe_args, (size_t)argc * sizeof(*o.fe_args)); driver_env_fini(&env); return rc; } diff --git a/driver/cmd/cpp.c b/driver/cmd/cpp.c @@ -29,7 +29,7 @@ typedef struct CppOptions { const char* output_path; const char* source_path; /* NULL when stdin source */ int from_stdin; - KitTarget target; + KitTargetSpec target; } CppOptions; void driver_help_cpp(void) { @@ -73,7 +73,7 @@ static void cpp_usage(void) { } static int cpp_parse(int argc, char** argv, CppOptions* o, DriverEnv* env, - DriverCflags* cf) { + DriverCflags* cf, DriverTargetFeatures* tf) { int i; o->target = driver_host_target(); @@ -108,6 +108,13 @@ static int cpp_parse(int argc, char** argv, CppOptions* o, DriverEnv* env, continue; } + { + int tr = + driver_target_features_try_consume(tf, env, CPP_TOOL, argc, argv, &i); + if (tr < 0) return 1; + if (tr > 0) continue; + } + if (driver_streq(a, "-")) { if (o->source_path || o->from_stdin) { driver_errf(CPP_TOOL, "multiple inputs not supported"); @@ -142,8 +149,10 @@ int driver_cpp(int argc, char** argv) { DriverEnv env; CppOptions o = {0}; DriverCflags cf = {0}; + DriverTargetFeatures tf = {0}; KitContext ctx; KitPreprocessOptions pp; + KitTarget* target = NULL; KitCompiler* compiler = NULL; KitWriter* writer = NULL; KitFileData src = {0}; @@ -161,13 +170,17 @@ int driver_cpp(int argc, char** argv) { driver_env_init(&env); - if (driver_cflags_init(&cf, &env, argc) != 0) { + if (driver_cflags_init(&cf, &env, argc) != 0 || + driver_target_features_init(&tf, &env, argc) != 0) { driver_errf(CPP_TOOL, "out of memory"); + driver_target_features_fini(&tf, &env); + driver_cflags_fini(&cf, &env); driver_env_fini(&env); return 2; } - if (cpp_parse(argc, argv, &o, &env, &cf) != 0) { + if (cpp_parse(argc, argv, &o, &env, &cf, &tf) != 0) { + driver_target_features_fini(&tf, &env); driver_cflags_fini(&cf, &env); driver_env_fini(&env); return 2; @@ -212,7 +225,8 @@ int driver_cpp(int argc, char** argv) { } } - if (driver_compiler_new(o.target, &ctx, &compiler) != KIT_OK) { + if (driver_target_new(&ctx, o.target, &tf, CPP_TOOL, &target) != KIT_OK || + driver_compiler_new(target, &ctx, &compiler) != KIT_OK) { driver_errf(CPP_TOOL, "failed to initialize compiler"); goto out; } @@ -223,9 +237,11 @@ int driver_cpp(int argc, char** argv) { out: if (compiler) driver_compiler_free(compiler); + kit_target_free(target); if (writer) kit_writer_close(writer); if (loaded) ctx.file_io->release(ctx.file_io->user, &src); if (stdin_buf) driver_free(&env, stdin_buf, stdin_size); + driver_target_features_fini(&tf, &env); driver_cflags_fini(&cf, &env); driver_env_fini(&env); return rc; diff --git a/driver/cmd/dbg.c b/driver/cmd/dbg.c @@ -2375,7 +2375,7 @@ static void dbg_cmd_disasm(DbgState* s, uint64_t addr, size_t count) { } memset(&dctx, 0, sizeof(dctx)); - dctx.target = driver_host_target(); + dctx.target = kit_compiler_target(s->compiler); dctx.context = s->ctx; if (kit_disasm_iter_new(&dctx, buf, byte_count, addr, s->view, &it) != KIT_OK) { @@ -3377,12 +3377,12 @@ int driver_dbg(int argc, char** argv) { DriverEnv env; DbgOpts o = {0}; KitCompiler* compiler = NULL; + KitTarget* target = NULL; KitJit* jit = NULL; DbgState st = {0}; KitContext ctx; KitJitHost jhost; KitDbgHost dhost; - KitTarget target; int rc; if (driver_argv_wants_help(argc, argv, 1)) { @@ -3402,17 +3402,24 @@ int driver_dbg(int argc, char** argv) { ctx = driver_env_to_context(&env); jhost = driver_env_to_jit_host(&env); dhost = driver_env_to_dbg_host(&env); - target = driver_host_target(); - if (driver_compiler_new(target, &ctx, &compiler) != KIT_OK) { - driver_errf(DBG_TOOL, "failed to initialize compiler"); - dbg_options_release(&o); - driver_env_fini(&env); - return 1; + { + KitTargetOptions topts; + memset(&topts, 0, sizeof topts); + topts.spec = driver_host_target(); + if (kit_target_new(&ctx, &topts, &target) != KIT_OK || + driver_compiler_new(target, &ctx, &compiler) != KIT_OK) { + driver_errf(DBG_TOOL, "failed to initialize compiler"); + kit_target_free(target); + dbg_options_release(&o); + driver_env_fini(&env); + return 1; + } } rc = dbg_compile_and_jit(&o, compiler, &jhost, &jit); if (rc != 0) { driver_compiler_free(compiler); + kit_target_free(target); dbg_options_release(&o); driver_env_fini(&env); return rc; @@ -3436,6 +3443,7 @@ int driver_dbg(int argc, char** argv) { KIT_SLICE_ARG(kit_slice_cstr(o.entry))); kit_jit_free(jit); driver_compiler_free(compiler); + kit_target_free(target); dbg_options_release(&o); driver_env_fini(&env); return 1; @@ -3469,6 +3477,7 @@ int driver_dbg(int argc, char** argv) { if (st.session) kit_jit_session_free(st.session); kit_jit_free(jit); driver_compiler_free(compiler); + kit_target_free(target); dbg_options_release(&o); driver_env_fini(&env); return 0; diff --git a/driver/cmd/disas.c b/driver/cmd/disas.c @@ -16,7 +16,7 @@ #define DISAS_TOOL "disas" typedef struct DisasOpts { - KitTarget target; + KitTargetSpec target; uint64_t base; /* --base: vaddr of the first byte */ const char* hex; /* -x: inline hex string, or NULL */ const char* in; /* positional file, "-" for stdin, or NULL */ @@ -132,6 +132,8 @@ int driver_disas(int argc, char** argv) { KitContext ctx; KitDisasmContext dctx; DisasOpts o; + DriverTargetFeatures tf = {0}; + KitTarget* target = NULL; const uint8_t* data = NULL; size_t len = 0; DriverLoad ld = {0}; @@ -150,6 +152,12 @@ int driver_disas(int argc, char** argv) { memset(&o, 0, sizeof o); o.target = driver_host_target(); driver_env_init(&env); + if (driver_target_features_init(&tf, &env, argc) != 0) { + driver_errf(DISAS_TOOL, "out of memory"); + driver_target_features_fini(&tf, &env); + driver_env_fini(&env); + return 1; + } for (i = 1; i < argc; ++i) { const char* a = argv[i]; @@ -164,6 +172,12 @@ int driver_disas(int argc, char** argv) { } continue; } + { + int tr = driver_target_features_try_consume(&tf, &env, DISAS_TOOL, argc, + argv, &i); + if (tr < 0) goto done; + if (tr > 0) continue; + } if (driver_streq(a, "-x")) { if (i + 1 >= argc) { driver_errf(DISAS_TOOL, "-x requires a hex string"); @@ -257,16 +271,23 @@ int driver_disas(int argc, char** argv) { } ctx = driver_env_to_context(&env); + if (driver_target_new(&ctx, o.target, &tf, DISAS_TOOL, &target) != KIT_OK) { + driver_errf(DISAS_TOOL, "failed to initialize target"); + rc = 1; + goto done; + } memset(&dctx, 0, sizeof dctx); - dctx.target = o.target; + dctx.target = target; dctx.context = ctx; disas_run(&dctx, data, len, o.base); rc = 0; done: + kit_target_free(target); if (hexbuf) driver_free(&env, hexbuf, hexbuf_len); if (sbuf) driver_free(&env, sbuf, sbuf_len); if (loaded) driver_release_bytes(&env.file_io, &ld); + driver_target_features_fini(&tf, &env); driver_env_fini(&env); return rc; } diff --git a/driver/cmd/emu.c b/driver/cmd/emu.c @@ -3,6 +3,7 @@ #include <kit/object.h> #include <stddef.h> #include <stdint.h> +#include <string.h> #include "driver.h" @@ -29,7 +30,7 @@ typedef struct EmuOptions { KitEmuTraceFlags trace; KitArchKind guest_arch; int guest_arch_set; - KitTarget guest_target; + KitTargetSpec guest_target; int guest_target_set; const char* guest_path; /* positional input (required) */ @@ -231,7 +232,7 @@ static const char* emu_arch_name(KitArchKind a) { } static int emu_resolve_target(EmuOptions* o, const KitSlice* input) { - KitTarget detected; + KitTargetSpec detected; if (kit_detect_target(input->data, input->len, &detected) != KIT_OK) { driver_errf(EMU_TOOL, "could not detect target from %.*s", KIT_SLICE_ARG(kit_slice_cstr(o->guest_path))); @@ -261,6 +262,7 @@ int driver_emu(int argc, char** argv) { DriverEnv env; EmuOptions eo = {0}; KitContext ctx; + KitTarget* target = NULL; KitCompiler* compiler = NULL; DriverLoad guest_lf = {0}; KitSlice guest_in; @@ -300,9 +302,15 @@ int driver_emu(int argc, char** argv) { /* The emu's host-side compiler runs at the host's native target — * the JIT image holds host code, while the guest target is resolved * from the executable and optional driver flags. */ - if (driver_compiler_new(driver_host_target(), &ctx, &compiler) != KIT_OK) { - driver_errf(EMU_TOOL, "failed to initialize compiler"); - goto out; + { + KitTargetOptions topts; + memset(&topts, 0, sizeof topts); + topts.spec = driver_host_target(); + if (kit_target_new(&ctx, &topts, &target) != KIT_OK || + driver_compiler_new(target, &ctx, &compiler) != KIT_OK) { + driver_errf(EMU_TOOL, "failed to initialize compiler"); + goto out; + } } emu_finalize_argv(&eo, &guest_argv); @@ -335,6 +343,7 @@ int driver_emu(int argc, char** argv) { out: if (compiler) driver_compiler_free(compiler); + kit_target_free(target); if (guest_lf.loaded && ctx.file_io) driver_release_bytes(ctx.file_io, &guest_lf); emu_options_release(&eo); diff --git a/driver/cmd/ld.c b/driver/cmd/ld.c @@ -71,7 +71,7 @@ typedef struct LdOptions { const char* driver_path; size_t argv_bound; - KitTarget target; + KitTargetSpec target; const char* output_path; /* -o */ int output_seen; @@ -1082,6 +1082,7 @@ static void release_all(LoadedFile* arr, uint32_t n) { static int ld_run_link(LdOptions* o) { KitContext ctx = driver_env_to_context(o->env); const KitFileIO* io = ctx.file_io; + KitTarget* target = NULL; KitCompiler* compiler = NULL; KitWriter* writer = NULL; LoadedFile* obj_lf = NULL; @@ -1128,7 +1129,7 @@ static int ld_run_link(LdOptions* o) { /* Auto-detect target from the first object input, falling back to * the host target. PIC overrides parsed from flags are preserved. */ if (o->nobject_files > 0) { - KitTarget detected; + KitTargetSpec detected; if (kit_detect_target(obj_lf[0].data.data, obj_lf[0].data.size, &detected) == KIT_OK) { uint8_t pic = o->target.pic; @@ -1205,9 +1206,15 @@ static int ld_run_link(LdOptions* o) { } } - if (driver_compiler_new(o->target, &ctx, &compiler) != KIT_OK) { - driver_errf(LD_TOOL, "failed to initialize compiler"); - goto out; + { + KitTargetOptions topts; + memset(&topts, 0, sizeof topts); + topts.spec = o->target; + if (kit_target_new(&ctx, &topts, &target) != KIT_OK || + driver_compiler_new(target, &ctx, &compiler) != KIT_OK) { + driver_errf(LD_TOOL, "failed to initialize compiler"); + goto out; + } } if (script_lf.loaded) { @@ -1331,6 +1338,7 @@ out: } if (script) kit_link_script_free(&ctx, script); if (compiler) driver_compiler_free(compiler); + kit_target_free(target); driver_runtime_archive_fini(o->env, &rt_archive); if (runtime_resolved) driver_runtime_support_fini(o->env, &runtime); release_file(&script_lf); diff --git a/driver/cmd/mc.c b/driver/cmd/mc.c @@ -19,7 +19,7 @@ #define MC_TOOL "mc" typedef struct McOpts { - KitTarget target; + KitTargetSpec target; int plain; /* -p: raw .text hex only */ } McOpts; @@ -73,7 +73,7 @@ static void mc_print_plain(const uint8_t* text, size_t len) { /* Disassemble the assembled .text and print one `mnemonic ops # encoding: [..]` * line per instruction. */ -static void mc_print_encoding(const KitContext* ctx, KitTarget target, +static void mc_print_encoding(const KitContext* ctx, const KitTarget* target, const uint8_t* text, size_t len) { KitDisasmContext dctx; KitDisasmIter* it = NULL; @@ -143,6 +143,8 @@ int driver_mc(int argc, char** argv) { DriverEnv env; KitContext ctx; McOpts o; + DriverTargetFeatures tf = {0}; + KitTarget* target = NULL; KitCompiler* compiler = NULL; KitCompileSession* session = NULL; KitObjBuilder* ob = NULL; @@ -164,6 +166,12 @@ int driver_mc(int argc, char** argv) { memset(&o, 0, sizeof o); o.target = driver_host_target(); driver_env_init(&env); + if (driver_target_features_init(&tf, &env, argc) != 0) { + driver_errf(MC_TOOL, "out of memory"); + driver_target_features_fini(&tf, &env); + driver_env_fini(&env); + return 1; + } for (i = 1; i < argc; ++i) { const char* a = argv[i]; @@ -182,6 +190,12 @@ int driver_mc(int argc, char** argv) { o.plain = 1; continue; } + { + int tr = driver_target_features_try_consume(&tf, &env, MC_TOOL, argc, + argv, &i); + if (tr < 0) goto done; + if (tr > 0) continue; + } if (driver_streq(a, "-")) { read_stdin = 1; first_pos = i; /* marks "have input"; stdin overrides token join */ @@ -216,7 +230,8 @@ int driver_mc(int argc, char** argv) { } ctx = driver_env_to_context(&env); - if (driver_compiler_new(o.target, &ctx, &compiler) != KIT_OK) { + if (driver_target_new(&ctx, o.target, &tf, MC_TOOL, &target) != KIT_OK || + driver_compiler_new(target, &ctx, &compiler) != KIT_OK) { driver_errf(MC_TOOL, "failed to initialize compiler"); rc = 1; goto done; @@ -278,7 +293,7 @@ int driver_mc(int argc, char** argv) { if (o.plain) mc_print_plain(text, text_len); else - mc_print_encoding(&ctx, o.target, text, text_len); + mc_print_encoding(&ctx, target, text, text_len); mc_print_relocs(objf); rc = 0; } @@ -289,8 +304,10 @@ done: if (ob) kit_obj_builder_free(ob); if (session) kit_compile_session_free(session); if (compiler) driver_compiler_free(compiler); + kit_target_free(target); if (stdin_buf) driver_free(&env, stdin_buf, stdin_len); if (src && !read_stdin) driver_free(&env, src, src_len); + driver_target_features_fini(&tf, &env); driver_env_fini(&env); return rc; } diff --git a/driver/cmd/nm.c b/driver/cmd/nm.c @@ -97,7 +97,7 @@ static int nm_value_cmp(const void* a, const void* b) { static int nm_append_sym(DriverEnv* env, NmSym** syms, uint32_t* n, uint32_t* cap, const KitObjSymInfo* si, const char* file_prefix, int* ptr_digits, - KitTarget t) { + KitTargetSpec t) { int d = (t.ptr_size == 4) ? 8 : 16; char* name; if (d > *ptr_digits) *ptr_digits = d; @@ -130,7 +130,7 @@ static int nm_append_sym(DriverEnv* env, NmSym** syms, uint32_t* n, static int nm_collect_obj(KitObjFile* of, const NmOpts* opts, const char* file_prefix, NmSym** syms, uint32_t* n, uint32_t* cap, int* ptr_digits, DriverEnv* env) { - KitTarget t = kit_obj_target(of); + KitTargetSpec t = kit_obj_target(of); KitObjSymIter* it = NULL; int rc = 0; KitStatus st = opts->dynamic ? kit_obj_dynsymiter_new(of, &it) diff --git a/driver/cmd/objdump.c b/driver/cmd/objdump.c @@ -1023,10 +1023,15 @@ static void dump_disasm(const KitDisasmContext* dctx, KitObjFile* f, uint32_t i; uint32_t emitted = 0; KitDisasmContext file_dctx; + KitTarget* file_target = NULL; + KitTargetOptions topts; if (!dctx) return; file_dctx = *dctx; - file_dctx.target = kit_obj_target(f); + memset(&topts, 0, sizeof topts); + topts.spec = kit_obj_target(f); + if (kit_target_new(&dctx->context, &topts, &file_target) != KIT_OK) return; + file_dctx.target = file_target; for (i = 0; i < nsec; ++i) { KitObjSecInfo sec; @@ -1056,6 +1061,7 @@ static void dump_disasm(const KitDisasmContext* dctx, KitObjFile* f, * was stripped. Fall back to the executable load segments. */ if (emitted == 0 && kit_obj_kind(f) != KIT_OBJ_KIND_REL) dump_disasm_segments(&file_dctx, f, opts, image); + kit_target_free(file_target); } /* `-f`: GNU objdump-style file header summary. Object files have no @@ -1064,7 +1070,7 @@ static void dump_disasm(const KitDisasmContext* dctx, KitObjFile* f, * whether the input has symbols and relocations so it's clear at a * glance whether further -t / -r work is going to be productive. */ static void dump_file_header(KitObjFile* f, const char* label) { - KitTarget target = kit_obj_target(f); + KitTargetSpec target = kit_obj_target(f); KitObjFmt fmt = kit_obj_fmt(f); KitObjSymIter* sit = NULL; KitObjRelocIter* rit = NULL; @@ -1587,7 +1593,7 @@ static void dump_private(KitObjFile* f) { static void dump_obj(const KitContext* ctx, const KitDisasmContext* dctx, const char* label, KitObjFile* f, const ObjdumpOpts* opts, const KitSlice* image) { - KitTarget target = kit_obj_target(f); + KitTargetSpec target = kit_obj_target(f); KitObjFmt fmt = kit_obj_fmt(f); driver_printf("%.*s:\tfile format %.*s-%.*s\n\n", @@ -1853,7 +1859,7 @@ int driver_objdump(int argc, char** argv) { /* Disassembler context: a target + ctx value pair. Disasm consults * the per-file object reader for annotation. */ if (opts.d || opts.D) { - dctx.target = driver_host_target(); + dctx.target = NULL; dctx.context = ctx; dctx_p = &dctx; } diff --git a/driver/cmd/run.c b/driver/cmd/run.c @@ -39,7 +39,8 @@ typedef struct RunOptions { const char* entry; /* -e, default "main" */ const char* sysroot; /* --sysroot */ int wants_hosted_libc; /* -lc */ - KitTarget target; /* -target / host */ + KitTargetSpec target; /* -target / host */ + DriverTargetFeatures target_features; DriverHostedPlan hosted; DriverCflags cf; @@ -349,6 +350,10 @@ static int run_alloc_arrays(RunOptions* o, int argc) { driver_errf(RUN_TOOL, "out of memory"); return 1; } + if (driver_target_features_init(&o->target_features, o->env, argc) != 0) { + driver_errf(RUN_TOOL, "out of memory"); + return 1; + } return 0; } @@ -540,6 +545,12 @@ static int run_parse(int argc, char** argv, RunOptions* o) { if (run_record_mcmodel(o, a + 9) != 0) return 1; continue; } + { + int tr = driver_target_features_try_consume(&o->target_features, o->env, + RUN_TOOL, argc, argv, &i); + if (tr < 0) return 1; + if (tr > 0) continue; + } if (driver_streq(a, "-target")) { if (++i >= argc) { @@ -634,6 +645,7 @@ static void run_options_release(RunOptions* o) { size_t bound = o->argv_bound; driver_hosted_plan_fini(o->env, &o->hosted); driver_inputs_release(&o->inputs); + driver_target_features_fini(&o->target_features, o->env); driver_cflags_fini(&o->cf, o->env); driver_free(o->env, o->prog_argv, bound * sizeof(*o->prog_argv)); } @@ -881,6 +893,7 @@ int driver_run(int argc, char** argv) { RunOptions ro = {0}; KitContext ctx; KitJitHost jhost; + KitTarget* target = NULL; KitCompiler* compiler = NULL; KitJit* jit = NULL; KitInterpProgram* interp = NULL; @@ -922,8 +935,11 @@ int driver_run(int argc, char** argv) { * and the entry call, free after kit_jit_free. */ ctx = driver_env_to_context(&env); jhost = driver_env_to_jit_host(&env); - if (driver_compiler_new(ro.target, &ctx, &compiler) != KIT_OK) { + if (driver_target_new(&ctx, ro.target, &ro.target_features, RUN_TOOL, + &target) != KIT_OK || + driver_compiler_new(target, &ctx, &compiler) != KIT_OK) { driver_errf(RUN_TOOL, "failed to initialize compiler"); + kit_target_free(target); run_metrics_finish(metrics); run_options_release(&ro); driver_env_fini(&env); @@ -939,6 +955,7 @@ int driver_run(int argc, char** argv) { if (!interp) { driver_errf(RUN_TOOL, "failed to initialize interpreter"); driver_compiler_free(compiler); + kit_target_free(target); run_metrics_finish(metrics); run_options_release(&ro); driver_env_fini(&env); @@ -958,6 +975,7 @@ int driver_run(int argc, char** argv) { bench_compile_end - bench_compile_start); kit_interp_program_free(interp); driver_compiler_free(compiler); + kit_target_free(target); run_metrics_finish(metrics); run_options_release(&ro); driver_env_fini(&env); @@ -973,6 +991,7 @@ int driver_run(int argc, char** argv) { kit_interp_program_free(interp); kit_jit_free(jit); driver_compiler_free(compiler); + kit_target_free(target); run_metrics_finish(metrics); run_options_release(&ro); driver_env_fini(&env); @@ -1051,6 +1070,7 @@ after_entry: kit_interp_program_free(interp); kit_jit_free(jit); driver_compiler_free(compiler); + kit_target_free(target); run_metrics_finish(metrics); run_options_release(&ro); driver_env_fini(&env); diff --git a/driver/driver.h b/driver/driver.h @@ -141,18 +141,43 @@ int driver_is_help_flag(const char* arg); int driver_argv_wants_help(int argc, char** argv, int accept_short_h); /* Parse a target triple string (`<arch>[-<vendor>]-<os>[-<env>]`) into a - * KitTarget. Recognized arches: x86_64/amd64, i386/i486/i586/i686, aarch64/ + * KitTargetSpec. Recognized arches: x86_64/amd64, i386/i486/i586/i686, aarch64/ * arm64, arm/armv7, riscv64, riscv32, wasm32, wasm64. Recognized OSes (scanned * across the remaining components, so vendor tokens like `pc`/`apple`/ * `unknown` are skipped): linux, darwin/macos, windows/win32, wasi, none/ * freestanding. Sets arch/os/obj/ptr_size/ptr_align/big_endian; pic and * code_model are left at their defaults. Returns 0 on success, nonzero on * unrecognized arch or NULL inputs. */ -int driver_target_from_triple(const char* triple, KitTarget* out); +int driver_target_from_triple(const char* triple, KitTargetSpec* out); /* Render a canonical driver target triple into `buf`. Returns 0 on success, * nonzero when `buf` is too small. */ -int driver_target_to_triple(KitTarget target, char* buf, size_t cap); +int driver_target_to_triple(KitTargetSpec target, char* buf, size_t cap); + +typedef struct DriverTargetFeatures { + DriverEnv* env; + KitTargetFeature* features; + uint32_t nfeatures; + uint32_t cap_features; + KitSlice isa; + KitSlice cpu; + KitSlice tune; +} DriverTargetFeatures; + +/* Shared target-feature parser. Canonical spelling is `-mattr=+foo,-bar`; + * GCC/clang-style `-mfoo` and `-mno-foo` are accepted as aliases and lowered + * into the same KitTargetFeature list before libkit sees the target. */ +int driver_target_features_init(DriverTargetFeatures*, DriverEnv*, + int argc_bound); +void driver_target_features_fini(DriverTargetFeatures*, DriverEnv*); +int driver_target_features_try_consume(DriverTargetFeatures*, DriverEnv*, + const char* tool, int argc, char** argv, + int* i); +int driver_target_options(const DriverTargetFeatures*, const char* tool, + KitTargetSpec, KitTargetOptions* out); +KitStatus driver_target_new(const KitContext*, KitTargetSpec, + const DriverTargetFeatures*, const char* tool, + KitTarget** out); /* Default PIC/PIE model for a target. Hosted targets default to PIE, * matching modern gcc/clang and platform norms (ELF -> ET_DYN, Mach-O -> @@ -163,7 +188,7 @@ KitPic driver_default_pic(KitObjFmt obj, KitOSKind os); /* Resolve whether the link step should emit a position-independent * executable. An explicit -pie always wins; -shared and -r (relocatable) * always suppress it; otherwise it follows the target's PIC model. */ -int driver_link_pie(KitTarget target, int explicit_pie, int shared, +int driver_link_pie(KitTargetSpec target, int explicit_pie, int shared, int relocatable); #endif diff --git a/driver/env.h b/driver/env.h @@ -51,16 +51,18 @@ KitDbgHost driver_env_to_dbg_host(const DriverEnv*); * KitCompiler from outside those helpers (none today). */ void driver_diag_set_compiler(KitCompiler*); -/* Lifecycle helpers around kit_compiler_{new,free}. Identical to the - * raw entries except they register the active compiler with the stderr - * diag sink so diagnostics resolve loc.file_id to its registered path. +/* Lifecycle helpers around kit_compiler_{new,free}. Identical to the raw + * entries except they register the active compiler with the stderr diag sink + * so diagnostics resolve loc.file_id to its registered path. The compiler + * borrows `target`; callers own it and must free it after driver_compiler_free. * Returns KIT_OK on success; on failure *out is NULL. */ -KitStatus driver_compiler_new(KitTarget, const KitContext*, KitCompiler** out); +KitStatus driver_compiler_new(const KitTarget*, const KitContext*, + KitCompiler** out); void driver_compiler_free(KitCompiler*); /* Default target used by tools that don't expose a target-selection flag * yet. v1: native-looking host target (chosen at compile time). */ -KitTarget driver_host_target(void); +KitTargetSpec driver_host_target(void); /* ---------------------------------------------------------------------- * Hosted-libc search directories @@ -118,7 +120,7 @@ void driver_hosted_dirs_fini(DriverHostedDirs* dirs); * nothing (its MinGW sysroot comes from --sysroot/KIT_SYSROOT in the cc * driver). The single env-var override, KIT_SYSROOT, is consulted by the * caller, not here. */ -int driver_default_hosted_dirs(DriverEnv* env, KitTarget target, +int driver_default_hosted_dirs(DriverEnv* env, KitTargetSpec target, DriverHostedDirs* out); /* ---------------------------------------------------------------------- diff --git a/driver/env/common.c b/driver/env/common.c @@ -62,7 +62,7 @@ static KitCompiler* g_diag_active_compiler; void driver_diag_set_compiler(KitCompiler* c) { g_diag_active_compiler = c; } -KitStatus driver_compiler_new(KitTarget t, const KitContext* ctx, +KitStatus driver_compiler_new(const KitTarget* t, const KitContext* ctx, KitCompiler** out) { KitCompiler* c = NULL; KitStatus st = kit_compiler_new(t, ctx, &c); diff --git a/driver/env/env_posix.h b/driver/env/env_posix.h @@ -92,8 +92,8 @@ int os_stat_mtime_ns(const struct stat* sb, int64_t* out); * underscore for dlsym; ELF passes through). */ void* os_dlsym(const char* name); -/* Fill the OS/object-format slots of KitTarget for the host. */ -void os_host_target_fill(KitTarget* t); +/* Fill the OS/object-format slots of KitTargetSpec for the host. */ +void os_host_target_fill(KitTargetSpec* t); /* ---- vtable singletons wired into DriverEnv on POSIX -------------------- */ extern KitExecMem g_execmem_posix; /* posix.c — page_size set in init */ diff --git a/driver/env/freebsd.c b/driver/env/freebsd.c @@ -133,7 +133,7 @@ void* os_dlsym(const char* name) { /* ---------------- host_target os/obj ---------------- */ -void os_host_target_fill(KitTarget* t) { +void os_host_target_fill(KitTargetSpec* t) { t->os = KIT_OS_FREEBSD; t->obj = KIT_OBJ_ELF; } @@ -171,7 +171,7 @@ int driver_self_exe_path(DriverEnv* env, char** out, size_t* out_size) { /* FreeBSD base system is flat: headers in /usr/include, crt + libc in /usr/lib * and /lib (libc.so.7 lives in /lib). Host target only. UNTESTED on the macOS * dev host -- see hosted_resolve_freebsd in driver/lib/hosted.c. */ -int driver_default_hosted_dirs(DriverEnv* env, KitTarget target, +int driver_default_hosted_dirs(DriverEnv* env, KitTargetSpec target, DriverHostedDirs* out) { (void)env; if (target.os != KIT_OS_FREEBSD) return 1; diff --git a/driver/env/linux.c b/driver/env/linux.c @@ -136,7 +136,7 @@ void* os_dlsym(const char* name) { /* ---------------- host_target os/obj ---------------- */ -void os_host_target_fill(KitTarget* t) { +void os_host_target_fill(KitTargetSpec* t) { t->os = KIT_OS_LINUX; t->obj = KIT_OBJ_ELF; } @@ -190,7 +190,7 @@ static const char* linux_multiarch_triple(KitArchKind arch) { * /usr/lib + /lib. We hand the resolver an ordered library search list that * covers both, plus the standard include roots, and let its per-file search * bind each crt/libc wherever it actually lives. Host target only. */ -int driver_default_hosted_dirs(DriverEnv* env, KitTarget target, +int driver_default_hosted_dirs(DriverEnv* env, KitTargetSpec target, DriverHostedDirs* out) { const char* triple; (void)env; diff --git a/driver/env/macos.c b/driver/env/macos.c @@ -124,7 +124,7 @@ void* os_dlsym(const char* name) { /* ---------------- host_target os/obj ---------------- */ -void os_host_target_fill(KitTarget* t) { +void os_host_target_fill(KitTargetSpec* t) { t->os = KIT_OS_MACOS; t->obj = KIT_OBJ_MACHO; } @@ -177,7 +177,7 @@ int driver_self_exe_path(DriverEnv* env, char** out, size_t* out_size) { * nothing. The <sdk>/usr/{include,lib} mapping mirrors * hosted_dirs_from_sysroot's macOS case in driver/lib/hosted.c (layering * forbids calling into it from the env TU). */ -int driver_default_hosted_dirs(DriverEnv* env, KitTarget target, +int driver_default_hosted_dirs(DriverEnv* env, KitTargetSpec target, DriverHostedDirs* out) { static const char* const candidates[] = { "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk", diff --git a/driver/env/posix.c b/driver/env/posix.c @@ -1144,8 +1144,8 @@ static KitArchKind host_arch_self(void) { #endif } -KitTarget driver_host_target(void) { - KitTarget t; +KitTargetSpec driver_host_target(void) { + KitTargetSpec t; t.arch = host_arch_self(); os_host_target_fill(&t); t.ptr_size = (uint8_t)sizeof(void*); diff --git a/driver/env/windows.c b/driver/env/windows.c @@ -1783,8 +1783,8 @@ static KitArchKind host_arch_self_win(void) { #endif } -KitTarget driver_host_target(void) { - KitTarget t; +KitTargetSpec driver_host_target(void) { + KitTargetSpec t; t.arch = host_arch_self_win(); t.os = KIT_OS_WINDOWS; t.obj = KIT_OBJ_COFF; @@ -1799,7 +1799,7 @@ KitTarget driver_host_target(void) { /* No host probe on Windows: the MinGW sysroot is supplied via --sysroot or the * KIT_SYSROOT env var (handled in the cc driver), not auto-discovered from a * fixed install path. */ -int driver_default_hosted_dirs(DriverEnv* env, KitTarget target, +int driver_default_hosted_dirs(DriverEnv* env, KitTargetSpec target, DriverHostedDirs* out) { (void)env; (void)target; diff --git a/driver/lib/hosted.c b/driver/lib/hosted.c @@ -158,8 +158,15 @@ static int hosted_add_clang_compat_defines(DriverHostedPlan* plan) { } static int hosted_add_darwin_defines(DriverHostedPlan* plan) { + /* __APPLE_CC__ is what the SDK's <TargetConditionals.h> actually keys on: + * its compiler-detection ladder treats us as "GCC/clang on Mac OS X" only + * when __GNUC__ AND one of __APPLE_CC__/__APPLE_CPP__/__MACOS_CLASSIC__ are + * defined. Without it the header can't map __arm64__/__x86_64__ onto a + * TARGET_CPU_* and hits its `#error unknown compiler`. 6000 is the value + * modern Apple clang advertises. */ if (hosted_add_clang_compat_defines(plan) != 0 || hosted_add_define(plan, "__APPLE__", "1") != 0 || + hosted_add_define(plan, "__APPLE_CC__", "6000") != 0 || hosted_add_define(plan, "__MACH__", "1") != 0 || hosted_add_define(plan, "__DYNAMIC__", "1") != 0) return 1; @@ -532,8 +539,8 @@ static const char* hosted_linux_inc_triple_sub(KitArchKind arch) { * layout. Mirrors the per-host probes' shape so both producers converge on the * same resolver contract. Dirs are added unconditionally (existence is checked * when the resolver consumes them); returns nonzero only on alloc/overflow. */ -static int hosted_dirs_from_sysroot(DriverHostedDirs* dirs, KitTarget target, - const char* sysroot) { +static int hosted_dirs_from_sysroot(DriverHostedDirs* dirs, + KitTargetSpec target, const char* sysroot) { dirs->root = sysroot; /* a single explicit sysroot; -print-sysroot reports it */ switch (target.os) { @@ -594,7 +601,7 @@ int driver_hosted_dirs_resolve(const DriverHostedRequest* req, * cross-compile -- a host SDK is meaningless for a foreign platform. * Best-effort: leaves dirs empty when nothing is found, and the resolver * then emits its "requires --sysroot" error. */ - KitTarget host = driver_host_target(); + KitTargetSpec host = driver_host_target(); if (req->target.os == host.os && req->target.arch == host.arch) (void)driver_default_hosted_dirs(req->env, req->target, out); } diff --git a/driver/lib/hosted.h b/driver/lib/hosted.h @@ -46,7 +46,7 @@ typedef struct DriverHostedPlan { typedef struct DriverHostedRequest { DriverEnv* env; const char* tool; - KitTarget target; + KitTargetSpec target; const char* sysroot; int static_link; int link_inputs; diff --git a/driver/lib/runtime.c b/driver/lib/runtime.c @@ -318,7 +318,7 @@ int driver_runtime_append_freestanding_headers(const DriverRuntimeSupport* s, return 0; } -static const RuntimeVariant* rt_variant_for_target(KitTarget target) { +static const RuntimeVariant* rt_variant_for_target(KitTargetSpec target) { uint32_t i; for (i = 0; i < (uint32_t)(sizeof(kRtVariants) / sizeof(kRtVariants[0])); ++i) { @@ -588,12 +588,13 @@ static int rt_build_archive(DriverEnv* env, const DriverRuntimeSupport* support, const RuntimeVariant* variant, const char* tool, uint64_t epoch, const char* archive_path) { KitContext ctx = driver_env_to_context(env); + KitTarget* resolved_target = NULL; KitCompiler* compiler = NULL; KitWriter* archive_writer = NULL; KitArInput* members = NULL; KitWriter** obj_writers = NULL; KitArWriteOptions ar_opts = {0}; - KitTarget target; + KitTargetSpec target; uint32_t i; int rc = 1; @@ -615,9 +616,15 @@ static int rt_build_archive(DriverEnv* env, const DriverRuntimeSupport* support, target.pic = KIT_PIC_NONE; target.code_model = KIT_CM_DEFAULT; - if (driver_compiler_new(target, &ctx, &compiler) != KIT_OK) { - driver_errf(tool, "failed to initialize compiler for runtime"); - goto out; + { + KitTargetOptions topts; + memset(&topts, 0, sizeof topts); + topts.spec = target; + if (kit_target_new(&ctx, &topts, &resolved_target) != KIT_OK || + driver_compiler_new(resolved_target, &ctx, &compiler) != KIT_OK) { + driver_errf(tool, "failed to initialize compiler for runtime"); + goto out; + } } for (i = 0; i < variant->nsources; ++i) { @@ -647,6 +654,7 @@ static int rt_build_archive(DriverEnv* env, const DriverRuntimeSupport* support, out: if (archive_writer) kit_writer_close(archive_writer); if (compiler) driver_compiler_free(compiler); + kit_target_free(resolved_target); if (obj_writers) { for (i = 0; i < variant->nsources; ++i) if (obj_writers[i]) kit_writer_close(obj_writers[i]); @@ -744,7 +752,7 @@ static int rt_archive_try_dir(DriverEnv* env, const char* tool, int driver_runtime_ensure_archive(DriverEnv* env, const char* tool, const DriverRuntimeSupport* support, - KitTarget target, uint64_t epoch, + KitTargetSpec target, uint64_t epoch, char** out_path, size_t* out_path_size) { const RuntimeVariant* variant = rt_variant_for_target(target); const char* diag_tool = tool ? tool : "rt"; @@ -828,7 +836,7 @@ int driver_runtime_ensure_archive(DriverEnv* env, const char* tool, int driver_runtime_prepare_archive(DriverEnv* env, const char* tool, const DriverRuntimeSupport* support, - KitTarget target, uint64_t epoch, + KitTargetSpec target, uint64_t epoch, DriverRuntimeArchive* out) { DriverRuntimeArchive z = {0}; if (!out) return 1; diff --git a/driver/lib/runtime.h b/driver/lib/runtime.h @@ -41,11 +41,11 @@ int driver_runtime_append_freestanding_headers(const DriverRuntimeSupport* s, int driver_runtime_ensure_archive(DriverEnv* env, const char* tool, const DriverRuntimeSupport* support, - KitTarget target, uint64_t epoch, + KitTargetSpec target, uint64_t epoch, char** out_path, size_t* out_path_size); int driver_runtime_prepare_archive(DriverEnv* env, const char* tool, const DriverRuntimeSupport* support, - KitTarget target, uint64_t epoch, + KitTargetSpec target, uint64_t epoch, DriverRuntimeArchive* out); void driver_runtime_archive_fini(DriverEnv* env, DriverRuntimeArchive* a); diff --git a/driver/lib/target.c b/driver/lib/target.c @@ -23,19 +23,216 @@ KitPic driver_default_pic(KitObjFmt obj, KitOSKind os) { return KIT_PIC_PIE; } -int driver_link_pie(KitTarget target, int explicit_pie, int shared, +int driver_link_pie(KitTargetSpec target, int explicit_pie, int shared, int relocatable) { if (explicit_pie) return 1; if (shared || relocatable) return 0; return target.pic == KIT_PIC_PIE; } -int driver_target_from_triple(const char* triple, KitTarget* out) { +static int target_features_grow(DriverTargetFeatures* tf) { + DriverEnv* env = tf->env; + uint32_t old_cap = tf->cap_features; + uint32_t new_cap = old_cap ? old_cap * 2u : 8u; + size_t old_sz = (size_t)old_cap * sizeof(*tf->features); + size_t new_sz = (size_t)new_cap * sizeof(*tf->features); + void* p = env->heap->realloc(env->heap, tf->features, old_sz, new_sz, + _Alignof(KitTargetFeature)); + if (!p) return 1; + tf->features = (KitTargetFeature*)p; + memset(tf->features + old_cap, 0, new_sz - old_sz); + tf->cap_features = new_cap; + return 0; +} + +int driver_target_features_init(DriverTargetFeatures* tf, DriverEnv* env, + int argc_bound) { + size_t bound = argc_bound > 0 ? (size_t)argc_bound + 8u : 8u; + if (!tf || !env || !env->heap) return 1; + memset(tf, 0, sizeof(*tf)); + tf->env = env; + tf->cap_features = (uint32_t)bound; + tf->features = driver_alloc_zeroed(env, bound * sizeof(*tf->features)); + if (!tf->features) return 1; + return 0; +} + +void driver_target_features_fini(DriverTargetFeatures* tf, DriverEnv* env) { + if (!tf || !env) return; + driver_free(env, tf->features, + (size_t)tf->cap_features * sizeof(*tf->features)); + memset(tf, 0, sizeof(*tf)); +} + +static int target_features_record_feature(DriverTargetFeatures* tf, + DriverEnv* env, KitSlice name, + int enabled) { + KitTargetFeature* f; + (void)env; + if (!name.s || name.len == 0) return 1; + if (tf->nfeatures >= tf->cap_features && target_features_grow(tf) != 0) + return 1; + f = &tf->features[tf->nfeatures++]; + f->name = name; + f->enabled = enabled ? true : false; + return 0; +} + +static const char* target_features_pull_value(const char* tool, int argc, + char** argv, int* i, + size_t prefix_len, + const char* flag_label) { + const char* a = argv[*i]; + if (a[prefix_len]) return a + prefix_len; + if (++(*i) >= argc) { + driver_errf(tool, "%.*s requires an argument", + KIT_SLICE_ARG(kit_slice_cstr(flag_label))); + return NULL; + } + return argv[*i]; +} + +static int target_features_parse_mattr(DriverTargetFeatures* tf, DriverEnv* env, + const char* tool, const char* list) { + const char* p = list; + if (!p || !*p) { + driver_errf(tool, "-mattr= requires a comma-separated feature list"); + return 1; + } + while (*p) { + int enabled; + const char* start; + if (*p == '+') { + enabled = 1; + } else if (*p == '-') { + enabled = 0; + } else { + driver_errf(tool, "-mattr entries must start with '+' or '-': %.*s", + KIT_SLICE_ARG(kit_slice_cstr(p))); + return 1; + } + ++p; + start = p; + while (*p && *p != ',') ++p; + if (p == start) { + driver_errf(tool, "empty -mattr feature"); + return 1; + } + if (target_features_record_feature( + tf, env, (KitSlice){.s = start, .len = (size_t)(p - start)}, + enabled) != 0) { + driver_errf(tool, "out of memory"); + return 1; + } + if (*p == ',') ++p; + } + return 0; +} + +int driver_target_features_try_consume(DriverTargetFeatures* tf, DriverEnv* env, + const char* tool, int argc, char** argv, + int* i) { + const char* a = argv[*i]; + const char* v; + if (!tf || !a) return 0; + + if (driver_strneq(a, "-mattr=", 7)) { + return target_features_parse_mattr(tf, env, tool, a + 7) == 0 ? 1 : -1; + } + if (driver_streq(a, "-mattr")) { + if (++(*i) >= argc) { + driver_errf(tool, "-mattr requires an argument"); + return -1; + } + return target_features_parse_mattr(tf, env, tool, argv[*i]) == 0 ? 1 : -1; + } + if (driver_strneq(a, "-mfeature=", 10) || + driver_strneq(a, "-mno-feature=", 13)) { + return 0; + } + if (driver_strneq(a, "-march=", 7)) { + tf->isa = kit_slice_cstr(a + 7); + return 1; + } + if (driver_streq(a, "-march")) { + if (++(*i) >= argc) { + driver_errf(tool, "-march requires an argument"); + return -1; + } + tf->isa = kit_slice_cstr(argv[*i]); + return 1; + } + if (driver_strneq(a, "-mcpu=", 6)) { + tf->cpu = kit_slice_cstr(a + 6); + return 1; + } + if (driver_streq(a, "-mcpu")) { + v = target_features_pull_value(tool, argc, argv, i, 5, "-mcpu"); + if (!v) return -1; + tf->cpu = kit_slice_cstr(v); + return 1; + } + if (driver_strneq(a, "-mtune=", 7)) { + tf->tune = kit_slice_cstr(a + 7); + return 1; + } + if (driver_streq(a, "-mtune")) { + v = target_features_pull_value(tool, argc, argv, i, 6, "-mtune"); + if (!v) return -1; + tf->tune = kit_slice_cstr(v); + return 1; + } + if (driver_strneq(a, "-mno-", 5) && a[5] != '\0') { + if (target_features_record_feature(tf, env, kit_slice_cstr(a + 5), 0) != + 0) { + driver_errf(tool, "out of memory"); + return -1; + } + return 1; + } + if (driver_strneq(a, "-m", 2) && a[2] != '\0') { + if (target_features_record_feature(tf, env, kit_slice_cstr(a + 2), 1) != + 0) { + driver_errf(tool, "out of memory"); + return -1; + } + return 1; + } + return 0; +} + +int driver_target_options(const DriverTargetFeatures* tf, const char* tool, + KitTargetSpec target, KitTargetOptions* out) { + (void)tool; + if (!out) return 1; + memset(out, 0, sizeof(*out)); + out->spec = target; + if (!tf) return 0; + out->isa = tf->isa; + out->cpu = tf->cpu; + out->tune = tf->tune; + out->features = tf->features; + out->nfeatures = tf->nfeatures; + return 0; +} + +KitStatus driver_target_new(const KitContext* ctx, KitTargetSpec target, + const DriverTargetFeatures* tf, const char* tool, + KitTarget** out) { + KitTargetOptions opts; + if (driver_target_options(tf, tool, target, &opts) != 0) { + if (out) *out = NULL; + return KIT_INVALID; + } + return kit_target_new(ctx, &opts, out); +} + +int driver_target_from_triple(const char* triple, KitTargetSpec* out) { const char* parts[4]; size_t plen[4]; int np = 0; const char* p; - KitTarget t; + KitTargetSpec t; int os_set; int i; @@ -137,7 +334,7 @@ int driver_target_from_triple(const char* triple, KitTarget* out) { return 0; } -int driver_target_to_triple(KitTarget target, char* buf, size_t cap) { +int driver_target_to_triple(KitTargetSpec target, char* buf, size_t cap) { const char* arch; const char* os; int n; diff --git a/include/kit/core.h b/include/kit/core.h @@ -25,6 +25,7 @@ /* Opaque handles shared across component headers. */ typedef struct KitCompiler KitCompiler; typedef struct KitCompileSession KitCompileSession; +typedef struct KitTarget KitTarget; typedef struct KitObjBuilder KitObjBuilder; typedef struct KitObjFile KitObjFile; typedef struct KitLinkSession KitLinkSession; @@ -157,7 +158,7 @@ typedef enum KitCodeModel { KIT_CM_LARGE, } KitCodeModel; -typedef struct KitTarget { +typedef struct KitTargetSpec { KitArchKind arch; KitOSKind os; KitObjFmt obj; @@ -166,7 +167,21 @@ typedef struct KitTarget { bool big_endian; uint8_t pic; /* KitPic */ uint8_t code_model; /* KitCodeModel */ -} KitTarget; +} KitTargetSpec; + +typedef struct KitTargetFeature { + KitSlice name; /* canonical target-owned feature name */ + bool enabled; +} KitTargetFeature; + +typedef struct KitTargetOptions { + KitTargetSpec spec; + KitSlice isa; /* optional ISA/profile string, e.g. rv64gc or x86-64-v3 */ + KitSlice cpu; /* optional target CPU/profile name */ + KitSlice tune; /* optional tuning CPU/profile name */ + const KitTargetFeature* features; + uint32_t nfeatures; +} KitTargetOptions; typedef enum KitSymBind { KIT_SB_LOCAL, @@ -314,10 +329,17 @@ typedef struct KitContext { int64_t now; } KitContext; -KIT_API KitStatus kit_compiler_new(KitTarget, const KitContext*, +KIT_API KitStatus kit_target_new(const KitContext*, const KitTargetOptions*, + KitTarget** out); +KIT_API void kit_target_free(KitTarget*); +KIT_API KitTargetSpec kit_target_spec(const KitTarget*); +KIT_API int kit_target_has_feature(const KitTarget*, KitSlice name); + +KIT_API KitStatus kit_compiler_new(const KitTarget*, const KitContext*, KitCompiler** out); KIT_API void kit_compiler_free(KitCompiler*); -KIT_API KitTarget kit_compiler_target(KitCompiler*); +KIT_API const KitTarget* kit_compiler_target(KitCompiler*); +KIT_API KitTargetSpec kit_compiler_target_spec(KitCompiler*); KIT_API const KitContext* kit_compiler_context(KitCompiler*); diff --git a/include/kit/disasm.h b/include/kit/disasm.h @@ -22,7 +22,7 @@ typedef struct KitInsn { typedef struct KitDisasmIter KitDisasmIter; typedef struct KitDisasmContext { - KitTarget target; + const KitTarget* target; KitContext context; } KitDisasmContext; diff --git a/include/kit/emu.h b/include/kit/emu.h @@ -98,7 +98,7 @@ typedef enum KitEmuMode { typedef struct KitEmuOptions { KitSlice guest_name; KitSlice guest_bytes; - KitTarget guest_target; + KitTargetSpec guest_target; bool has_guest_target; const KitJitHost* jit_host; int optimize; diff --git a/include/kit/object.h b/include/kit/object.h @@ -301,14 +301,14 @@ typedef struct KitObjRpathIter KitObjRpathIter; KIT_API KitBinFmt kit_detect_fmt(const uint8_t* data, size_t len); KIT_API KitStatus kit_detect_target(const uint8_t* data, size_t len, - KitTarget* out); + KitTargetSpec* out); KIT_API KitStatus kit_obj_open(const KitContext*, KitSlice name, const KitSlice*, KitObjFile** out); KIT_API void kit_obj_free(KitObjFile*); KIT_API KitObjFmt kit_obj_fmt(const KitObjFile*); -KIT_API KitTarget kit_obj_target(const KitObjFile*); +KIT_API KitTargetSpec kit_obj_target(const KitObjFile*); KIT_API uint32_t kit_obj_nsections(const KitObjFile*); KIT_API KitStatus kit_obj_section(const KitObjFile*, KitObjSection idx, diff --git a/lang/c/abi/c_abi.c b/lang/c/abi/c_abi.c @@ -112,7 +112,7 @@ const ABIFuncInfo* c_abi_func_info(KitCompiler* a, Pool* p, } static const Type* c_size_or_uintptr(KitCompiler* a, Pool* p) { - KitTarget target = kit_compiler_target(a); + KitTargetSpec target = kit_compiler_target_spec(a); return target.ptr_size == 8 ? type_prim(p, TY_ULLONG) : type_prim(p, TY_UINT); } @@ -121,7 +121,7 @@ const Type* c_abi_size_type(KitCompiler* a, Pool* p) { } const Type* c_abi_ptrdiff_type(KitCompiler* a, Pool* p) { - KitTarget target = kit_compiler_target(a); + KitTargetSpec target = kit_compiler_target_spec(a); return target.ptr_size == 8 ? type_prim(p, TY_LLONG) : type_prim(p, TY_INT); } @@ -134,7 +134,7 @@ const Type* c_abi_uintptr_type(KitCompiler* a, Pool* p) { } const Type* c_abi_va_list_type(KitCompiler* a, Pool* p) { - KitTarget target = kit_compiler_target(a); + KitTargetSpec target = kit_compiler_target_spec(a); switch (target.arch) { case KIT_ARCH_X86_64: { const Type* vp = type_ptr(p, type_void(p)); diff --git a/lang/c/parse/parse_expr.c b/lang/c/parse/parse_expr.c @@ -49,7 +49,7 @@ static const Type* ty_char16(Parser* p) { static const Type* ty_char32(Parser* p) { return type_prim(p->pool, TY_UINT); } static const Type* ty_wchar(Parser* p) { - KitTarget target = kit_compiler_target(p->c); + KitTargetSpec target = kit_compiler_target_spec(p->c); return target.os == KIT_OS_WINDOWS ? ty_char16(p) : ty_int(p); } @@ -1276,7 +1276,7 @@ static int parse_builtin_clear_cache_call(Parser* p, Sym name, SrcLoc loc) { * Emitting the __clear_cache libcall there would reference an undefined * symbol. Targets that need explicit coherency (ARM, RISC-V) keep the call. * The argument expressions are still evaluated for their side effects. */ - switch (kit_compiler_target(p->c).arch) { + switch (kit_compiler_target_spec(p->c).arch) { case KIT_ARCH_WASM: case KIT_ARCH_X86_32: case KIT_ARCH_X86_64: diff --git a/lang/c/type/type.c b/lang/c/type/type.c @@ -495,7 +495,7 @@ static int type_is_signed_integer_for_cg(const Type* t) { static KitCgTypeId type_cg_builtin(KitCompiler* c, TypeKind kind) { KitCgBuiltinTypes b = kit_cg_builtin_types(c); - KitTarget target = kit_compiler_target(c); + KitTargetSpec target = kit_compiler_target_spec(c); switch (kind) { case TY_VOID: return b.id[KIT_CG_BUILTIN_VOID]; diff --git a/lang/cpp/cpp_support.h b/lang/cpp/cpp_support.h @@ -84,7 +84,7 @@ _Noreturn static inline void compiler_panicv(Compiler* c, SrcLoc loc, * double to double. Centralized so the preprocessor's __LDBL_* / * __SIZEOF_LONG_DOUBLE__ macros and the C type system's long-double -> * CG-builtin mapping cannot drift apart. */ -static inline int kit_target_long_double_is_binary128(KitTarget t) { +static inline int kit_target_long_double_is_binary128(KitTargetSpec t) { if (t.arch == KIT_ARCH_RV64) return 1; if (t.arch == KIT_ARCH_ARM_64 && t.os == KIT_OS_LINUX) return 1; if (t.arch == KIT_ARCH_WASM) return 1; diff --git a/lang/cpp/pp/pp.c b/lang/cpp/pp/pp.c @@ -344,7 +344,7 @@ static void pp_register_static_predefined(Pp* pp) { * pointer width plus the target data model: LP64 for Unix-like 64-bit targets, * LLP64 for 64-bit Windows, and ILP32 for 32-bit targets. */ static void pp_register_target_predefined(Pp* pp) { - KitTarget target = kit_compiler_target(pp->c); + KitTargetSpec target = kit_compiler_target_spec(pp->c); const KitPredefinedMacro* arch_defs = NULL; uint32_t narch_defs = kit_compiler_arch_predefines(pp->c, &arch_defs); uint32_t i; diff --git a/lang/toy/internal.h b/lang/toy/internal.h @@ -237,7 +237,7 @@ typedef struct ToyParser { KitCgTypeId int_type; KitCgTypeId int_ptr_type; KitCgTypeId va_list_type; - KitTarget target; + KitTargetSpec target; /* Durable cross-snippet state lives in the module; the parser borrows it * for one compile and stages/commits/rolls back through it. */ diff --git a/lang/toy/parser_core.c b/lang/toy/parser_core.c @@ -62,7 +62,7 @@ void toy_parser_init(ToyParser* p, KitCompiler* c, KitCg* cg, ToyModule* module, p->int_type = toy_builtin_type(p, KIT_CG_BUILTIN_I64); p->int_ptr_type = kit_cg_type_ptr(c, p->int_type, 0); p->va_list_type = toy_builtin_type(p, KIT_CG_BUILTIN_VARARG_STATE); - p->target = kit_compiler_target(c); + p->target = kit_compiler_target_spec(c); p->nvars = 0; p->cap_vars = 0; p->vars = NULL; @@ -103,7 +103,7 @@ void toy_parser_reinit(ToyParser* p, KitCompiler* c, KitCg* cg, p->int_type = toy_builtin_type(p, KIT_CG_BUILTIN_I64); p->int_ptr_type = kit_cg_type_ptr(c, p->int_type, 0); p->va_list_type = toy_builtin_type(p, KIT_CG_BUILTIN_VARARG_STATE); - p->target = kit_compiler_target(c); + p->target = kit_compiler_target_spec(c); p->nvars = 0; p->nscopes = 0; p->nlabels = 0; diff --git a/src/api/core.c b/src/api/core.c @@ -3,23 +3,149 @@ #include "core/core.h" #include <kit/core.h> +#include <stdarg.h> #include <string.h> +#include "arch/arch.h" #include "core/heap.h" #include "core/pool.h" #include "core/slice.h" -KitStatus kit_compiler_new(KitTarget target, const KitContext* ctx, +static void target_diag(const KitContext* ctx, const char* fmt, ...) { + va_list ap; + KitSrcLoc loc; + if (!ctx || !ctx->diag || !ctx->diag->emit) return; + loc.file_id = 0; + loc.line = 0; + loc.col = 0; + va_start(ap, fmt); + ctx->diag->emit(ctx->diag, KIT_DIAG_ERROR, loc, fmt, ap); + va_end(ap); +} + +static void bitset_set(u64* words, u32 nwords, u32 idx, int enabled) { + u32 w = idx / 64u; + u64 bit = 1ull << (idx % 64u); + if (!words || w >= nwords) return; + if (enabled) + words[w] |= bit; + else + words[w] &= ~bit; +} + +static int bitset_get(const u64* words, u32 nwords, u32 idx) { + u32 w = idx / 64u; + if (!words || w >= nwords) return 0; + return (words[w] & (1ull << (idx % 64u))) != 0; +} + +KitStatus kit_target_new(const KitContext* ctx, const KitTargetOptions* opts, + KitTarget** out) { + const ArchImpl* arch; + KitTarget* t; + Heap* h; + u32 nwords; + u32 i; + + if (!out) return KIT_INVALID; + *out = NULL; + if (!ctx || !ctx->heap || !opts) return KIT_INVALID; + arch = arch_lookup(opts->spec.arch); + if (!arch) { + target_diag(ctx, "unsupported target architecture: %u", + (unsigned)opts->spec.arch); + return KIT_UNSUPPORTED; + } + + h = ctx->heap; + t = (KitTarget*)h->alloc(h, sizeof(*t), _Alignof(KitTarget)); + if (!t) return KIT_NOMEM; + memset(t, 0, sizeof(*t)); + t->ctx = ctx; + t->spec = opts->spec; + + nwords = (arch->ntarget_features + 63u) / 64u; + if (nwords) { + t->feature_words = + (u64*)h->alloc(h, sizeof(*t->feature_words) * nwords, _Alignof(u64)); + if (!t->feature_words) { + h->free(h, t, sizeof(*t)); + return KIT_NOMEM; + } + memset(t->feature_words, 0, sizeof(*t->feature_words) * nwords); + t->nfeature_words = nwords; + arch_target_feature_defaults(arch, &t->spec, t->feature_words, nwords); + } + + if (opts->isa.s && opts->isa.len) { + KitStatus st = arch_target_feature_apply_isa( + arch, &t->spec, opts->isa, t->feature_words, t->nfeature_words); + if (st != KIT_OK) { + target_diag(ctx, "unsupported ISA/profile for %s: %.*s", arch->name, + KIT_SLICE_ARG(opts->isa)); + kit_target_free(t); + return st == KIT_UNSUPPORTED ? KIT_INVALID : st; + } + } + + for (i = 0; i < opts->nfeatures; ++i) { + u32 idx; + KitSlice name = opts->features[i].name; + if (!arch_target_feature_index(arch, name, &idx)) { + target_diag(ctx, "unknown target feature for %s: %.*s", arch->name, + KIT_SLICE_ARG(name)); + kit_target_free(t); + return KIT_INVALID; + } + bitset_set(t->feature_words, t->nfeature_words, idx, + opts->features[i].enabled); + } + + *out = t; + return KIT_OK; +} + +void kit_target_free(KitTarget* t) { + Heap* h; + if (!t) return; + h = t->ctx ? t->ctx->heap : NULL; + if (!h) return; + if (t->feature_words) + h->free(h, t->feature_words, sizeof(*t->feature_words) * t->nfeature_words); + h->free(h, t, sizeof(*t)); +} + +KitTargetSpec kit_target_spec(const KitTarget* t) { + KitTargetSpec spec; + memset(&spec, 0, sizeof spec); + return t ? t->spec : spec; +} + +int kit_target_has_feature(const KitTarget* t, KitSlice name) { + const ArchImpl* arch; + u32 idx; + if (!t) return 0; + arch = arch_lookup(t->spec.arch); + if (!arch_target_feature_index(arch, name, &idx)) return 0; + return bitset_get(t->feature_words, t->nfeature_words, idx); +} + +KitStatus kit_compiler_new(const KitTarget* target, const KitContext* ctx, KitCompiler** out) { Heap* h; Compiler* c; + KitStatus st; if (!out) return KIT_INVALID; - if (!ctx || !ctx->heap) return KIT_INVALID; + if (!target || !ctx || !ctx->heap) return KIT_INVALID; h = ctx->heap; c = h->alloc(h, sizeof(*c), _Alignof(Compiler)); if (!c) return KIT_NOMEM; - compiler_init(c, target, ctx); + st = compiler_init(c, target, ctx); + if (st != KIT_OK) { + h->free(h, c, sizeof(*c)); + return st; + } *out = c; return KIT_OK; } @@ -32,8 +158,12 @@ void kit_compiler_free(KitCompiler* c) { h->free(h, c, sizeof(*c)); } -KitTarget kit_compiler_target(KitCompiler* c) { - KitTarget t; +const KitTarget* kit_compiler_target(KitCompiler* c) { + return c ? c->target_ref : NULL; +} + +KitTargetSpec kit_compiler_target_spec(KitCompiler* c) { + KitTargetSpec t; memset(&t, 0, sizeof t); if (!c) return t; return c->target; diff --git a/src/api/disasm.c b/src/api/disasm.c @@ -110,7 +110,17 @@ KitStatus kit_disasm_iter_new(const KitDisasmContext* dctx, memset(it, 0, sizeof(*it)); it->ctx = &dctx->context; it->heap = h; - compiler_init(&it->compiler, dctx->target, &dctx->context); + if (!dctx->target) { + h->free(h, it, sizeof(*it)); + return KIT_INVALID; + } + { + KitStatus st = compiler_init(&it->compiler, dctx->target, &dctx->context); + if (st != KIT_OK) { + h->free(h, it, sizeof(*it)); + return st; + } + } it->arch = arch_disasm_new(&it->compiler); if (!it->arch) { compiler_fini(&it->compiler); @@ -229,8 +239,15 @@ KitStatus kit_disasm_obj(const KitContext* ctx, const KitObjFile* f, KitWriter* out) { uint32_t nsec, i; KitDisasmContext dctx; + KitTarget* target = NULL; + KitTargetOptions topts; + KitStatus st; if (!ctx || !f || !out) return KIT_INVALID; - dctx.target = kit_obj_target(f); + memset(&topts, 0, sizeof topts); + topts.spec = kit_obj_target(f); + st = kit_target_new(ctx, &topts, &target); + if (st != KIT_OK) return st; + dctx.target = target; dctx.context = *ctx; nsec = kit_obj_nsections(f); for (i = 0; i < nsec; ++i) { @@ -238,7 +255,6 @@ KitStatus kit_disasm_obj(const KitContext* ctx, const KitObjFile* f, const uint8_t* data = NULL; size_t n = 0; KitDisasmIter* it = NULL; - KitStatus st; Slice head; if (kit_obj_section(f, i, &s) != KIT_OK) continue; @@ -251,7 +267,10 @@ KitStatus kit_disasm_obj(const KitContext* ctx, const KitObjFile* f, w_str(out, ":\n\n"); st = kit_disasm_iter_new(&dctx, data, n, 0, f, &it); - if (st != KIT_OK) return st; + if (st != KIT_OK) { + kit_target_free(target); + return st; + } for (;;) { KitInsn ins; KitIterResult r = kit_disasm_iter_next(it, &ins); @@ -291,7 +310,9 @@ KitStatus kit_disasm_obj(const KitContext* ctx, const KitObjFile* f, } kit_disasm_iter_free(it); } - return kit_writer_status(out); + st = kit_writer_status(out); + kit_target_free(target); + return st; } KitStatus kit_disasm_obj_bytes(const KitContext* ctx, const KitSlice* bytes, diff --git a/src/api/object_detect.c b/src/api/object_detect.c @@ -77,13 +77,13 @@ KitBinFmt kit_detect_fmt(const uint8_t* data, size_t len) { return KIT_BIN_UNKNOWN; } -static void detect_target_defaults(KitTarget* t) { +static void detect_target_defaults(KitTargetSpec* t) { t->big_endian = 0; t->pic = KIT_PIC_NONE; t->code_model = KIT_CM_DEFAULT; } -static void detect_set_ptr(KitTarget* t, KitArchKind arch) { +static void detect_set_ptr(KitTargetSpec* t, KitArchKind arch) { t->arch = arch; switch (arch) { case KIT_ARCH_X86_64: @@ -102,7 +102,7 @@ static void detect_set_ptr(KitTarget* t, KitArchKind arch) { } } -static KitStatus detect_elf(const u8* d, size_t len, KitTarget* out) { +static KitStatus detect_elf(const u8* d, size_t len, KitTargetSpec* out) { u8 ei_class, ei_data, ei_osabi; u16 e_machine; if (len < 20) return KIT_MALFORMED; @@ -152,7 +152,7 @@ static KitStatus detect_elf(const u8* d, size_t len, KitTarget* out) { return KIT_OK; } -static KitStatus detect_coff(const u8* d, size_t len, KitTarget* out) { +static KitStatus detect_coff(const u8* d, size_t len, KitTargetSpec* out) { u16 machine; if (len < 2) return KIT_MALFORMED; machine = (u16)d[0] | ((u16)d[1] << 8); @@ -184,7 +184,7 @@ static KitStatus detect_coff(const u8* d, size_t len, KitTarget* out) { return KIT_OK; } -static KitStatus detect_macho(const u8* d, size_t len, KitTarget* out) { +static KitStatus detect_macho(const u8* d, size_t len, KitTargetSpec* out) { u32 magic, cputype; int swap, is64; if (len < 8) return KIT_MALFORMED; @@ -239,7 +239,8 @@ static KitStatus detect_macho(const u8* d, size_t len, KitTarget* out) { return KIT_OK; } -KitStatus kit_detect_target(const uint8_t* data, size_t len, KitTarget* out) { +KitStatus kit_detect_target(const uint8_t* data, size_t len, + KitTargetSpec* out) { KitBinFmt bin; if (!data || !out) return KIT_INVALID; bin = kit_detect_fmt(data, len); @@ -258,7 +259,7 @@ KitStatus kit_detect_target(const uint8_t* data, size_t len, KitTarget* out) { #endif #if KIT_OBJ_WASM_ENABLED case KIT_BIN_WASM: { - KitTarget t; + KitTargetSpec t; t.big_endian = 0; t.pic = KIT_PIC_NONE; t.code_model = KIT_CM_DEFAULT; diff --git a/src/api/object_file.c b/src/api/object_file.c @@ -17,9 +17,10 @@ struct KitObjFile { Compiler compiler; const KitContext* ctx; + KitTarget* resolved_target; ObjBuilder* ob; ObjFmt fmt; - KitTarget target; + KitTargetSpec target; const u8** sec_data_cache; u32* sec_data_size; u32 sec_data_n; @@ -30,7 +31,7 @@ KitStatus kit_obj_open(const KitContext* ctx, KitSlice name, Heap* h; KitObjFile* f; const ObjFormatImpl* impl; - KitTarget target; + KitTargetSpec target; KitStatus st; if (!out) return KIT_INVALID; @@ -51,16 +52,33 @@ KitStatus kit_obj_open(const KitContext* ctx, KitSlice name, f->fmt = target.obj; f->target = target; - compiler_init(&f->compiler, target, ctx); + { + KitTargetOptions topts; + memset(&topts, 0, sizeof topts); + topts.spec = target; + st = kit_target_new(ctx, &topts, &f->resolved_target); + if (st != KIT_OK) { + h->free(h, f, sizeof(*f)); + return st; + } + } + st = compiler_init(&f->compiler, f->resolved_target, ctx); + if (st != KIT_OK) { + kit_target_free(f->resolved_target); + h->free(h, f, sizeof(*f)); + return st; + } if (setjmp(f->compiler.panic)) { compiler_run_cleanups(&f->compiler); compiler_fini(&f->compiler); + kit_target_free(f->resolved_target); h->free(h, f, sizeof(*f)); return KIT_MALFORMED; } f->ob = impl->read(&f->compiler, name.s, input->data, input->len); if (!f->ob) { compiler_fini(&f->compiler); + kit_target_free(f->resolved_target); h->free(h, f, sizeof(*f)); return KIT_MALFORMED; } @@ -84,12 +102,13 @@ void kit_obj_free(KitObjFile* f) { } if (f->ob) obj_free(f->ob); compiler_fini(&f->compiler); + kit_target_free(f->resolved_target); h->free(h, f, sizeof(*f)); } KitObjFmt kit_obj_fmt(const KitObjFile* f) { return f->fmt; } -KitTarget kit_obj_target(const KitObjFile* f) { return f->target; } +KitTargetSpec kit_obj_target(const KitObjFile* f) { return f->target; } uint32_t kit_obj_nsections(const KitObjFile* f) { return obj_section_count(f->ob); @@ -555,8 +574,8 @@ KitObjBuilder* kit_obj_file_builder(const KitObjFile* f) { * kit_obj_free path is keyed off obj_read_bytes, so the view path uses * its own teardown that does not depend on the cached section data * tables. */ -KitObjFile* kit_objfile_internal_new(const KitContext* ctx, KitTarget target, - KitObjFmt fmt) { +KitObjFile* kit_objfile_internal_new(const KitContext* ctx, + KitTargetSpec target, KitObjFmt fmt) { Heap* h; KitObjFile* f; if (!ctx || !ctx->heap) return NULL; @@ -567,16 +586,34 @@ KitObjFile* kit_objfile_internal_new(const KitContext* ctx, KitTarget target, f->ctx = ctx; f->fmt = fmt; f->target = target; - compiler_init(&f->compiler, target, ctx); + { + KitTargetOptions topts; + KitStatus st; + memset(&topts, 0, sizeof topts); + topts.spec = target; + st = kit_target_new(ctx, &topts, &f->resolved_target); + if (st != KIT_OK) { + h->free(h, f, sizeof(*f)); + return NULL; + } + st = compiler_init(&f->compiler, f->resolved_target, ctx); + if (st != KIT_OK) { + kit_target_free(f->resolved_target); + h->free(h, f, sizeof(*f)); + return NULL; + } + } if (setjmp(f->compiler.panic)) { compiler_run_cleanups(&f->compiler); compiler_fini(&f->compiler); + kit_target_free(f->resolved_target); h->free(h, f, sizeof(*f)); return NULL; } f->ob = obj_new(&f->compiler); if (!f->ob) { compiler_fini(&f->compiler); + kit_target_free(f->resolved_target); h->free(h, f, sizeof(*f)); return NULL; } diff --git a/src/arch/aa64/arch.c b/src/arch/aa64/arch.c @@ -154,6 +154,24 @@ static const KitPredefinedMacro aa64_predefined_macros[] = { {KIT_SLICE_LIT("__LITTLE_ENDIAN__"), KIT_SLICE_LIT("1")}, }; +static const ArchTargetFeature aa64_target_features[] = { + {"bti"}, + {"pac-ret"}, + {"lse"}, +}; + +static KitStatus aa64_target_feature_apply_isa(const Target* target, + KitSlice isa, u64* words, + u32 nwords) { + (void)target; + (void)words; + (void)nwords; + if (kit_slice_eq_cstr(isa, "aarch64") || kit_slice_eq_cstr(isa, "arm64") || + kit_slice_eq_cstr(isa, "armv8-a")) + return KIT_OK; + return KIT_UNSUPPORTED; +} + const ArchImpl arch_impl_aa64 = { .backend = {.name = "aa64", .make = aa64_backend_make}, .kind = KIT_ARCH_ARM_64, @@ -169,6 +187,10 @@ const ArchImpl arch_impl_aa64 = { .predefined_macros = aa64_predefined_macros, .npredefined_macros = (u32)(sizeof aa64_predefined_macros / sizeof aa64_predefined_macros[0]), + .target_features = aa64_target_features, + .ntarget_features = + (u32)(sizeof aa64_target_features / sizeof aa64_target_features[0]), + .target_feature_apply_isa = aa64_target_feature_apply_isa, .register_name = aa64_register_name, .register_index = aa64_register_index, .register_count = aa64_register_iter_size, diff --git a/src/arch/arch.h b/src/arch/arch.h @@ -143,6 +143,10 @@ typedef struct ArchDwarfOps { u8 pad[2]; } ArchDwarfOps; +typedef struct ArchTargetFeature { + const char* name; +} ArchTargetFeature; + #define ARCH_DBG_MAX_TRAP_BYTES 8u #define ARCH_DBG_MAX_INSN_BYTES 15u @@ -269,6 +273,11 @@ typedef struct ArchImpl { const KitPredefinedMacro* predefined_macros; u32 npredefined_macros; + const ArchTargetFeature* target_features; + u32 ntarget_features; + void (*target_feature_defaults)(const Target*, u64* words, u32 nwords); + KitStatus (*target_feature_apply_isa)(const Target*, KitSlice isa, u64* words, + u32 nwords); const char* (*register_name)(uint32_t dwarf_idx); int (*register_index)(const char* name, uint32_t* idx_out); @@ -288,6 +297,11 @@ typedef struct ArchImpl { const ArchImpl* arch_lookup(KitArchKind); const ArchImpl* arch_for_compiler(const Compiler*); +int arch_target_feature_index(const ArchImpl*, KitSlice name, u32* idx_out); +void arch_target_feature_defaults(const ArchImpl*, const Target*, u64* words, + u32 nwords); +KitStatus arch_target_feature_apply_isa(const ArchImpl*, const Target*, + KitSlice isa, u64* words, u32 nwords); /* Spelling for a relocated operand in `cc -S` text, for the compiler's target * arch+format. Returns 1 and fills *out when symbolizable, 0 to keep numeric diff --git a/src/arch/registry.c b/src/arch/registry.c @@ -95,6 +95,34 @@ const ArchImpl* arch_for_compiler(const Compiler* c) { return arch_lookup(c->target.arch); } +int arch_target_feature_index(const ArchImpl* a, KitSlice name, u32* idx_out) { + u32 i; + if (!a || !name.s) return 0; + for (i = 0; i < a->ntarget_features; ++i) { + if (kit_slice_eq_cstr(name, a->target_features[i].name)) { + if (idx_out) *idx_out = i; + return 1; + } + } + return 0; +} + +void arch_target_feature_defaults(const ArchImpl* a, const Target* target, + u64* words, u32 nwords) { + if (!a || !words || nwords == 0) return; + if (a->target_feature_defaults) { + a->target_feature_defaults(target, words, nwords); + } +} + +KitStatus arch_target_feature_apply_isa(const ArchImpl* a, const Target* target, + KitSlice isa, u64* words, u32 nwords) { + if (!a || !isa.s || isa.len == 0) return KIT_OK; + if (!words && nwords != 0) return KIT_INVALID; + if (!a->target_feature_apply_isa) return KIT_UNSUPPORTED; + return a->target_feature_apply_isa(target, isa, words, nwords); +} + int arch_reloc_operand(const Compiler* c, u16 reloc_kind, ArchRelocOperand* out) { const ArchImpl* a = arch_for_compiler(c); diff --git a/src/arch/rv64/arch.c b/src/arch/rv64/arch.c @@ -1,5 +1,7 @@ #include "arch/arch.h" +#include <string.h> + #include "arch/rv64/asm.h" #include "arch/rv64/disasm.h" #include "arch/rv64/regs.h" @@ -136,6 +138,147 @@ static const KitPredefinedMacro rv64_predefined_macros[] = { {KIT_SLICE_LIT("__LITTLE_ENDIAN__"), KIT_SLICE_LIT("1")}, }; +enum { + RV64_FEAT_I = 0, + RV64_FEAT_M, + RV64_FEAT_A, + RV64_FEAT_F, + RV64_FEAT_D, + RV64_FEAT_C, + RV64_FEAT_ZICSR, + RV64_FEAT_ZIFENCEI, +}; + +static const ArchTargetFeature rv64_target_features[] = { + {"i"}, {"m"}, {"a"}, {"f"}, {"d"}, {"c"}, {"zicsr"}, {"zifencei"}, +}; + +static void rv64_feature_set(u64* words, u32 nwords, u32 idx) { + if (!words || idx / 64u >= nwords) return; + words[idx / 64u] |= 1ull << (idx % 64u); +} + +static void rv64_feature_clear(u64* words, u32 nwords, u32 idx) { + if (!words || idx / 64u >= nwords) return; + words[idx / 64u] &= ~(1ull << (idx % 64u)); +} + +static void rv64_feature_disable_all(u64* words, u32 nwords) { + rv64_feature_clear(words, nwords, RV64_FEAT_I); + rv64_feature_clear(words, nwords, RV64_FEAT_M); + rv64_feature_clear(words, nwords, RV64_FEAT_A); + rv64_feature_clear(words, nwords, RV64_FEAT_F); + rv64_feature_clear(words, nwords, RV64_FEAT_D); + rv64_feature_clear(words, nwords, RV64_FEAT_C); + rv64_feature_clear(words, nwords, RV64_FEAT_ZICSR); + rv64_feature_clear(words, nwords, RV64_FEAT_ZIFENCEI); +} + +static void rv64_feature_enable_g(u64* words, u32 nwords) { + rv64_feature_set(words, nwords, RV64_FEAT_I); + rv64_feature_set(words, nwords, RV64_FEAT_M); + rv64_feature_set(words, nwords, RV64_FEAT_A); + rv64_feature_set(words, nwords, RV64_FEAT_F); + rv64_feature_set(words, nwords, RV64_FEAT_D); + rv64_feature_set(words, nwords, RV64_FEAT_ZICSR); + rv64_feature_set(words, nwords, RV64_FEAT_ZIFENCEI); +} + +static int rv64_has_prefix(const char* p, const char* end, const char* lit) { + size_t n = strlen(lit); + return (size_t)(end - p) >= n && memcmp(p, lit, n) == 0; +} + +static void rv64_skip_version(const char** pp, const char* end) { + const char* p = *pp; + while (p < end && ((*p >= '0' && *p <= '9') || *p == 'p')) ++p; + *pp = p; +} + +static KitStatus rv64_target_feature_apply_isa(const Target* target, + KitSlice isa, u64* words, + u32 nwords) { + const char* p; + const char* end; + (void)target; + if (isa.len < 5 || memcmp(isa.s, "rv64", 4) != 0) return KIT_UNSUPPORTED; + p = isa.s + 4; + end = isa.s + isa.len; + rv64_feature_disable_all(words, nwords); + while (p < end) { + if (*p == '_') { + ++p; + continue; + } + switch (*p) { + case 'i': + rv64_feature_set(words, nwords, RV64_FEAT_I); + ++p; + rv64_skip_version(&p, end); + continue; + case 'm': + rv64_feature_set(words, nwords, RV64_FEAT_M); + ++p; + rv64_skip_version(&p, end); + continue; + case 'a': + rv64_feature_set(words, nwords, RV64_FEAT_A); + ++p; + rv64_skip_version(&p, end); + continue; + case 'f': + rv64_feature_set(words, nwords, RV64_FEAT_F); + ++p; + rv64_skip_version(&p, end); + continue; + case 'd': + rv64_feature_set(words, nwords, RV64_FEAT_D); + ++p; + rv64_skip_version(&p, end); + continue; + case 'c': + rv64_feature_set(words, nwords, RV64_FEAT_C); + ++p; + rv64_skip_version(&p, end); + continue; + case 'g': + rv64_feature_enable_g(words, nwords); + ++p; + rv64_skip_version(&p, end); + continue; + case 'z': + if (rv64_has_prefix(p, end, "zicsr")) { + rv64_feature_set(words, nwords, RV64_FEAT_ZICSR); + p += 5; + rv64_skip_version(&p, end); + continue; + } + if (rv64_has_prefix(p, end, "zifencei")) { + rv64_feature_set(words, nwords, RV64_FEAT_ZIFENCEI); + p += 8; + rv64_skip_version(&p, end); + continue; + } + break; + } + return KIT_UNSUPPORTED; + } + return KIT_OK; +} + +static void rv64_target_feature_defaults(const Target* target, u64* words, + u32 nwords) { + (void)target; + rv64_feature_set(words, nwords, RV64_FEAT_I); + rv64_feature_set(words, nwords, RV64_FEAT_M); + rv64_feature_set(words, nwords, RV64_FEAT_A); + rv64_feature_set(words, nwords, RV64_FEAT_F); + rv64_feature_set(words, nwords, RV64_FEAT_D); + rv64_feature_set(words, nwords, RV64_FEAT_C); + rv64_feature_set(words, nwords, RV64_FEAT_ZICSR); + rv64_feature_set(words, nwords, RV64_FEAT_ZIFENCEI); +} + static CgTarget* rv64_backend_make(Compiler* c, ObjBuilder* o, const KitCodeOptions* opts) { MCEmitter* mc = NULL; @@ -184,6 +327,11 @@ const ArchImpl arch_impl_rv64 = { .predefined_macros = rv64_predefined_macros, .npredefined_macros = (u32)(sizeof rv64_predefined_macros / sizeof rv64_predefined_macros[0]), + .target_features = rv64_target_features, + .ntarget_features = + (u32)(sizeof rv64_target_features / sizeof rv64_target_features[0]), + .target_feature_defaults = rv64_target_feature_defaults, + .target_feature_apply_isa = rv64_target_feature_apply_isa, .register_name = rv64_register_name, .register_index = rv64_register_index, .register_count = rv64_register_iter_size, diff --git a/src/arch/wasm/arch.c b/src/arch/wasm/arch.c @@ -22,6 +22,49 @@ static const KitPredefinedMacro wasm_predefined_macros[] = { {KIT_SLICE_LIT("__LITTLE_ENDIAN__"), KIT_SLICE_LIT("1")}, }; +enum { + WASM_TFEAT_THREADS = 0, + WASM_TFEAT_TYPED_FUNC_REFS, + WASM_TFEAT_TAIL_CALLS, + WASM_TFEAT_MULTI_MEMORY, + WASM_TFEAT_MEMORY64, + WASM_TFEAT_BULK_MEMORY, + WASM_TFEAT_NONTRAPPING_FTOI, +}; + +static const ArchTargetFeature wasm_target_features[] = { + {"threads"}, {"typed-func-refs"}, {"tail-calls"}, {"multi-memory"}, + {"memory64"}, {"bulk-memory"}, {"nontrapping-ftoi"}, +}; + +static void wasm_feature_set(u64* words, u32 nwords, u32 idx) { + if (!words || idx / 64u >= nwords) return; + words[idx / 64u] |= 1ull << (idx % 64u); +} + +static KitStatus wasm_target_feature_apply_isa(const Target* target, + KitSlice isa, u64* words, + u32 nwords) { + (void)target; + (void)words; + (void)nwords; + if (kit_slice_eq_cstr(isa, "wasm32") || kit_slice_eq_cstr(isa, "wasm64")) + return KIT_OK; + return KIT_UNSUPPORTED; +} + +static void wasm_target_feature_defaults(const Target* target, u64* words, + u32 nwords) { + (void)target; + wasm_feature_set(words, nwords, WASM_TFEAT_THREADS); + wasm_feature_set(words, nwords, WASM_TFEAT_TYPED_FUNC_REFS); + wasm_feature_set(words, nwords, WASM_TFEAT_TAIL_CALLS); + wasm_feature_set(words, nwords, WASM_TFEAT_MULTI_MEMORY); + wasm_feature_set(words, nwords, WASM_TFEAT_MEMORY64); + wasm_feature_set(words, nwords, WASM_TFEAT_BULK_MEMORY); + wasm_feature_set(words, nwords, WASM_TFEAT_NONTRAPPING_FTOI); +} + static CGTarget* wasm_backend_make(Compiler* c, ObjBuilder* o, const KitCodeOptions* opts) { (void)opts; @@ -40,6 +83,11 @@ const ArchImpl arch_impl_wasm = { .predefined_macros = wasm_predefined_macros, .npredefined_macros = (u32)(sizeof wasm_predefined_macros / sizeof wasm_predefined_macros[0]), + .target_features = wasm_target_features, + .ntarget_features = + (u32)(sizeof wasm_target_features / sizeof wasm_target_features[0]), + .target_feature_defaults = wasm_target_feature_defaults, + .target_feature_apply_isa = wasm_target_feature_apply_isa, .register_name = NULL, .register_index = NULL, .register_count = NULL, diff --git a/src/arch/x64/arch.c b/src/arch/x64/arch.c @@ -39,6 +39,68 @@ static const KitPredefinedMacro x64_predefined_macros[] = { {KIT_SLICE_LIT("__LITTLE_ENDIAN__"), KIT_SLICE_LIT("1")}, }; +enum { + X64_FEAT_SSE = 0, + X64_FEAT_SSE2, + X64_FEAT_SSE3, + X64_FEAT_SSSE3, + X64_FEAT_SSE41, + X64_FEAT_SSE42, + X64_FEAT_AVX, + X64_FEAT_AVX2, +}; + +static const ArchTargetFeature x64_target_features[] = { + {"sse"}, {"sse2"}, {"sse3"}, {"ssse3"}, + {"sse4.1"}, {"sse4.2"}, {"avx"}, {"avx2"}, +}; + +static void x64_feature_set(u64* words, u32 nwords, u32 idx) { + if (!words || idx / 64u >= nwords) return; + words[idx / 64u] |= 1ull << (idx % 64u); +} + +static KitStatus x64_target_feature_apply_isa(const Target* target, + KitSlice isa, u64* words, + u32 nwords) { + (void)target; + if (kit_slice_eq_cstr(isa, "x86-64") || kit_slice_eq_cstr(isa, "x86-64-v1")) { + x64_feature_set(words, nwords, X64_FEAT_SSE); + x64_feature_set(words, nwords, X64_FEAT_SSE2); + return KIT_OK; + } + if (kit_slice_eq_cstr(isa, "x86-64-v2")) { + x64_feature_set(words, nwords, X64_FEAT_SSE); + x64_feature_set(words, nwords, X64_FEAT_SSE2); + x64_feature_set(words, nwords, X64_FEAT_SSE3); + x64_feature_set(words, nwords, X64_FEAT_SSSE3); + x64_feature_set(words, nwords, X64_FEAT_SSE41); + x64_feature_set(words, nwords, X64_FEAT_SSE42); + return KIT_OK; + } + if (kit_slice_eq_cstr(isa, "x86-64-v3") || + kit_slice_eq_cstr(isa, "x86-64-v4") || + kit_slice_eq_cstr(isa, "haswell") || kit_slice_eq_cstr(isa, "skylake")) { + x64_feature_set(words, nwords, X64_FEAT_SSE); + x64_feature_set(words, nwords, X64_FEAT_SSE2); + x64_feature_set(words, nwords, X64_FEAT_SSE3); + x64_feature_set(words, nwords, X64_FEAT_SSSE3); + x64_feature_set(words, nwords, X64_FEAT_SSE41); + x64_feature_set(words, nwords, X64_FEAT_SSE42); + x64_feature_set(words, nwords, X64_FEAT_AVX); + x64_feature_set(words, nwords, X64_FEAT_AVX2); + return KIT_OK; + } + return KIT_UNSUPPORTED; +} + +static void x64_target_feature_defaults(const Target* target, u64* words, + u32 nwords) { + (void)target; + x64_feature_set(words, nwords, X64_FEAT_SSE); + x64_feature_set(words, nwords, X64_FEAT_SSE2); +} + static int x64_register_at_public(uint32_t idx, KitArchReg* out) { const char* nm = NULL; int rc; @@ -94,6 +156,11 @@ const ArchImpl arch_impl_x64 = { .predefined_macros = x64_predefined_macros, .npredefined_macros = (u32)(sizeof x64_predefined_macros / sizeof x64_predefined_macros[0]), + .target_features = x64_target_features, + .ntarget_features = + (u32)(sizeof x64_target_features / sizeof x64_target_features[0]), + .target_feature_defaults = x64_target_feature_defaults, + .target_feature_apply_isa = x64_target_feature_apply_isa, .register_name = x64_register_name, .register_index = x64_register_index, .register_count = x64_register_iter_size, diff --git a/src/cg/session.c b/src/cg/session.c @@ -120,7 +120,7 @@ KitStatus kit_cg_begin_obj(KitCg* g, KitObjBuilder* out, compiler_panic((Compiler*)c, api_no_loc(), "KitCg: no code generator for target arch '%s' — no " "backend enabled in this build (KIT_ARCH_*_ENABLED)", - arch_kind_name(kit_compiler_target(c).arch)); + arch_kind_name(kit_compiler_target_spec(c).arch)); } target = backend->make((Compiler*)c, (ObjBuilder*)out, opts); if (!target) return KIT_UNSUPPORTED; diff --git a/src/core/core.c b/src/core/core.c @@ -64,29 +64,42 @@ struct CompilerCleanup { CompilerCleanup* prev; }; -void compiler_init(Compiler* c, Target target, const KitContext* ctx) { +KitStatus compiler_init(Compiler* c, const KitTarget* target, + const KitContext* ctx) { Heap* h = ctx->heap; + if (!c || !target || !ctx || !h) return KIT_INVALID; memset(c, 0, sizeof(*c)); c->ctx = ctx; - c->target = target; + c->target_ref = target; + c->target = target->spec; c->global = (Pool*)h->alloc(h, sizeof(Pool), _Alignof(Pool)); + if (!c->global) goto nomem; pool_init(c->global, h); c->tu = (Arena*)h->alloc(h, sizeof(Arena), _Alignof(Arena)); + if (!c->tu) goto nomem; arena_init(c->tu, h, 0); c->scratch = (Arena*)h->alloc(h, sizeof(Arena), _Alignof(Arena)); + if (!c->scratch) goto nomem; arena_init(c->scratch, h, 0); c->sources = source_new(c); + if (!c->sources) goto nomem; c->abi = abi_new(c); + if (!c->abi) goto nomem; c->cleanup = NULL; lang_registry_init(c); + return KIT_OK; + +nomem: + compiler_fini(c); + return KIT_NOMEM; } void compiler_fini(Compiler* c) { diff --git a/src/core/core.h b/src/core/core.h @@ -26,7 +26,7 @@ typedef KitContext Context; typedef KitHeap Heap; typedef KitDiagSink DiagSink; typedef KitWriter Writer; -typedef KitTarget Target; +typedef KitTargetSpec Target; typedef KitObjBuilder ObjBuilder; typedef enum KitArchKind ArchKind; typedef enum KitOSKind OSKind; @@ -50,6 +50,13 @@ typedef struct SrcRange { SrcLoc end; } SrcRange; +struct KitTarget { + const KitContext* ctx; + Target spec; + u64* feature_words; + u32 nfeature_words; +}; + typedef enum SourceFileKind { SRC_FILE_REAL, SRC_FILE_MEMORY, @@ -116,6 +123,7 @@ struct KitCompiler { Arena* scratch; SourceManager* sources; TargetABI* abi; + const KitTarget* target_ref; Target target; CompilerCleanup* cleanup; const KitFrontendVTable* frontends[KIT_LANG_COUNT]; @@ -140,7 +148,7 @@ struct KitCompiler { jmp_buf panic; }; -void compiler_init(Compiler*, Target, const KitContext*); +KitStatus compiler_init(Compiler*, const KitTarget*, const KitContext*); void compiler_fini(Compiler*); CompilerCleanup* compiler_defer(Compiler*, void (*fn)(void*), void* arg); diff --git a/src/emu/emu.c b/src/emu/emu.c @@ -26,7 +26,7 @@ struct KitEmu { Compiler* c; - KitTarget guest_target; + KitTargetSpec guest_target; int opt_level; KitEmuTraceFlags trace; KitEmuExternalBindings bindings; @@ -68,7 +68,7 @@ static SrcLoc no_loc(void) { typedef u64 (*EmuBlockFn)(EmuThread*); typedef struct EmuResolvedConfig { - KitTarget target; + KitTargetSpec target; const ObjFormatImpl* obj_format; const ArchImpl* arch; const KitOsImpl* os; @@ -186,7 +186,7 @@ void emu_thread_os_free(Compiler* c, EmuThread* thread, size_t size) { static KitStatus emu_resolve_config(Compiler* c, const KitEmuOptions* opts, EmuResolvedConfig* out) { KitBinFmt bin_fmt; - KitTarget target; + KitTargetSpec target; const ObjFormatImpl* obj_format; const ObjFormatImpl* target_format; const ArchImpl* arch; diff --git a/src/emu/emu.h b/src/emu/emu.h @@ -264,7 +264,7 @@ typedef struct EmuLoadedImage { typedef struct EmuLoadOptions { KitSlice name; KitSlice bytes; - KitTarget guest_target; + KitTargetSpec guest_target; const char* const* argv; const char* const* envp; const KitOsImpl* os; @@ -305,7 +305,7 @@ typedef struct EmuFaultEvent { struct EmuProcess { Compiler* compiler; - KitTarget guest_target; + KitTargetSpec guest_target; const ObjFormatImpl* obj_format; const ArchImpl* arch; const KitOsImpl* os; diff --git a/src/link/link_jit.c b/src/link/link_jit.c @@ -29,8 +29,8 @@ * KitObjFile and the internal-only helpers for allocating an empty * KitObjFile (used by the JIT debug-view builder). */ ObjBuilder* kit_objfile_builder(const KitObjFile*); -KitObjFile* kit_objfile_internal_new(const KitContext* ctx, KitTarget target, - KitObjFmt fmt); +KitObjFile* kit_objfile_internal_new(const KitContext* ctx, + KitTargetSpec target, KitObjFmt fmt); void kit_objfile_internal_free(KitObjFile*); #define jit_view_objfile_free(f) kit_objfile_internal_free(f) diff --git a/src/obj/elf/emu_load.c b/src/obj/elf/emu_load.c @@ -76,7 +76,7 @@ static u8 elf_phdr_perms(u32 p_flags) { } static KitStatus elf_detect_executable(Compiler* c, KitSlice slice, - KitTarget* out) { + KitTargetSpec* out) { const u8* bytes = slice.data; const ObjFormatImpl* fmt; const ObjElfArchOps* arch_ops; diff --git a/src/obj/format.h b/src/obj/format.h @@ -89,7 +89,7 @@ typedef struct ObjFormatDsoReader { typedef struct ObjFormatEmuOps { KitStatus (*detect_executable)(Compiler*, KitSlice bytes, - KitTarget* target_out); + KitTargetSpec* target_out); KitStatus (*load_executable)(Compiler*, const EmuLoadOptions*, EmuLoadedImage* out); KitStatus (*map_object)(Compiler*, EmuProcess*, EmuLoadedImage*, diff --git a/test/api/abi_classify_test.c b/test/api/abi_classify_test.c @@ -27,7 +27,7 @@ static void expect_direct_1x_int(const char* tag, const ABIArgInfo* ai, static KitCompiler* new_compiler(KitArchKind arch, KitOSKind os, KitObjFmt obj) { - KitTarget t = kit_unit_target(arch, os, obj); + KitTargetSpec t = kit_unit_target(arch, os, obj); KitCompiler* c = NULL; if (kit_unit_compiler_new(&g_u, t, &c) != KIT_OK || !c) { fprintf(stderr, "compiler_new failed for arch=%d os=%d\n", (int)arch, diff --git a/test/api/cg_fp_cmp_test.c b/test/api/cg_fp_cmp_test.c @@ -177,7 +177,8 @@ static void build_cmp_fn(KitCompiler* c, KitCg* cg, const char* name, * linked runtime. The f128 lowering is covered by run_emit (emission) and, end * to end, by the `long double` cases in the parse suite under JIT. */ static void run_exec(void) { - KitTarget tgt = kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF); + KitTargetSpec tgt = + kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF); KitCompiler* c = NULL; KitInterpProgram* pp; KitObjBuilder* ob = NULL; @@ -237,7 +238,7 @@ static void run_exec(void) { static void run_emit(KitArchKind arch, KitOSKind os, KitObjFmt fmt, const char* tag, int opt_level, int do_f128) { - KitTarget tgt = kit_unit_target(arch, os, fmt); + KitTargetSpec tgt = kit_unit_target(arch, os, fmt); KitCompiler* c = NULL; KitObjBuilder* ob = NULL; KitCg* cg = NULL; diff --git a/test/api/cg_switch_test.c b/test/api/cg_switch_test.c @@ -251,7 +251,7 @@ static void run_all_shapes(KitCompiler* c, KitCgTypeId i32_ty, /* ---- Entry ----------------------------------------------------------- */ int main(void) { - KitTarget target; + KitTargetSpec target; KitCompiler* c = NULL; KitCgBuiltinTypes bi; KitCgTypeId i32_ty; diff --git a/test/api/cg_type_test.c b/test/api/cg_type_test.c @@ -1354,7 +1354,7 @@ static void exercise_cg_begin_end_two_objects(KitCompiler* c) { } int main(void) { - KitTarget target; + KitTargetSpec target; KitCompiler* c; KitCgBuiltinTypes bi; KitCgTypeId void_ty; diff --git a/test/api/target_test.c b/test/api/target_test.c @@ -0,0 +1,129 @@ +/* target_test - public <kit/core.h> target creation and feature API. */ + +#include <kit/core.h> +#include <string.h> + +#include "lib/kit_unit.h" + +static KitUnit g_u; +#define EXPECT(c, ...) CU_EXPECT(&g_u, c, __VA_ARGS__) + +static KitTargetSpec target_spec(KitArchKind arch, KitOSKind os, + KitObjFmt obj) { + KitTargetSpec t = kit_unit_target(arch, os, obj); + if (arch == KIT_ARCH_WASM) { + t.ptr_size = 4; + t.ptr_align = 4; + } + return t; +} + +static KitStatus make_target(KitTargetSpec spec, KitSlice isa, + const KitTargetFeature* features, + uint32_t nfeatures, KitTarget** out) { + KitTargetOptions opts; + memset(&opts, 0, sizeof opts); + opts.spec = spec; + opts.isa = isa; + opts.features = features; + opts.nfeatures = nfeatures; + return kit_target_new(&g_u.ctx, &opts, out); +} + +static int has(KitTarget* t, const char* name) { + return kit_target_has_feature(t, kit_slice_cstr(name)); +} + +static void check_x64_defaults_and_isa(void) { + KitTargetSpec spec = target_spec(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF); + KitTargetFeature disable_avx2[] = { + {KIT_SLICE_LIT("avx2"), false}, + }; + KitTarget* t = NULL; + + EXPECT(make_target(spec, KIT_SLICE_NULL, NULL, 0, &t) == KIT_OK, + "x64 default target"); + EXPECT(has(t, "sse"), "x64 default has sse"); + EXPECT(has(t, "sse2"), "x64 default has sse2"); + EXPECT(!has(t, "avx"), "x64 default lacks avx"); + kit_target_free(t); + + t = NULL; + EXPECT(make_target(spec, KIT_SLICE_LIT("x86-64-v3"), disable_avx2, 1, &t) == + KIT_OK, + "x64 isa profile plus feature override"); + EXPECT(has(t, "avx"), "x86-64-v3 enables avx"); + EXPECT(!has(t, "avx2"), "explicit feature override disables avx2"); + kit_target_free(t); +} + +static void check_rv64_isa_and_overrides(void) { + KitTargetSpec spec = target_spec(KIT_ARCH_RV64, KIT_OS_LINUX, KIT_OBJ_ELF); + KitTargetFeature enable_c[] = { + {KIT_SLICE_LIT("c"), true}, + }; + KitTarget* t = NULL; + + EXPECT(make_target(spec, KIT_SLICE_LIT("rv64im"), enable_c, 1, &t) == KIT_OK, + "rv64 isa plus feature override"); + EXPECT(has(t, "i"), "rv64im has i"); + EXPECT(has(t, "m"), "rv64im has m"); + EXPECT(!has(t, "a"), "rv64im lacks a"); + EXPECT(has(t, "c"), "explicit feature override enables c"); + EXPECT(!has(t, "zicsr"), "rv64im lacks zicsr"); + kit_target_free(t); +} + +static void check_wasm_features(void) { + KitTargetSpec spec = target_spec(KIT_ARCH_WASM, KIT_OS_WASI, KIT_OBJ_WASM); + KitTargetFeature disable_tail_calls[] = { + {KIT_SLICE_LIT("tail-calls"), false}, + }; + KitTarget* t = NULL; + KitTargetSpec resolved; + + EXPECT(make_target(spec, KIT_SLICE_LIT("wasm32"), disable_tail_calls, 1, + &t) == KIT_OK, + "wasm32 target with disabled tail-calls"); + resolved = kit_target_spec(t); + EXPECT(resolved.arch == KIT_ARCH_WASM && resolved.ptr_size == 4, + "kit_target_spec preserves wasm32 shape"); + EXPECT(!has(t, "tail-calls"), + "explicit feature override disables tail-calls"); + EXPECT(has(t, "bulk-memory"), "wasm default still has bulk-memory"); + EXPECT(!has(t, "not-a-feature"), "unknown queried feature is absent"); + kit_target_free(t); +} + +static void check_errors(void) { + KitTargetSpec spec = target_spec(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF); + KitTargetFeature bad_feature[] = { + {KIT_SLICE_LIT("nope"), true}, + }; + KitTarget* t = NULL; + + g_u.last_diag[0] = '\0'; + EXPECT(make_target(spec, KIT_SLICE_NULL, bad_feature, 1, &t) == KIT_INVALID, + "unknown feature rejected"); + EXPECT(t == NULL, "unknown feature leaves target NULL"); + EXPECT(strstr(g_u.last_diag, "unknown target feature") != NULL, + "unknown feature diagnostic"); + + g_u.last_diag[0] = '\0'; + EXPECT( + make_target(spec, KIT_SLICE_LIT("x86-64-v9"), NULL, 0, &t) == KIT_INVALID, + "unknown ISA/profile rejected"); + EXPECT(t == NULL, "unknown ISA/profile leaves target NULL"); + EXPECT(strstr(g_u.last_diag, "unsupported ISA/profile") != NULL, + "unknown ISA/profile diagnostic"); +} + +int main(void) { + kit_unit_init(&g_u); + check_x64_defaults_and_isa(); + check_rv64_isa_and_overrides(); + check_wasm_features(); + check_errors(); + kit_unit_summary(&g_u, "target_test"); + return kit_unit_status(&g_u); +} diff --git a/test/arch/aa64_isa_test.c b/test/arch/aa64_isa_test.c @@ -7,7 +7,7 @@ * invariant: an alias-bearing word (e.g. ORR Rd, ZR, Rm) resolves to * the alias spelling (MOV) rather than the canonical row. * - * Builds against the internal arch/aa64/isa.h surface (test.mk passes + * Builds against the internal arch/aa64/isa.h surface (mk/test.mk passes * -Isrc). No public-API dependency — this is a unit test of the * descriptor table itself. */ diff --git a/test/arch/aa64_sweep_gen.c b/test/arch/aa64_sweep_gen.c @@ -18,7 +18,8 @@ * they aren't assemblable standalone without a label/symbol, and L1/L2 of the * codegen round-trip already cover them. * - * Builds against the internal arch/aa64/isa.h surface (test.mk passes -Isrc). + * Builds against the internal arch/aa64/isa.h surface (mk/test.mk passes + * -Isrc). */ #include <stdint.h> diff --git a/test/arch/inline_public_test.h b/test/arch/inline_public_test.h @@ -39,7 +39,7 @@ typedef struct InlineEmit { const char* name; } InlineEmit; -static inline KitTarget it_target(KitArchKind arch) { +static inline KitTargetSpec it_target(KitArchKind arch) { return kit_unit_target(arch, KIT_OS_LINUX, KIT_OBJ_ELF); } @@ -84,6 +84,8 @@ static inline int it_emit_text(InlineTestEnv* env, KitArchKind arch, const char* name, InlineBodyFn body, InlineText* text) { KitCompiler* c = NULL; + KitTarget* target = NULL; + KitTargetOptions target_opts; InlineEmit emit; KitSlice bytes; size_t len = 0; @@ -96,8 +98,14 @@ static inline int it_emit_text(InlineTestEnv* env, KitArchKind arch, emit.body = body; emit.name = name; - if (kit_compiler_new(it_target(arch), &env->ctx, &c) != KIT_OK || !c) + memset(&target_opts, 0, sizeof target_opts); + target_opts.spec = it_target(arch); + if (kit_target_new(&env->ctx, &target_opts, &target) != KIT_OK || !target) return 0; + if (kit_compiler_new(target, &env->ctx, &c) != KIT_OK || !c) { + kit_target_free(target); + return 0; + } if (kit_frontend_run(c, it_emit_func, &emit) != KIT_OK) goto done; if (kit_writer_mem(&env->heap, &text->writer) != KIT_OK || !text->writer) goto done; @@ -118,6 +126,7 @@ static inline int it_emit_text(InlineTestEnv* env, KitArchKind arch, done: if (emit.ob) kit_obj_builder_free(emit.ob); kit_compiler_free(c); + kit_target_free(target); if (!ok) { if (text->file) kit_obj_free(text->file); if (text->writer) kit_writer_close(text->writer); @@ -153,11 +162,19 @@ static inline int it_expect_panic(InlineTestEnv* env, KitArchKind arch, const char* name, InlineBodyFn body, const char* expected) { KitCompiler* c = NULL; + KitTarget* target = NULL; + KitTargetOptions target_opts; InlinePanic panic; KitStatus st; int ok; - if (kit_compiler_new(it_target(arch), &env->ctx, &c) != KIT_OK || !c) + memset(&target_opts, 0, sizeof target_opts); + target_opts.spec = it_target(arch); + if (kit_target_new(&env->ctx, &target_opts, &target) != KIT_OK || !target) return 0; + if (kit_compiler_new(target, &env->ctx, &c) != KIT_OK || !c) { + kit_target_free(target); + return 0; + } panic.body = body; panic.name = name; env->last_diag[0] = '\0'; @@ -166,6 +183,7 @@ static inline int it_expect_panic(InlineTestEnv* env, KitArchKind arch, env->suppress_fatal--; ok = st == KIT_ERR && (!expected || strstr(env->last_diag, expected) != NULL); kit_compiler_free(c); + kit_target_free(target); return ok; } diff --git a/test/arch/rv64_decode_test.c b/test/arch/rv64_decode_test.c @@ -21,7 +21,7 @@ static KitUnit g_u; #define EXPECT(cond, ...) CU_EXPECT(&g_u, cond, __VA_ARGS__) static KitCompiler* new_compiler(void) { - KitTarget t = kit_unit_target(KIT_ARCH_RV64, KIT_OS_LINUX, KIT_OBJ_ELF); + KitTargetSpec t = kit_unit_target(KIT_ARCH_RV64, KIT_OS_LINUX, KIT_OBJ_ELF); KitCompiler* c = NULL; if (kit_unit_compiler_new(&g_u, t, &c) != KIT_OK || !c) { fprintf(stderr, "compiler_new failed\n"); diff --git a/test/asm/harness/asm_runner.c b/test/asm/harness/asm_runner.c @@ -251,13 +251,32 @@ static void ctx_init(KitContext* ctx) { ctx->now = -1; } -static void target_from_env(KitTarget* t) { +static void target_from_env(KitTargetSpec* t) { if (kit_test_target_init(t) != 0) { fprintf(stderr, "asm-runner: kit_test_target_init failed\n"); exit(2); } } +static KitStatus compiler_new_for_target(KitTargetSpec spec, KitContext* ctx, + KitTarget** target_out, + KitCompiler** compiler_out) { + KitTargetOptions opts; + KitStatus st; + if (target_out) *target_out = NULL; + if (compiler_out) *compiler_out = NULL; + memset(&opts, 0, sizeof opts); + opts.spec = spec; + st = kit_target_new(ctx, &opts, target_out); + if (st != KIT_OK) return st; + st = kit_compiler_new(*target_out, ctx, compiler_out); + if (st != KIT_OK) { + kit_target_free(*target_out); + *target_out = NULL; + } + return st; +} + static KitStatus compile_asm_obj(KitCompiler* c, const KitAsmCompileOptions* opts, KitSlice name, const KitSlice* in, @@ -418,8 +437,9 @@ static int hex_decode(const uint8_t* in, size_t in_len, uint8_t** out, static int mode_encode(const char* src_path, const char* out_path) { uint8_t* src = NULL; size_t src_len = 0; - KitTarget tgt; + KitTargetSpec tgt; KitContext ctx; + KitTarget* target = NULL; KitCompiler* c = NULL; KitSlice in; KitAsmCompileOptions opts; @@ -439,7 +459,7 @@ static int mode_encode(const char* src_path, const char* out_path) { } target_from_env(&tgt); ctx_init(&ctx); - if (kit_compiler_new(tgt, &ctx, &c) != KIT_OK || !c) { + if (compiler_new_for_target(tgt, &ctx, &target, &c) != KIT_OK || !c) { free(src); return 2; } @@ -453,6 +473,7 @@ static int mode_encode(const char* src_path, const char* out_path) { if (compile_asm_emit(c, &opts, kit_slice_cstr(src_path), &in, w) != KIT_OK) { kit_writer_close(w); kit_compiler_free(c); + kit_target_free(target); free(src); return 1; } @@ -465,6 +486,7 @@ static int mode_encode(const char* src_path, const char* out_path) { !of) { kit_writer_close(w); kit_compiler_free(c); + kit_target_free(target); free(src); return 1; } @@ -512,6 +534,7 @@ static int mode_encode(const char* src_path, const char* out_path) { kit_obj_free(of); kit_writer_close(w); kit_compiler_free(c); + kit_target_free(target); free(src); return rc; } @@ -526,8 +549,9 @@ static int mode_decode(const char* in_path, const char* out_path) { size_t raw_len = 0; uint8_t* bytes = NULL; size_t nbytes = 0; - KitTarget tgt; + KitTargetSpec tgt; KitContext ctx; + KitTarget* target = NULL; KitCompiler* c = NULL; KitDisasmIter* it = NULL; KitDisasmContext dctx; @@ -547,17 +571,18 @@ static int mode_decode(const char* in_path, const char* out_path) { target_from_env(&tgt); ctx_init(&ctx); - if (kit_compiler_new(tgt, &ctx, &c) != KIT_OK || !c) { + if (compiler_new_for_target(tgt, &ctx, &target, &c) != KIT_OK || !c) { free(bytes); return 2; } memset(&dctx, 0, sizeof dctx); - dctx.target = tgt; + dctx.target = target; dctx.context = ctx; if (kit_disasm_iter_new(&dctx, bytes, nbytes, 0, NULL, &it) != KIT_OK || !it) { kit_compiler_free(c); + kit_target_free(target); free(bytes); return 1; } @@ -567,6 +592,7 @@ static int mode_decode(const char* in_path, const char* out_path) { perror(out_path); kit_disasm_iter_free(it); kit_compiler_free(c); + kit_target_free(target); free(bytes); return 2; } @@ -588,6 +614,7 @@ static int mode_decode(const char* in_path, const char* out_path) { kit_disasm_iter_free(it); kit_compiler_free(c); + kit_target_free(target); free(bytes); return rc; } @@ -599,8 +626,9 @@ static int mode_decode(const char* in_path, const char* out_path) { static int mode_listing(const char* in_path, const char* out_path) { uint8_t* bytes = NULL; size_t nbytes = 0; - KitTarget tgt; + KitTargetSpec tgt; KitContext ctx; + KitTarget* target = NULL; KitCompiler* c = NULL; KitSlice in; KitWriter* w = NULL; @@ -614,7 +642,7 @@ static int mode_listing(const char* in_path, const char* out_path) { } target_from_env(&tgt); ctx_init(&ctx); - if (kit_compiler_new(tgt, &ctx, &c) != KIT_OK || !c) { + if (compiler_new_for_target(tgt, &ctx, &target, &c) != KIT_OK || !c) { free(bytes); return 2; } @@ -627,6 +655,7 @@ static int mode_listing(const char* in_path, const char* out_path) { if (kit_disasm_obj_bytes(&ctx, &in, w) != KIT_OK) { kit_writer_close(w); kit_compiler_free(c); + kit_target_free(target); free(bytes); return 1; } @@ -635,6 +664,7 @@ static int mode_listing(const char* in_path, const char* out_path) { kit_writer_close(w); kit_compiler_free(c); + kit_target_free(target); free(bytes); return rc; } @@ -645,8 +675,9 @@ static int mode_listing(const char* in_path, const char* out_path) { static int mode_emit(const char* src_path, const char* out_path) { uint8_t* src = NULL; size_t src_len = 0; - KitTarget tgt; + KitTargetSpec tgt; KitContext ctx; + KitTarget* target = NULL; KitCompiler* c = NULL; KitSlice in; KitAsmCompileOptions opts; @@ -661,7 +692,7 @@ static int mode_emit(const char* src_path, const char* out_path) { } target_from_env(&tgt); ctx_init(&ctx); - if (kit_compiler_new(tgt, &ctx, &c) != KIT_OK || !c) { + if (compiler_new_for_target(tgt, &ctx, &target, &c) != KIT_OK || !c) { free(src); return 2; } @@ -675,6 +706,7 @@ static int mode_emit(const char* src_path, const char* out_path) { if (compile_asm_emit(c, &opts, kit_slice_cstr(src_path), &in, w) != KIT_OK) { kit_writer_close(w); kit_compiler_free(c); + kit_target_free(target); free(src); return 1; } @@ -684,6 +716,7 @@ static int mode_emit(const char* src_path, const char* out_path) { kit_writer_close(w); kit_compiler_free(c); + kit_target_free(target); free(src); return rc; } @@ -701,8 +734,9 @@ static char g_tls_block[8192] __attribute__((aligned(16))); static int mode_jit(const char* src_path) { uint8_t* src = NULL; size_t src_len = 0; - KitTarget tgt; + KitTargetSpec tgt; KitContext ctx; + KitTarget* target = NULL; KitCompiler* c = NULL; KitSlice in; KitAsmCompileOptions opts; @@ -718,7 +752,7 @@ static int mode_jit(const char* src_path) { } target_from_env(&tgt); ctx_init(&ctx); - if (kit_compiler_new(tgt, &ctx, &c) != KIT_OK || !c) { + if (compiler_new_for_target(tgt, &ctx, &target, &c) != KIT_OK || !c) { free(src); return 2; } @@ -731,6 +765,7 @@ static int mode_jit(const char* src_path) { if (compile_asm_obj(c, &opts, kit_slice_cstr(src_path), &in, &ob) != KIT_OK || !ob) { kit_compiler_free(c); + kit_target_free(target); free(src); return 1; } @@ -740,6 +775,7 @@ static int mode_jit(const char* src_path) { if (link_one_obj_jit(c, ob, &jhost, "test_main", &jit) != KIT_OK || !jit) { kit_compiler_free(c); + kit_target_free(target); free(src); return 1; } @@ -772,6 +808,7 @@ static int mode_jit(const char* src_path) { kit_jit_free(jit); kit_compiler_free(c); + kit_target_free(target); free(src); return result; } diff --git a/test/cg/ir_recorder_test.c b/test/cg/ir_recorder_test.c @@ -23,7 +23,7 @@ typedef struct TestCtx { } TestCtx; static void tc_init(TestCtx* tc) { - KitTarget target; + KitTargetSpec target; KitCgBuiltinTypes b; memset(tc, 0, sizeof *tc); target = kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF); diff --git a/test/cg/native_direct_target_test.c b/test/cg/native_direct_target_test.c @@ -22,7 +22,7 @@ typedef struct TestCtx { } TestCtx; static void tc_init(TestCtx* tc) { - KitTarget target; + KitTargetSpec target; KitCgBuiltinTypes b; memset(tc, 0, sizeof *tc); target = kit_unit_target(KIT_ARCH_X86_64, KIT_OS_LINUX, KIT_OBJ_ELF); diff --git a/test/cg/strength_reduce_test.c b/test/cg/strength_reduce_test.c @@ -119,7 +119,9 @@ static void slice_to_buf(KitSlice s, char* buf, size_t cap) { * Returns the instruction count, or -1 on harness failure. */ static int op_disasm(KitArchKind arch, KitCgIntBinOp op, int64_t imm, DisInsn* out, int cap) { - KitTarget target = kit_unit_target(arch, KIT_OS_LINUX, KIT_OBJ_ELF); + KitTargetSpec target = kit_unit_target(arch, KIT_OS_LINUX, KIT_OBJ_ELF); + KitTargetOptions target_opts; + KitTarget* kt = NULL; KitCompiler* c = NULL; EmitCtx ctx; KitWriter* writer = NULL; @@ -138,20 +140,25 @@ static int op_disasm(KitArchKind arch, KitCgIntBinOp op, int64_t imm, ctx.op = op; ctx.imm = imm; - if (kit_compiler_new(target, &g_u.ctx, &c) != KIT_OK || !c) return -1; + memset(&target_opts, 0, sizeof target_opts); + target_opts.spec = target; + if (kit_target_new(&g_u.ctx, &target_opts, &kt) != KIT_OK || !kt) return -1; + if (kit_compiler_new(kt, &g_u.ctx, &c) != KIT_OK || !c) goto done; if (kit_frontend_run(c, emit_binop_fn, &ctx) != KIT_OK) goto done; if (kit_writer_mem(&g_u.heap, &writer) != KIT_OK || !writer) goto done; if (kit_obj_builder_emit(ctx.ob, writer) != KIT_OK) goto done; bytes.data = kit_writer_mem_bytes(writer, &len); bytes.len = len; - if (kit_obj_open(&g_u.ctx, KIT_SLICE_LIT("<sr-test>"), &bytes, &file) != KIT_OK) + if (kit_obj_open(&g_u.ctx, KIT_SLICE_LIT("<sr-test>"), &bytes, &file) != + KIT_OK) goto done; - if (kit_obj_section_by_name(file, KIT_SLICE_LIT(".text"), &text_sec) != KIT_OK) + if (kit_obj_section_by_name(file, KIT_SLICE_LIT(".text"), &text_sec) != + KIT_OK) goto done; if (kit_obj_section_data(file, text_sec, &data, &len) != KIT_OK) goto done; memset(&dc, 0, sizeof dc); - dc.target = target; + dc.target = kt; dc.context = g_u.ctx; if (kit_disasm_iter_new(&dc, data, len, 0, file, &it) != KIT_OK || !it) goto done; @@ -168,6 +175,7 @@ done: if (writer) kit_writer_close(writer); if (ctx.ob) kit_obj_builder_free(ctx.ob); kit_compiler_free(c); + kit_target_free(kt); return result; } @@ -195,7 +203,7 @@ typedef struct ArchExpect { const char* shl_mnem; /* immediate shift-left mnemonic (x*2^k) */ const char* shr_mnem; /* immediate logical shift-right (x u/ 2^k) */ const char* and_mnem; /* immediate bitwise-and (x u% 2^k) */ - const char* mark; /* operand marker for an immediate ("" if in mnemonic) */ + const char* mark; /* operand marker for an immediate ("" if in mnemonic) */ } ArchExpect; static void check_arch(const ArchExpect* ex) { diff --git a/test/coff/kit-roundtrip-coff.c b/test/coff/kit-roundtrip-coff.c @@ -12,7 +12,7 @@ * * Mixes public (<kit/core.h>, <kit/object.h>) and internal * (src/obj/obj.h, src/core/core.h) surfaces. Compiled with -Isrc - * by test/test.mk. Not a libkit consumer in the usual sense — a + * by mk/test.mk. Not a libkit consumer in the usual sense — a * test binary that pokes the same private headers the writer / * reader use. */ @@ -75,7 +75,7 @@ static const char* g_test_name = "?"; /* ---- target builders ---------------------------------------------- */ -static void target_x64_windows(KitTarget* t) { +static void target_x64_windows(KitTargetSpec* t) { memset(t, 0, sizeof *t); t->arch = KIT_ARCH_X86_64; t->os = KIT_OS_WINDOWS; @@ -87,7 +87,7 @@ static void target_x64_windows(KitTarget* t) { t->code_model = KIT_CM_SMALL; } -static void target_aa64_windows(KitTarget* t) { +static void target_aa64_windows(KitTargetSpec* t) { memset(t, 0, sizeof *t); t->arch = KIT_ARCH_ARM_64; t->os = KIT_OS_WINDOWS; @@ -254,16 +254,31 @@ static void run_roundtrip(Compiler* c, ObjBuilder* in, static KitContext g_ctx; -static Compiler* make_compiler(const KitTarget* t) { +static Compiler* make_compiler(const KitTargetSpec* t) { memset(&g_ctx, 0, sizeof g_ctx); g_ctx.heap = &g_heap; g_ctx.diag = &g_diag; g_ctx.now = -1; + KitTargetOptions opts; + memset(&opts, 0, sizeof opts); + opts.spec = *t; + KitTarget* target = NULL; + if (kit_target_new(&g_ctx, &opts, &target) != KIT_OK || !target) return NULL; KitCompiler* cc = NULL; - if (kit_compiler_new(*t, &g_ctx, &cc) != KIT_OK || !cc) return NULL; + if (kit_compiler_new(target, &g_ctx, &cc) != KIT_OK || !cc) { + kit_target_free(target); + return NULL; + } return (Compiler*)cc; } +static void free_compiler(Compiler* c) { + if (!c) return; + const KitTarget* target = kit_compiler_target((KitCompiler*)c); + kit_compiler_free((KitCompiler*)c); + kit_target_free((KitTarget*)target); +} + /* ---- payload bytes ------------------------------------------------- */ /* x64: mov eax, 42 ; ret. */ @@ -294,7 +309,7 @@ static void verify_header_minimal(const ObjBuilder* ob, Pool* p) { static void test_header_minimal_x64(void) { g_test_name = "header_minimal_x64"; - KitTarget t; + KitTargetSpec t; target_x64_windows(&t); Compiler* c = make_compiler(&t); EXPECT(c != NULL, "compiler_new"); @@ -302,7 +317,7 @@ static void test_header_minimal_x64(void) { if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic during test"); return; } @@ -316,12 +331,12 @@ static void test_header_minimal_x64(void) { run_roundtrip(c, ob, verify_header_minimal); obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } static void test_header_minimal_aa64(void) { g_test_name = "header_minimal_aa64"; - KitTarget t; + KitTargetSpec t; target_aa64_windows(&t); Compiler* c = make_compiler(&t); EXPECT(c != NULL, "compiler_new"); @@ -329,7 +344,7 @@ static void test_header_minimal_aa64(void) { if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic during test"); return; } @@ -343,7 +358,7 @@ static void test_header_minimal_aa64(void) { run_roundtrip(c, ob, verify_header_minimal); obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } /* test_text_only_x64: .text + one defined global function symbol. */ @@ -363,7 +378,7 @@ static void verify_text_only(const ObjBuilder* ob, Pool* p) { static void test_text_only_x64(void) { g_test_name = "text_only_x64"; - KitTarget t; + KitTargetSpec t; target_x64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -372,7 +387,7 @@ static void test_text_only_x64(void) { } if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic"); return; } @@ -388,12 +403,12 @@ static void test_text_only_x64(void) { run_roundtrip(c, ob, verify_text_only); obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } static void test_text_only_aa64(void) { g_test_name = "text_only_aa64"; - KitTarget t; + KitTargetSpec t; target_aa64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -402,7 +417,7 @@ static void test_text_only_aa64(void) { } if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic"); return; } @@ -418,7 +433,7 @@ static void test_text_only_aa64(void) { run_roundtrip(c, ob, verify_text_only); obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } /* test_rodata: .rdata read-only data + a defined object symbol. */ @@ -438,7 +453,7 @@ static void verify_rodata(const ObjBuilder* ob, Pool* p) { static void test_rodata(void) { g_test_name = "rodata"; - KitTarget t; + KitTargetSpec t; target_x64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -447,7 +462,7 @@ static void test_rodata(void) { } if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic"); return; } @@ -464,7 +479,7 @@ static void test_rodata(void) { run_roundtrip(c, ob, verify_rodata); obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } /* test_bss: .bss section (NOBITS), one defined symbol, size > 0. */ @@ -480,7 +495,7 @@ static void verify_bss(const ObjBuilder* ob, Pool* p) { static void test_bss(void) { g_test_name = "bss"; - KitTarget t; + KitTargetSpec t; target_x64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -489,7 +504,7 @@ static void test_bss(void) { } if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic"); return; } @@ -506,7 +521,7 @@ static void test_bss(void) { run_roundtrip(c, ob, verify_bss); obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } /* test_data_with_reloc_abs64_x64: .data with an 8-byte slot @@ -544,7 +559,7 @@ static void verify_data_abs64(const ObjBuilder* ob, Pool* p) { static void test_data_with_reloc_abs64_x64(void) { g_test_name = "data_with_reloc_abs64_x64"; - KitTarget t; + KitTargetSpec t; target_x64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -553,7 +568,7 @@ static void test_data_with_reloc_abs64_x64(void) { } if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic"); return; } @@ -571,12 +586,12 @@ static void test_data_with_reloc_abs64_x64(void) { run_roundtrip(c, ob, verify_data_abs64); obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } static void test_data_with_reloc_abs64_aa64(void) { g_test_name = "data_with_reloc_abs64_aa64"; - KitTarget t; + KitTargetSpec t; target_aa64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -585,7 +600,7 @@ static void test_data_with_reloc_abs64_aa64(void) { } if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic"); return; } @@ -603,7 +618,7 @@ static void test_data_with_reloc_abs64_aa64(void) { run_roundtrip(c, ob, verify_data_abs64); obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } /* test_data_with_reloc_rel32_x64: .text with a REL32 relocation @@ -629,7 +644,7 @@ static void verify_rel32(const ObjBuilder* ob, Pool* p) { static void test_data_with_reloc_rel32_x64(void) { g_test_name = "reloc_rel32_x64"; - KitTarget t; + KitTargetSpec t; target_x64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -638,7 +653,7 @@ static void test_data_with_reloc_rel32_x64(void) { } if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic"); return; } @@ -661,7 +676,7 @@ static void test_data_with_reloc_rel32_x64(void) { run_roundtrip(c, ob, verify_rel32); obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } /* test_aa64_branch26: .text with a BRANCH26 (R_AARCH64_CALL26) @@ -685,7 +700,7 @@ static void verify_aa64_branch26(const ObjBuilder* ob, Pool* p) { static void test_aa64_branch26(void) { g_test_name = "aa64_branch26"; - KitTarget t; + KitTargetSpec t; target_aa64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -694,7 +709,7 @@ static void test_aa64_branch26(void) { } if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic"); return; } @@ -714,7 +729,7 @@ static void test_aa64_branch26(void) { run_roundtrip(c, ob, verify_aa64_branch26); obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } /* test_aa64_pagebase_pageoffset: ADRP + ADD pair against a .rdata @@ -738,7 +753,7 @@ static void verify_aa64_adrp_add(const ObjBuilder* ob, Pool* p) { static void test_aa64_pagebase_pageoffset(void) { g_test_name = "aa64_pagebase_pageoffset"; - KitTarget t; + KitTargetSpec t; target_aa64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -747,7 +762,7 @@ static void test_aa64_pagebase_pageoffset(void) { } if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic"); return; } @@ -773,7 +788,7 @@ static void test_aa64_pagebase_pageoffset(void) { run_roundtrip(c, ob, verify_aa64_adrp_add); obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } /* test_long_section_name: section whose name exceeds 8 bytes, @@ -787,7 +802,7 @@ static void verify_long_section_name(const ObjBuilder* ob, Pool* p) { static void test_long_section_name(void) { g_test_name = "long_section_name"; - KitTarget t; + KitTargetSpec t; target_x64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -796,7 +811,7 @@ static void test_long_section_name(void) { } if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic"); return; } @@ -810,7 +825,7 @@ static void test_long_section_name(void) { run_roundtrip(c, ob, verify_long_section_name); obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } /* test_long_symbol_name: symbol whose name exceeds 8 bytes — uses @@ -828,7 +843,7 @@ static void verify_long_symbol_name(const ObjBuilder* ob, Pool* p) { static void test_long_symbol_name(void) { g_test_name = "long_symbol_name"; - KitTarget t; + KitTargetSpec t; target_x64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -837,7 +852,7 @@ static void test_long_symbol_name(void) { } if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic"); return; } @@ -853,7 +868,7 @@ static void test_long_symbol_name(void) { run_roundtrip(c, ob, verify_long_symbol_name); obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } /* test_weak_global: weak global symbol — IMAGE_SYM_CLASS_WEAK_EXTERNAL @@ -871,7 +886,7 @@ static void verify_weak_global(const ObjBuilder* ob, Pool* p) { static void test_weak_global(void) { g_test_name = "weak_global"; - KitTarget t; + KitTargetSpec t; target_x64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -880,7 +895,7 @@ static void test_weak_global(void) { } if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic"); return; } @@ -896,7 +911,7 @@ static void test_weak_global(void) { run_roundtrip(c, ob, verify_weak_global); obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } /* test_common_symbol: COFF common — UNDEFINED section number with @@ -916,7 +931,7 @@ static void verify_common_symbol(const ObjBuilder* ob, Pool* p) { static void test_common_symbol(void) { g_test_name = "common_symbol"; - KitTarget t; + KitTargetSpec t; target_x64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -925,7 +940,7 @@ static void test_common_symbol(void) { } if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic"); return; } @@ -939,7 +954,7 @@ static void test_common_symbol(void) { run_roundtrip(c, ob, verify_common_symbol); obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } /* test_comdat_group: two sections wired into one COMDAT group. */ @@ -975,7 +990,7 @@ static void verify_comdat_group(const ObjBuilder* ob, Pool* p) { static void test_comdat_group(void) { g_test_name = "comdat_group"; - KitTarget t; + KitTargetSpec t; target_x64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -984,7 +999,7 @@ static void test_comdat_group(void) { } if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic"); return; } @@ -1020,7 +1035,7 @@ static void test_comdat_group(void) { run_roundtrip(c, ob, verify_comdat_group); obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } /* test_static_local_symbol: STATIC storage class — file-local symbol. */ @@ -1037,7 +1052,7 @@ static void verify_static_local(const ObjBuilder* ob, Pool* p) { static void test_static_local_symbol(void) { g_test_name = "static_local_symbol"; - KitTarget t; + KitTargetSpec t; target_x64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -1046,7 +1061,7 @@ static void test_static_local_symbol(void) { } if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic"); return; } @@ -1062,7 +1077,7 @@ static void test_static_local_symbol(void) { run_roundtrip(c, ob, verify_static_local); obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } /* test_section_symbol_synthesis: input has no explicit SK_SECTION @@ -1089,7 +1104,7 @@ static void verify_section_symbol_synthesis(const ObjBuilder* ob, Pool* p) { static void test_section_symbol_synthesis(void) { g_test_name = "section_symbol_synthesis"; - KitTarget t; + KitTargetSpec t; target_x64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -1098,7 +1113,7 @@ static void test_section_symbol_synthesis(void) { } if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic"); return; } @@ -1125,7 +1140,7 @@ static void test_section_symbol_synthesis(void) { run_roundtrip(c, ob, verify_section_symbol_synthesis); obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } /* test_tls_section: ".tls$" section gets SF_TLS on readback (name- @@ -1142,7 +1157,7 @@ static void verify_tls_section(const ObjBuilder* ob, Pool* p) { static void test_tls_section(void) { g_test_name = "tls_section"; - KitTarget t; + KitTargetSpec t; target_x64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -1151,7 +1166,7 @@ static void test_tls_section(void) { } if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic"); return; } @@ -1168,7 +1183,7 @@ static void test_tls_section(void) { run_roundtrip(c, ob, verify_tls_section); obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } /* test_align_nibble: section with a non-trivial alignment (4096) @@ -1184,7 +1199,7 @@ static void verify_align_nibble(const ObjBuilder* ob, Pool* p) { static void test_align_nibble(void) { g_test_name = "align_nibble"; - KitTarget t; + KitTargetSpec t; target_x64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -1193,7 +1208,7 @@ static void test_align_nibble(void) { } if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic"); return; } @@ -1208,7 +1223,7 @@ static void test_align_nibble(void) { run_roundtrip(c, ob, verify_align_nibble); obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } /* test_empty_obj: no sections, no symbols. Smallest valid .obj. */ @@ -1227,7 +1242,7 @@ static void verify_empty_obj(const ObjBuilder* ob, Pool* p) { static void test_empty_obj(void) { g_test_name = "empty_obj"; - KitTarget t; + KitTargetSpec t; target_x64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -1236,7 +1251,7 @@ static void test_empty_obj(void) { } if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic"); return; } @@ -1246,7 +1261,7 @@ static void test_empty_obj(void) { run_roundtrip(c, ob, verify_empty_obj); obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } /* ---- short-import (Microsoft .lib member) smoke ------------------ */ @@ -1263,7 +1278,7 @@ static void test_empty_obj(void) { * name is recoverable via obj_get_coff_import_dll. */ static void test_short_import_amd64(void) { g_test_name = "short_import_amd64"; - KitTarget t; + KitTargetSpec t; target_x64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -1272,7 +1287,7 @@ static void test_short_import_amd64(void) { } if (setjmp(c->panic)) { compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); EXPECT(0, "panic during test"); return; } @@ -1311,7 +1326,7 @@ static void test_short_import_amd64(void) { ObjBuilder* ob = read_coff(c, "short-import", buf, kTotal); EXPECT(ob != NULL, "read_coff returned NULL on short-import"); if (!ob) { - kit_compiler_free((KitCompiler*)c); + free_compiler(c); return; } @@ -1345,7 +1360,7 @@ static void test_short_import_amd64(void) { (void)kSymLen; (void)kDllLen; obj_free(ob); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); } /* ---- driver -------------------------------------------------------- */ diff --git a/test/coff/pe-dso-forwarder.c b/test/coff/pe-dso-forwarder.c @@ -68,7 +68,7 @@ static int g_failures; static KitContext g_ctx; -static void target_x64_windows(KitTarget* t) { +static void target_x64_windows(KitTargetSpec* t) { memset(t, 0, sizeof *t); t->arch = KIT_ARCH_X86_64; t->os = KIT_OS_WINDOWS; @@ -80,16 +80,31 @@ static void target_x64_windows(KitTarget* t) { t->code_model = KIT_CM_SMALL; } -static Compiler* make_compiler(const KitTarget* t) { +static Compiler* make_compiler(const KitTargetSpec* t) { memset(&g_ctx, 0, sizeof g_ctx); g_ctx.heap = &g_heap; g_ctx.diag = &g_diag; g_ctx.now = -1; + KitTargetOptions opts; + memset(&opts, 0, sizeof opts); + opts.spec = *t; + KitTarget* target = NULL; + if (kit_target_new(&g_ctx, &opts, &target) != KIT_OK || !target) return NULL; KitCompiler* cc = NULL; - if (kit_compiler_new(*t, &g_ctx, &cc) != KIT_OK || !cc) return NULL; + if (kit_compiler_new(target, &g_ctx, &cc) != KIT_OK || !cc) { + kit_target_free(target); + return NULL; + } return (Compiler*)cc; } +static void free_compiler(Compiler* c) { + if (!c) return; + const KitTarget* target = kit_compiler_target((KitCompiler*)c); + kit_compiler_free((KitCompiler*)c); + kit_target_free((KitTarget*)target); +} + /* ---- little-endian writers ---------------------------------------- */ static void wr_u16(uint8_t* p, uint16_t v) { @@ -252,7 +267,7 @@ static int has_sym(const ObjBuilder* ob, Pool* p, const char* name) { } int main(void) { - KitTarget t; + KitTargetSpec t; target_x64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -262,14 +277,14 @@ int main(void) { if (setjmp(c->panic)) { fprintf(stderr, "FAIL: panic during pe-dso-forwarder\n"); compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); return 1; } uint8_t* buf = (uint8_t*)malloc(FILE_SIZE); EXPECT(buf != NULL, "malloc PE buffer"); if (!buf) { - kit_compiler_free((KitCompiler*)c); + free_compiler(c); return 1; } build_dso(buf); @@ -292,7 +307,7 @@ int main(void) { "forwarded export \"%s\" missing from ObjBuilder", kForwarded); free(buf); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); if (g_failures) { fprintf(stderr, "FAILED %d assertion(s)\n", g_failures); diff --git a/test/coff/pe-import-mingw.c b/test/coff/pe-import-mingw.c @@ -91,7 +91,7 @@ static int g_failures; static KitContext g_ctx; -static void target_x64_windows(KitTarget* t) { +static void target_x64_windows(KitTargetSpec* t) { memset(t, 0, sizeof *t); t->arch = KIT_ARCH_X86_64; t->os = KIT_OS_WINDOWS; @@ -103,16 +103,31 @@ static void target_x64_windows(KitTarget* t) { t->code_model = KIT_CM_SMALL; } -static Compiler* make_compiler(const KitTarget* t) { +static Compiler* make_compiler(const KitTargetSpec* t) { memset(&g_ctx, 0, sizeof g_ctx); g_ctx.heap = &g_heap; g_ctx.diag = &g_diag; g_ctx.now = -1; + KitTargetOptions opts; + memset(&opts, 0, sizeof opts); + opts.spec = *t; + KitTarget* target = NULL; + if (kit_target_new(&g_ctx, &opts, &target) != KIT_OK || !target) return NULL; KitCompiler* cc = NULL; - if (kit_compiler_new(*t, &g_ctx, &cc) != KIT_OK || !cc) return NULL; + if (kit_compiler_new(target, &g_ctx, &cc) != KIT_OK || !cc) { + kit_target_free(target); + return NULL; + } return (Compiler*)cc; } +static void free_compiler(Compiler* c) { + if (!c) return; + const KitTarget* target = kit_compiler_target((KitCompiler*)c); + kit_compiler_free((KitCompiler*)c); + kit_target_free((KitTarget*)target); +} + /* ---- program ObjBuilder builder ----------------------------------- */ static ObjBuilder* build_program(Compiler* c) { @@ -233,7 +248,7 @@ int main(void) { return 0; } - KitTarget t; + KitTargetSpec t; target_x64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -244,7 +259,7 @@ int main(void) { if (setjmp(c->panic)) { fprintf(stderr, "FAIL: panic during pe-import-mingw\n"); compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); free(ar_bytes); return 1; } @@ -272,7 +287,7 @@ int main(void) { EXPECT(img != NULL, "link_resolve returned NULL"); if (!img) { link_free(l); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); free(ar_bytes); return 1; } @@ -303,7 +318,7 @@ int main(void) { fprintf(stderr, "FAIL: kit_writer_mem\n"); link_image_free(img); link_free(l); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); free(ar_bytes); return 1; } @@ -362,7 +377,7 @@ int main(void) { free(dump_p); } - kit_compiler_free((KitCompiler*)c); + free_compiler(c); free(ar_bytes); if (g_failures) { diff --git a/test/coff/pe-import-smoke.c b/test/coff/pe-import-smoke.c @@ -105,7 +105,7 @@ static int g_failures; static KitContext g_ctx; -static void target_x64_windows(KitTarget* t) { +static void target_x64_windows(KitTargetSpec* t) { memset(t, 0, sizeof *t); t->arch = KIT_ARCH_X86_64; t->os = KIT_OS_WINDOWS; @@ -117,16 +117,31 @@ static void target_x64_windows(KitTarget* t) { t->code_model = KIT_CM_SMALL; } -static Compiler* make_compiler(const KitTarget* t) { +static Compiler* make_compiler(const KitTargetSpec* t) { memset(&g_ctx, 0, sizeof g_ctx); g_ctx.heap = &g_heap; g_ctx.diag = &g_diag; g_ctx.now = -1; + KitTargetOptions opts; + memset(&opts, 0, sizeof opts); + opts.spec = *t; + KitTarget* target = NULL; + if (kit_target_new(&g_ctx, &opts, &target) != KIT_OK || !target) return NULL; KitCompiler* cc = NULL; - if (kit_compiler_new(*t, &g_ctx, &cc) != KIT_OK || !cc) return NULL; + if (kit_compiler_new(target, &g_ctx, &cc) != KIT_OK || !cc) { + kit_target_free(target); + return NULL; + } return (Compiler*)cc; } +static void free_compiler(Compiler* c) { + if (!c) return; + const KitTarget* target = kit_compiler_target((KitCompiler*)c); + kit_compiler_free((KitCompiler*)c); + kit_target_free((KitTarget*)target); +} + /* ---- short-import shim builder ------------------------------------ */ static void build_short_import_amd64(uint8_t buf[SHIM_TOTAL_LEN]) { @@ -226,7 +241,7 @@ int main(void) { return 0; } - KitTarget t; + KitTargetSpec t; target_x64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -236,7 +251,7 @@ int main(void) { if (setjmp(c->panic)) { fprintf(stderr, "FAIL: panic during pe-import-smoke\n"); compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); return 1; } @@ -263,7 +278,7 @@ int main(void) { EXPECT(img != NULL, "link_resolve returned NULL"); if (!img) { link_free(l); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); return 1; } @@ -299,7 +314,7 @@ int main(void) { fprintf(stderr, "FAIL: kit_writer_mem\n"); link_image_free(img); link_free(l); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); return 1; } link_emit_image_writer(img, w); @@ -418,7 +433,7 @@ int main(void) { free(dump_d); } - kit_compiler_free((KitCompiler*)c); + free_compiler(c); if (g_failures) { fprintf(stderr, "FAILED %d assertion(s)\n", g_failures); diff --git a/test/coff/pe-mixed-archive.c b/test/coff/pe-mixed-archive.c @@ -106,7 +106,7 @@ static int g_failures; static KitContext g_ctx; -static void target_x64_windows(KitTarget* t) { +static void target_x64_windows(KitTargetSpec* t) { memset(t, 0, sizeof *t); t->arch = KIT_ARCH_X86_64; t->os = KIT_OS_WINDOWS; @@ -118,16 +118,31 @@ static void target_x64_windows(KitTarget* t) { t->code_model = KIT_CM_SMALL; } -static Compiler* make_compiler(const KitTarget* t) { +static Compiler* make_compiler(const KitTargetSpec* t) { memset(&g_ctx, 0, sizeof g_ctx); g_ctx.heap = &g_heap; g_ctx.diag = &g_diag; g_ctx.now = -1; + KitTargetOptions opts; + memset(&opts, 0, sizeof opts); + opts.spec = *t; + KitTarget* target = NULL; + if (kit_target_new(&g_ctx, &opts, &target) != KIT_OK || !target) return NULL; KitCompiler* cc = NULL; - if (kit_compiler_new(*t, &g_ctx, &cc) != KIT_OK || !cc) return NULL; + if (kit_compiler_new(target, &g_ctx, &cc) != KIT_OK || !cc) { + kit_target_free(target); + return NULL; + } return (Compiler*)cc; } +static void free_compiler(Compiler* c) { + if (!c) return; + const KitTarget* target = kit_compiler_target((KitCompiler*)c); + kit_compiler_free((KitCompiler*)c); + kit_target_free((KitTarget*)target); +} + /* ---- builders ----------------------------------------------------- */ static void build_short_import_amd64(uint8_t buf[SHIM_TOTAL_LEN]) { @@ -198,7 +213,7 @@ static ObjBuilder* build_program(Compiler* c) { /* ---- main --------------------------------------------------------- */ int main(void) { - KitTarget t; + KitTargetSpec t; target_x64_windows(&t); Compiler* c = make_compiler(&t); if (!c) { @@ -208,7 +223,7 @@ int main(void) { if (setjmp(c->panic)) { fprintf(stderr, "FAIL: panic during pe-mixed-archive\n"); compiler_run_cleanups(c); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); return 1; } @@ -224,7 +239,7 @@ int main(void) { EXPECT(helper_bytes != NULL && helper_len > 0, "build_helper_object produced %zu bytes", helper_len); if (!helper_bytes) { - kit_compiler_free((KitCompiler*)c); + free_compiler(c); return 1; } @@ -244,7 +259,7 @@ int main(void) { if (kit_writer_mem(&g_heap, &aw) != KIT_OK || !aw) { fprintf(stderr, "FAIL: writer_mem for archive\n"); free(helper_bytes); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); return 1; } KitArWriteOptions opts; @@ -261,7 +276,7 @@ int main(void) { "archive empty after kit_ar_write (len=%zu)", ar_len); if (!ar_bytes) { free(helper_bytes); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); return 1; } @@ -325,7 +340,7 @@ int main(void) { free(ar_bytes); free(helper_bytes); - kit_compiler_free((KitCompiler*)c); + free_compiler(c); if (g_failures) { fprintf(stderr, "FAILED %d assertion(s)\n", g_failures); diff --git a/test/compile/run.sh b/test/compile/run.sh @@ -4,9 +4,9 @@ # output existence, and a few symbol/text markers via the shared kit_* verbs. # # Coverage: per-language compile (C / toy / wasm), the emit modes -# (-S / --emit=c / --emit=ir), check-only, default output naming, the -# frontend-owned wasm feature flags, and the negative paths that exercise the -# capability gate, the frontend option parser, and the no-link policy. +# (-S / --emit=c / --emit=ir), check-only, default output naming, the target +# feature flag path, and the negative paths that exercise the capability gate, +# the target/frontend option parsers, and the no-link policy. set -u @@ -57,8 +57,9 @@ assert_file_exists toy-obj-exists prog_toy.o run_ok wasm-compile "$KIT" compile -c prog.wat -o prog_wat.o assert_file_exists wasm-obj-exists prog_wat.o -# Frontend-owned option: the wasm parser accepts -m[no-]feature. -run_ok wasm-feature-flag "$KIT" compile -c -mno-feature=tail-calls prog.wat -o prog_wat2.o +# Target-owned option: the driver lowers feature flags into KitTargetOptions. +run_ok wasm-feature-flag "$KIT" compile -target wasm32-none -c \ + -mattr=-tail-calls prog.wat -o prog_wat2.o # ---- emit modes ------------------------------------------------------------ run_ok emit-asm "$KIT" compile -S -Iinc hello.c -o hello.s @@ -79,8 +80,9 @@ else not_ok check-only-no-output; fi run_fail neg-cpp-on-wasm "$KIT" compile -Iinc -c prog.wat -o x.o # A frontend with no option parser rejects any leftover flag. run_fail neg-unknown-toy-flag "$KIT" compile --bogus -c prog.toy -o x.o -# The wasm parser rejects an unknown feature name. -run_fail neg-bad-wasm-feature "$KIT" compile -mfeature=nope -c prog.wat -o x.o +# The target parser rejects an unknown feature name. +run_fail neg-bad-target-feature "$KIT" compile -target wasm32-none \ + -mattr=+nope -c prog.wat -o x.o # compile never links: object/archive inputs are refused. run_fail neg-link-input "$KIT" compile hello.o # One language per invocation. diff --git a/test/debug/cfi_unit.c b/test/debug/cfi_unit.c @@ -89,7 +89,7 @@ typedef struct CfiExpect { } CfiExpect; static void check_arch(const CfiExpect* ex) { - KitTarget t; + KitTargetSpec t; Compiler* c; ObjBuilder* ob; ObjSecId text_sec; diff --git a/test/debug/roundtrip_unit.c b/test/debug/roundtrip_unit.c @@ -77,7 +77,7 @@ static u8 byte_at(const Section* s, u32 ofs) { #define ARCH_NOP_RV64 0x00000013u /* ADDI x0, x0, 0 */ static int run_one(KitArchKind arch, uint32_t nop_word, const char* tag) { - KitTarget t; + KitTargetSpec t; Compiler* c; ObjBuilder* ob; Debug* d; @@ -304,7 +304,7 @@ static void run_arch_register_checks(void) { } static int run_x64_debug_line_check(void) { - KitTarget xt; + KitTargetSpec xt; Compiler* xc; ObjBuilder* xob; Debug* xd; diff --git a/test/dwarf/dwarf_test.c b/test/dwarf/dwarf_test.c @@ -1059,7 +1059,7 @@ static void run_tests(KitDebugInfo* di) { } int main(void) { - KitTarget target; + KitTargetSpec target; kit_unit_init(&g_u); g_u.ctx.now = -1; target = kit_unit_target(KIT_ARCH_ARM_64, KIT_OS_LINUX, KIT_OBJ_ELF); diff --git a/test/elf/kit-roundtrip.c b/test/elf/kit-roundtrip.c @@ -120,7 +120,7 @@ int main(int argc, char** argv) { return 1; } - KitTarget target; + KitTargetSpec target; if (kit_detect_target(in_data, in_len, &target) != KIT_OK || target.obj != KIT_OBJ_ELF) { fprintf(stderr, "error: %s: not a recognized object file\n", in_path); diff --git a/test/elf/run.sh b/test/elf/run.sh @@ -65,7 +65,7 @@ command -v readelf >/dev/null 2>&1 && have_llvm_readelf=1 command -v python3 >/dev/null 2>&1 && have_python3=1 # ----- locate kit-roundtrip ------------------------------------------- -# Built as a Make target (test/test.mk) so it picks up libkit.a changes. +# Built as a Make target (mk/test.mk) so it picks up libkit.a changes. ROUNDTRIP_BIN="$BUILD_DIR/kit-roundtrip" roundtrip_ok=0 @@ -92,7 +92,7 @@ fi # Map (KIT_TEST_ARCH, KIT_TEST_OBJ) (defaults aa64+elf) to the clang # `--target=` triple the Layer B golden objects are compiled against. # kit-roundtrip then detects the input's e_machine / Mach-O cputype and -# constructs a matching KitTarget, so the readelf/objdump diff stays +# constructs a matching KitTargetSpec, so the readelf/objdump diff stays # apples-to-apples per target. case "${KIT_TEST_OBJ:-elf}" in elf) diff --git a/test/elf/unit/align_4k.c b/test/elf/unit/align_4k.c @@ -62,7 +62,7 @@ static const uint8_t TEXT_BYTES[8] = { #define WANT_ALIGN 4096u int main(void) { - KitTarget target; + KitTargetSpec target; if (kit_test_target_init(&target) != 0) { fprintf(stderr, "FAIL: kit_test_target_init\n"); return 1; @@ -73,9 +73,18 @@ int main(void) { .diag = &g_diag, .metrics = NULL, .now = -1}; + KitTargetOptions target_opts; + memset(&target_opts, 0, sizeof target_opts); + target_opts.spec = target; + KitTarget* kt = NULL; + if (kit_target_new(&ctx, &target_opts, &kt) != KIT_OK || !kt) { + fprintf(stderr, "FAIL: kit_target_new\n"); + return 1; + } KitCompiler* cc = NULL; - if (kit_compiler_new(target, &ctx, &cc) != KIT_OK || !cc) { + if (kit_compiler_new(kt, &ctx, &cc) != KIT_OK || !cc) { fprintf(stderr, "FAIL: kit_compiler_new\n"); + kit_target_free(kt); return 1; } KitObjBuilder* in = NULL; @@ -140,6 +149,7 @@ int main(void) { free(roundtrip); kit_obj_builder_free(in); kit_compiler_free(cc); + kit_target_free(kt); if (g_failures) { fprintf(stderr, "%d failure(s)\n", g_failures); diff --git a/test/elf/unit/groupiter.c b/test/elf/unit/groupiter.c @@ -66,7 +66,7 @@ static int name_eq(KitSlice s, const char* want) { } int main(void) { - KitTarget target; + KitTargetSpec target; if (kit_test_target_init(&target) != 0) { fprintf(stderr, "FAIL: kit_test_target_init\n"); return 1; @@ -77,9 +77,18 @@ int main(void) { .diag = &g_diag, .metrics = NULL, .now = -1}; + KitTargetOptions target_opts; + memset(&target_opts, 0, sizeof target_opts); + target_opts.spec = target; + KitTarget* kt = NULL; + if (kit_target_new(&ctx, &target_opts, &kt) != KIT_OK || !kt) { + fprintf(stderr, "FAIL: kit_target_new\n"); + return 1; + } KitCompiler* cc = NULL; - if (kit_compiler_new(target, &ctx, &cc) != KIT_OK || !cc) { + if (kit_compiler_new(kt, &ctx, &cc) != KIT_OK || !cc) { fprintf(stderr, "FAIL: kit_compiler_new\n"); + kit_target_free(kt); return 1; } /* ---- build ---- */ @@ -198,6 +207,7 @@ int main(void) { free(roundtrip); kit_obj_builder_free(in); kit_compiler_free(cc); + kit_target_free(kt); if (g_failures) { fprintf(stderr, "%d failure(s)\n", g_failures); diff --git a/test/elf/unit/mutate.c b/test/elf/unit/mutate.c @@ -66,7 +66,7 @@ static const uint8_t TEXT_BYTES[8] = { }; int main(void) { - KitTarget target; + KitTargetSpec target; if (kit_test_target_init(&target) != 0) { fprintf(stderr, "FAIL: kit_test_target_init\n"); return 1; @@ -76,9 +76,18 @@ int main(void) { .diag = &g_diag, .metrics = NULL, .now = -1}; + KitTargetOptions target_opts; + memset(&target_opts, 0, sizeof target_opts); + target_opts.spec = target; + KitTarget* kt = NULL; + if (kit_target_new(&ctx, &target_opts, &kt) != KIT_OK || !kt) { + fprintf(stderr, "FAIL: kit_target_new\n"); + return 1; + } KitCompiler* cc = NULL; - if (kit_compiler_new(target, &ctx, &cc) != KIT_OK || !cc) { + if (kit_compiler_new(kt, &ctx, &cc) != KIT_OK || !cc) { fprintf(stderr, "FAIL: kit_compiler_new\n"); + kit_target_free(kt); return 1; } /* ---- build ---- */ @@ -272,6 +281,7 @@ int main(void) { free(roundtrip); kit_obj_builder_free(ob); kit_compiler_free(cc); + kit_target_free(kt); if (g_failures) { fprintf(stderr, "%d failure(s)\n", g_failures); diff --git a/test/elf/unit/smoke.c b/test/elf/unit/smoke.c @@ -73,7 +73,7 @@ static const uint8_t TEXT_BYTES[8] = { 0x40, 0x05, 0x80, 0x52, 0xc0, 0x03, 0x5f, 0xd6, }; -static KitObjBuilder* build_input(KitCompiler* c, KitTarget target) { +static KitObjBuilder* build_input(KitCompiler* c, KitTargetSpec target) { KitObjBuilder* ob = NULL; KitObjSection sec_text = KIT_SECTION_NONE; KitObjSection sec_data = KIT_SECTION_NONE; @@ -240,7 +240,7 @@ static void verify_shape(KitObjFile* f) { /* ---- main ---- */ int main(void) { - KitTarget target; + KitTargetSpec target; if (kit_test_target_init(&target) != 0) { fprintf(stderr, "FAIL: kit_test_target_init\n"); return 1; @@ -252,9 +252,18 @@ int main(void) { ctx.diag = &g_diag; ctx.now = -1; + KitTargetOptions target_opts; + memset(&target_opts, 0, sizeof target_opts); + target_opts.spec = target; + KitTarget* kt = NULL; + if (kit_target_new(&ctx, &target_opts, &kt) != KIT_OK || !kt) { + fprintf(stderr, "FAIL: kit_target_new\n"); + return 1; + } KitCompiler* cc = NULL; - if (kit_compiler_new(target, &ctx, &cc) != KIT_OK || !cc) { + if (kit_compiler_new(kt, &ctx, &cc) != KIT_OK || !cc) { fprintf(stderr, "FAIL: kit_compiler_new\n"); + kit_target_free(kt); return 1; } /* Build, emit, read back, inspect. */ @@ -289,6 +298,7 @@ int main(void) { free(roundtrip); kit_obj_builder_free(in); kit_compiler_free(cc); + kit_target_free(kt); if (g_failures) { fprintf(stderr, "%d failure(s)\n", g_failures); diff --git a/test/elf/unit/x64_disasm_annotations.c b/test/elf/unit/x64_disasm_annotations.c @@ -53,8 +53,8 @@ static int g_failures; } \ } while (0) -static KitTarget x64_elf_target(void) { - KitTarget t; +static KitTargetSpec x64_elf_target(void) { + KitTargetSpec t; memset(&t, 0, sizeof t); t.arch = KIT_ARCH_X86_64; t.os = KIT_OS_LINUX; @@ -64,7 +64,7 @@ static KitTarget x64_elf_target(void) { return t; } -static KitObjBuilder* build_input(KitCompiler* c, KitTarget target) { +static KitObjBuilder* build_input(KitCompiler* c, KitTargetSpec target) { KitObjBuilder* ob = NULL; KitObjSection sec_text = KIT_SECTION_NONE; CHECK(kit_obj_builder_new(c, &ob) == KIT_OK && ob, "kit_obj_builder_new"); @@ -253,7 +253,7 @@ static void check_disasm_annotations(const KitSlice* bytes, } int main(void) { - KitTarget target = x64_elf_target(); + KitTargetSpec target = x64_elf_target(); KitContext ctx; KitCompiler* cc = NULL; KitObjBuilder* ob; @@ -267,8 +267,17 @@ int main(void) { ctx.diag = &g_diag; ctx.now = -1; - if (kit_compiler_new(target, &ctx, &cc) != KIT_OK || !cc) { + KitTargetOptions target_opts; + memset(&target_opts, 0, sizeof target_opts); + target_opts.spec = target; + KitTarget* kt = NULL; + if (kit_target_new(&ctx, &target_opts, &kt) != KIT_OK || !kt) { + fprintf(stderr, "FAIL: kit_target_new\n"); + return 1; + } + if (kit_compiler_new(kt, &ctx, &cc) != KIT_OK || !cc) { fprintf(stderr, "FAIL: kit_compiler_new\n"); + kit_target_free(kt); return 1; } @@ -285,6 +294,7 @@ int main(void) { kit_writer_close(w); kit_obj_builder_free(ob); kit_compiler_free(cc); + kit_target_free(kt); if (g_failures) { fprintf(stderr, "%d failure(s)\n", g_failures); diff --git a/test/emu/rv64_interp_smoke_test.c b/test/emu/rv64_interp_smoke_test.c @@ -197,7 +197,7 @@ static KitCompiler* new_host_compiler(void) { KitArchKind arch; KitOSKind os; KitObjFmt obj; - KitTarget t; + KitTargetSpec t; KitCompiler* c = NULL; #if defined(__x86_64__) || defined(_M_X64) arch = KIT_ARCH_X86_64; @@ -468,7 +468,7 @@ static int run_guest(const unsigned char* elf, size_t elf_len, KitEmuMode mode, KitCompiler* c = new_host_compiler(); KitJitHost host; KitEmuOptions opts; - KitTarget gt; + KitTargetSpec gt; int exit_code = -1; KitStatus st; long ps; diff --git a/test/emu/rv64_smoke_test.c b/test/emu/rv64_smoke_test.c @@ -199,7 +199,7 @@ static KitCompiler* new_host_compiler(void) { KitArchKind arch; KitOSKind os; KitObjFmt obj; - KitTarget t; + KitTargetSpec t; KitCompiler* c = NULL; #if defined(__x86_64__) || defined(_M_X64) arch = KIT_ARCH_X86_64; @@ -1011,7 +1011,7 @@ static void emu_fixture_expect_exit_with_bindings( KitCompiler* c; KitJitHost host; KitEmuOptions opts; - KitTarget guest_target; + KitTargetSpec guest_target; KitStatus st; int exit_code = -1; long ps; diff --git a/test/emu/rv64_vm_unit_test.c b/test/emu/rv64_vm_unit_test.c @@ -3,8 +3,9 @@ * These exercise INTERNAL units that have no public API surface — the rv64 * decoder (ArchDecodeOps), the guest address space (EmuAddrSpace), and the * Linux syscall handler (mmap/mprotect/munmap) — so this binary links the - * library objects directly (test.mk), unlike rv64_smoke_test.c which drives the - * emulator end-to-end through the public kit_emu_* API and links the archive. + * library objects directly (mk/test.mk), unlike rv64_smoke_test.c which drives + * the emulator end-to-end through the public kit_emu_* API and links the + * archive. */ #include <kit/compile.h> @@ -27,7 +28,7 @@ static KitUnit g_u; #define EXPECT(cond, ...) CU_EXPECT(&g_u, cond, __VA_ARGS__) static KitCompiler* new_compiler(void) { - KitTarget t = kit_unit_target(KIT_ARCH_RV64, KIT_OS_LINUX, KIT_OBJ_ELF); + KitTargetSpec t = kit_unit_target(KIT_ARCH_RV64, KIT_OS_LINUX, KIT_OBJ_ELF); KitCompiler* c = NULL; if (kit_unit_compiler_new(&g_u, t, &c) != KIT_OK || !c) { fprintf(stderr, "compiler_new failed\n"); @@ -40,7 +41,7 @@ static KitCompiler* new_host_compiler(void) { KitArchKind arch; KitOSKind os; KitObjFmt obj; - KitTarget t; + KitTargetSpec t; KitCompiler* c = NULL; #if defined(__x86_64__) || defined(_M_X64) arch = KIT_ARCH_X86_64; diff --git a/test/interp/interp_smoke_test.c b/test/interp/interp_smoke_test.c @@ -38,7 +38,7 @@ typedef struct TestCtx { } TestCtx; static void tc_init(TestCtx* tc) { - KitTarget target; + KitTargetSpec target; KitCgBuiltinTypes b; memset(tc, 0, sizeof *tc); target = kit_unit_target(KIT_ARCH_ARM_64, KIT_OS_MACOS, KIT_OBJ_MACHO); diff --git a/test/lib/kit_corpus.sh b/test/lib/kit_corpus.sh @@ -34,6 +34,10 @@ # KIT_FLUSH_VERIFY optional fn, called per queued-E item after flush: args # (label, payload, rc); return 0 to keep pass, 1 to fail # +# Debugging: +# KIT_CORPUS_TRACE=1 force serial dispatch and print each item/lane before it +# runs; deferred exec queues are flushed after each item. +# # Per-item vars the engine sets before each kit_lane_<ID> call: # KIT_BASE KIT_SRC KIT_WORK KIT_OPT KIT_LANE KIT_ARCH KIT_OBJ KIT_TUPLE KIT_EXPECTED # KIT_NAME (display label "base" or "base/Oopt") KIT_SIDECAR_DIR @@ -61,6 +65,18 @@ kit_now_ms() { fi } +kit_corpus_trace_enabled() { + case "${KIT_CORPUS_TRACE:-0}" in + ""|0|false|FALSE|no|NO) return 1 ;; + *) return 0 ;; + esac +} + +kit_corpus_trace() { + kit_corpus_trace_enabled || return 0 + printf 'TRACE %s\n' "$*" >&2 +} + # Deferred-exec bookkeeping (populated ONLY during serial execution / replay, # never inside a worker — workers merely emit QUEUE_E events). CFQ_LABELS=(); CFQ_RCS=(); CFQ_EXPS=(); CFQ_PAYLOADS=() @@ -116,6 +132,7 @@ kit_corpus_item() { else KIT_NAME="$KIT_BASE/O$KIT_OPT"; KIT_WORK="$KIT_BUILD_DIR/$KIT_BASE/O$KIT_OPT/$KIT_TUPLE" fi + kit_corpus_trace "item $KIT_NAME tuple=$KIT_TUPLE src=$KIT_SRC" rm -rf "$KIT_WORK"; mkdir -p "$KIT_WORK" KIT_EXPECTED=0 @@ -152,8 +169,10 @@ kit_corpus_item() { esac # per-lane sidecar skip (.<lane>.skip) if reason=$(kit_skip_sidecar "$KIT_SIDECAR_DIR" "$KIT_BASE" "" "$lane"); then + kit_corpus_trace "lane $KIT_NAME/$lane skip-sidecar" kit_skip "$KIT_NAME/$lane" "$reason"; continue fi + kit_corpus_trace "lane $KIT_NAME/$lane" "kit_lane_$lane" done } @@ -198,6 +217,7 @@ kit_corpus_replay() { # ---- deferred-exec flush + per-case verification --------------------------- kit_corpus_flush_e() { [ "${#CFQ_LABELS[@]}" -eq 0 ] && return 0 + kit_corpus_trace "flush E queued=${#CFQ_LABELS[@]}" exec_target_flush local i rc ok for i in "${!CFQ_LABELS[@]}"; do @@ -253,12 +273,18 @@ kit_corpus_run() { done local jobs; jobs="$(kit_parallel_jobs)" || jobs=1 + if kit_corpus_trace_enabled; then + KIT_PARALLELIZABLE=0 + jobs=1 + kit_corpus_trace "dispatch serial items=${#KIT_ITEMS[@]} lanes=${KIT_LANES# }" + fi if [ "$KIT_PARALLELIZABLE" = "1" ] && [ "$jobs" -gt 1 ] && [ "${#KIT_ITEMS[@]}" -gt 4 ]; then kit_corpus_dispatch_parallel "$jobs" else local idx=0 for item in "${KIT_ITEMS[@]}"; do KIT_EV=; kit_corpus_item "$item" + kit_corpus_trace_enabled && kit_corpus_flush_e idx=$((idx + 1)) done fi diff --git a/test/lib/kit_test_target.h b/test/lib/kit_test_target.h @@ -1,4 +1,4 @@ -/* Shared KitTarget setup for C test runners. +/* Shared KitTargetSpec setup for C test runners. * * Reads two env vars to pick a (arch, os, obj) triple: * @@ -32,7 +32,7 @@ static inline const char* kit_test_obj_name(void) { return o; } -static inline int kit_test_target_init(KitTarget* t) { +static inline int kit_test_target_init(KitTargetSpec* t) { memset(t, 0, sizeof *t); t->ptr_size = 8; t->ptr_align = 8; diff --git a/test/lib/kit_unit.h b/test/lib/kit_unit.h @@ -3,8 +3,8 @@ * The unit suite copy-pasted the same prologue into ~18 files: a * malloc/realloc/free shim wrapping the host allocator into a KitHeap, a * diagnostic sink that prints to stderr, an EXPECT/CHECK macro, a - * KitTarget + KitContext + kit_compiler_new dance, and a pass/fail - * counter checked in main(). This header folds all of that into one + * KitTargetSpec + KitTarget + KitContext + kit_compiler_new dance, and a + * pass/fail counter checked in main(). This header folds all of that into one * stack-resident KitUnit context. * * Design constraints honored: @@ -36,10 +36,16 @@ /* One context per test. Zero-initialize with kit_unit_init. The diag sink * captures the most recent message body into last_diag (before any * suppression), which expected-panic tests strstr() against. */ +typedef struct KitUnitTargetNode { + KitTarget* target; + struct KitUnitTargetNode* next; +} KitUnitTargetNode; + typedef struct KitUnit { KitHeap heap; KitDiagSink diag; KitContext ctx; + KitUnitTargetNode* targets; char last_diag[256]; int suppress_fatal; /* when set, FATAL diagnostics are captured but not * printed — used by tests that drive a deliberate @@ -102,9 +108,9 @@ static inline void kit_unit_init(KitUnit* u) { u->ctx.now = 0; } -static inline KitTarget kit_unit_target(KitArchKind arch, KitOSKind os, - KitObjFmt obj) { - KitTarget t; +static inline KitTargetSpec kit_unit_target(KitArchKind arch, KitOSKind os, + KitObjFmt obj) { + KitTargetSpec t; memset(&t, 0, sizeof t); t.arch = arch; t.os = os; @@ -114,9 +120,44 @@ static inline KitTarget kit_unit_target(KitArchKind arch, KitOSKind os, return t; } -static inline KitStatus kit_unit_compiler_new(KitUnit* u, KitTarget t, +static inline KitStatus kit_unit_compiler_new(KitUnit* u, KitTargetSpec t, KitCompiler** out) { - return kit_compiler_new(t, &u->ctx, out); + KitTargetOptions opts; + KitTarget* target = NULL; + KitUnitTargetNode* node = NULL; + KitStatus st; + if (out) *out = NULL; + memset(&opts, 0, sizeof opts); + opts.spec = t; + st = kit_target_new(&u->ctx, &opts, &target); + if (st != KIT_OK) return st; + node = (KitUnitTargetNode*)kit_unit_alloc(&u->heap, sizeof(*node), + _Alignof(KitUnitTargetNode)); + if (!node) { + kit_target_free(target); + return KIT_NOMEM; + } + st = kit_compiler_new(target, &u->ctx, out); + if (st != KIT_OK) { + kit_unit_free(&u->heap, node, sizeof(*node)); + kit_target_free(target); + return st; + } + node->target = target; + node->next = u->targets; + u->targets = node; + return KIT_OK; +} + +static inline void kit_unit_fini(KitUnit* u) { + KitUnitTargetNode* n = u ? u->targets : NULL; + while (n) { + KitUnitTargetNode* next = n->next; + kit_target_free(n->target); + kit_unit_free(&u->heap, n, sizeof(*n)); + n = next; + } + if (u) u->targets = NULL; } /* Byte-substring search, for tests asserting emitted machine code contains a @@ -140,7 +181,11 @@ static inline void kit_unit_summary(KitUnit* u, const char* name) { } } -static inline int kit_unit_status(KitUnit* u) { return u->fails ? 1 : 0; } +static inline int kit_unit_status(KitUnit* u) { + int rc = u->fails ? 1 : 0; + kit_unit_fini(u); + return rc; +} /* Record one check. `cond` is evaluated EXACTLY ONCE — it may have side * effects (e.g. EXPECT(kit_ar_iter_next(it, &m), ...) advances an diff --git a/test/link/harness/jit_runner.c b/test/link/harness/jit_runner.c @@ -77,6 +77,11 @@ static void diag_fn(KitDiagSink* s, KitDiagKind k, KitSrcLoc loc, } static KitDiagSink g_diag = {diag_fn, NULL, 0, 0}; +static void free_compiler_target(KitCompiler* compiler, KitTarget* target) { + kit_compiler_free(compiler); + kit_target_free(target); +} + /* Mirrors driver/env.c — see that file for the strict-W^X dual-mapping * rationale. */ #if defined(__APPLE__) @@ -489,7 +494,7 @@ int main(int argc, char** argv) { } } - KitTarget target; + KitTargetSpec target; if (kit_test_target_init(&target) != 0) { fprintf(stderr, "jit_runner: kit_test_target_init failed\n"); return 2; @@ -501,9 +506,18 @@ int main(int argc, char** argv) { ctx.diag = &g_diag; ctx.now = -1; + KitTargetOptions target_opts; + memset(&target_opts, 0, sizeof target_opts); + target_opts.spec = target; + KitTarget* kt = NULL; + if (kit_target_new(&ctx, &target_opts, &kt) != KIT_OK || !kt) { + fprintf(stderr, "jit-runner: target_new failed\n"); + return 2; + } KitCompiler* c = NULL; - if (kit_compiler_new(target, &ctx, &c) != KIT_OK || !c) { + if (kit_compiler_new(kt, &ctx, &c) != KIT_OK || !c) { fprintf(stderr, "jit-runner: compiler_new failed\n"); + kit_target_free(kt); return 2; } @@ -530,7 +544,7 @@ int main(int argc, char** argv) { size_t slen; if (slurp(script_path, &sbytes, &slen)) { fprintf(stderr, "jit-runner: cannot read %s\n", script_path); - kit_compiler_free(c); + free_compiler_target(c, kt); return 2; } KitSlice script_text = {.s = (const char*)sbytes, .len = slen}; @@ -539,7 +553,7 @@ int main(int argc, char** argv) { if (prc != KIT_OK) { fprintf(stderr, "jit-runner: linker script parse failed: %s\n", script_path); - kit_compiler_free(c); + free_compiler_target(c, kt); return 1; } opts.linker_script = script; @@ -557,7 +571,7 @@ int main(int argc, char** argv) { kit_link_session_free(link); if (script) kit_link_script_free(&ctx, script); for (int i = 0; i < nbufs; i++) free(bufs[i]); - kit_compiler_free(c); + free_compiler_target(c, kt); return 1; } kit_link_session_free(link); @@ -569,7 +583,7 @@ int main(int argc, char** argv) { fprintf(stderr, "jit-runner: sentinel lookup unexpectedly non-NULL\n"); kit_jit_run_dtors(jit); kit_jit_free(jit); - kit_compiler_free(c); + free_compiler_target(c, kt); return 1; } @@ -581,7 +595,7 @@ int main(int argc, char** argv) { check_absent); kit_jit_run_dtors(jit); kit_jit_free(jit); - kit_compiler_free(c); + free_compiler_target(c, kt); return absent_ok ? 0 : 1; } @@ -593,7 +607,7 @@ int main(int argc, char** argv) { check_present); kit_jit_run_dtors(jit); kit_jit_free(jit); - kit_compiler_free(c); + free_compiler_target(c, kt); return present_ok ? 0 : 1; } @@ -650,7 +664,7 @@ int main(int argc, char** argv) { free(instance); kit_jit_run_dtors(jit); kit_jit_free(jit); - kit_compiler_free(c); + free_compiler_target(c, kt); return 1; } for (uint32_t i = 0; i < 8u; ++i) @@ -678,7 +692,7 @@ int main(int argc, char** argv) { free(instance); kit_jit_run_dtors(jit); kit_jit_free(jit); - kit_compiler_free(c); + free_compiler_target(c, kt); return 1; } ((WasmRunnerInitFn)wasm_init)(instance); @@ -714,6 +728,6 @@ int main(int argc, char** argv) { } kit_jit_free(jit); - kit_compiler_free(c); + free_compiler_target(c, kt); return result; } diff --git a/test/link/harness/link_exe_runner.c b/test/link/harness/link_exe_runner.c @@ -53,6 +53,11 @@ static void diag_fn(KitDiagSink* s, KitDiagKind k, KitSrcLoc loc, } static KitDiagSink g_diag = {diag_fn, NULL, 0, 0}; +static void free_compiler_target(KitCompiler* compiler, KitTarget* target) { + kit_compiler_free(compiler); + kit_target_free(target); +} + static int slurp(const char* path, uint8_t** out, size_t* len) { int fd = open(path, O_RDONLY); if (fd < 0) return -1; @@ -176,7 +181,7 @@ int main(int argc, char** argv) { return 2; } - KitTarget target; + KitTargetSpec target; if (kit_test_target_init(&target) != 0) { fprintf(stderr, "link_exe_runner: kit_test_target_init failed\n"); return 2; @@ -188,9 +193,18 @@ int main(int argc, char** argv) { ctx.diag = &g_diag; ctx.now = -1; + KitTargetOptions target_opts; + memset(&target_opts, 0, sizeof target_opts); + target_opts.spec = target; + KitTarget* kt = NULL; + if (kit_target_new(&ctx, &target_opts, &kt) != KIT_OK || !kt) { + fprintf(stderr, "link-exe-runner: target_new failed\n"); + return 2; + } KitCompiler* c = NULL; - if (kit_compiler_new(target, &ctx, &c) != KIT_OK || !c) { + if (kit_compiler_new(kt, &ctx, &c) != KIT_OK || !c) { fprintf(stderr, "link-exe-runner: compiler_new failed\n"); + kit_target_free(kt); return 2; } @@ -207,7 +221,7 @@ int main(int argc, char** argv) { size_t slen; if (slurp(script_path, &sbytes, &slen)) { fprintf(stderr, "link-exe-runner: cannot read %s\n", script_path); - kit_compiler_free(c); + free_compiler_target(c, kt); return 2; } KitSlice script_text = {.s = (const char*)sbytes, .len = slen}; @@ -216,7 +230,7 @@ int main(int argc, char** argv) { if (prc != KIT_OK) { fprintf(stderr, "link-exe-runner: linker script parse failed: %s\n", script_path); - kit_compiler_free(c); + free_compiler_target(c, kt); return 1; } opts.linker_script = script; @@ -224,7 +238,7 @@ int main(int argc, char** argv) { KitWriter* w = NULL; if (kit_writer_mem(&g_heap, &w) != KIT_OK || !w) { - kit_compiler_free(c); + free_compiler_target(c, kt); return 2; } @@ -244,7 +258,7 @@ int main(int argc, char** argv) { if (script) kit_link_script_free(&ctx, script); for (int i = 0; i < nbufs; i++) free(bufs[i]); kit_writer_close(w); - kit_compiler_free(c); + free_compiler_target(c, kt); return 1; } for (int i = 0; i < nbufs; i++) free(bufs[i]); @@ -255,7 +269,7 @@ int main(int argc, char** argv) { kit_link_session_free(link); if (script) kit_link_script_free(&ctx, script); kit_writer_close(w); - kit_compiler_free(c); + free_compiler_target(c, kt); if (wrc) { fprintf(stderr, "link-exe-runner: write failed\n"); return 2; diff --git a/test/link/rv64_jit_test.c b/test/link/rv64_jit_test.c @@ -24,7 +24,7 @@ * the host CPU can't decode them. The test prints "SKIP <reason>" and * exits 77 (the GNU autotools "skipped" convention) when this happens. * - * Wired into test.mk via test-rv64-jit. Always builds; calls only on + * Wired into mk/test.mk via test-rv64-jit. Always builds; calls only on * rv64 Linux. This mirrors the value-proposition outlined in * doc/RV64_PARITY_CHECKLIST.md: have the code path in place for the day * someone runs kit on a rv64 dev box. */ @@ -190,7 +190,8 @@ int main(void) { if (ps > 0) g_execmem.page_size = (size_t)ps; } - KitTarget target = kit_unit_target(KIT_ARCH_RV64, KIT_OS_LINUX, KIT_OBJ_ELF); + KitTargetSpec target = + kit_unit_target(KIT_ARCH_RV64, KIT_OS_LINUX, KIT_OBJ_ELF); kit_unit_init(&g_u); g_u.ctx.now = -1; diff --git a/test/macho/kit-roundtrip-macho.c b/test/macho/kit-roundtrip-macho.c @@ -113,7 +113,7 @@ int main(int argc, char** argv) { return 1; } - KitTarget target; + KitTargetSpec target; if (kit_detect_target(in_data, in_len, &target) != KIT_OK || target.obj != KIT_OBJ_MACHO) { fprintf(stderr, "error: %s: not a recognized Mach-O object file\n", diff --git a/test/opt/cg_ir_lower_test.c b/test/opt/cg_ir_lower_test.c @@ -26,7 +26,7 @@ typedef struct TestCtx { } TestCtx; static void tc_init(TestCtx* tc) { - KitTarget target; + KitTargetSpec target; KitCgBuiltinTypes b; memset(tc, 0, sizeof *tc); target = kit_unit_target(KIT_ARCH_ARM_64, KIT_OS_MACOS, KIT_OBJ_MACHO); diff --git a/test/opt/tiny_inline_test.c b/test/opt/tiny_inline_test.c @@ -38,7 +38,7 @@ typedef struct TestCtx { } TestCtx; static void tc_init(TestCtx* tc) { - KitTarget target; + KitTargetSpec target; KitCgBuiltinTypes b; KitCgFuncSig sig; KitCgFuncParam params[1]; diff --git a/test/parse/harness/parse_runner.c b/test/parse/harness/parse_runner.c @@ -241,7 +241,7 @@ static KitExecMem g_execmem = { /* ---- helpers ---- */ -static void target_from_env(KitTarget* t) { +static void target_from_env(KitTargetSpec* t) { if (kit_test_target_init(t) != 0) { fprintf(stderr, "parse-runner: kit_test_target_init failed\n"); exit(2); @@ -310,6 +310,25 @@ static void ctx_init(KitContext* ctx) { ctx->now = -1; } +static KitStatus compiler_new_for_target(KitTargetSpec spec, KitContext* ctx, + KitTarget** target_out, + KitCompiler** compiler_out) { + KitTargetOptions opts; + KitStatus st; + if (target_out) *target_out = NULL; + if (compiler_out) *compiler_out = NULL; + memset(&opts, 0, sizeof opts); + opts.spec = spec; + st = kit_target_new(ctx, &opts, target_out); + if (st != KIT_OK) return st; + st = kit_compiler_new(*target_out, ctx, compiler_out); + if (st != KIT_OK) { + kit_target_free(*target_out); + *target_out = NULL; + } + return st; +} + static int opt_level_from_env(void) { const char* s = getenv("KIT_OPT_LEVEL"); if (!s || !*s) return 0; @@ -385,8 +404,9 @@ static int mode_emit_impl(const char* src_path, const char* out_path, int emit_c) { uint8_t* src = NULL; size_t src_len = 0; - KitTarget tgt; + KitTargetSpec tgt; KitContext ctx; + KitTarget* target = NULL; KitCompiler* c = NULL; KitSlice in; KitCCompileOptions opts; @@ -403,7 +423,7 @@ static int mode_emit_impl(const char* src_path, const char* out_path, } target_from_env(&tgt); ctx_init(&ctx); - if (kit_compiler_new(tgt, &ctx, &c) != KIT_OK || !c) { + if (compiler_new_for_target(tgt, &ctx, &target, &c) != KIT_OK || !c) { free(src); return 2; } @@ -427,6 +447,7 @@ static int mode_emit_impl(const char* src_path, const char* out_path, KIT_OK) { kit_writer_close(w); kit_compiler_free(c); + kit_target_free(target); free(src); return 1; } @@ -451,6 +472,7 @@ static int mode_emit_impl(const char* src_path, const char* out_path, } kit_writer_close(w); kit_compiler_free(c); + kit_target_free(target); free(src); return rc; } @@ -478,8 +500,9 @@ call_with_aarch64_tls(int (*fn)(void), void* tls_block) { static int mode_jit(const char* src_path) { uint8_t* src = NULL; size_t src_len = 0; - KitTarget tgt; + KitTargetSpec tgt; KitContext ctx; + KitTarget* target = NULL; KitCompiler* c = NULL; KitSlice in; KitCCompileOptions opts; @@ -500,7 +523,7 @@ static int mode_jit(const char* src_path) { } target_from_env(&tgt); ctx_init(&ctx); - if (kit_compiler_new(tgt, &ctx, &c) != KIT_OK || !c) { + if (compiler_new_for_target(tgt, &ctx, &target, &c) != KIT_OK || !c) { free(src); return 2; } @@ -516,6 +539,7 @@ static int mode_jit(const char* src_path) { KIT_OK || !ob) { kit_compiler_free(c); + kit_target_free(target); free(src); return 1; } @@ -530,6 +554,7 @@ static int mode_jit(const char* src_path) { if (!add_runtime_archive(&rt_archive, &rt_data)) { kit_compiler_free(c); + kit_target_free(target); free(src); return 1; } @@ -539,6 +564,7 @@ static int mode_jit(const char* src_path) { kit_link_session_jit(link, &jit) != KIT_OK || !jit) { kit_link_session_free(link); kit_compiler_free(c); + kit_target_free(target); free(rt_data); free(src); return 1; @@ -574,6 +600,7 @@ static int mode_jit(const char* src_path) { kit_jit_free(jit); kit_compiler_free(c); + kit_target_free(target); free(rt_data); free(src); return result; diff --git a/test/parse/run.sh b/test/parse/run.sh @@ -57,6 +57,8 @@ # default run in parallel with a capped CPU-count default. # KIT_TEST_JOBS=N run up to N cases concurrently. # KIT_PARSE_PARALLEL=0 force serial dispatch. +# KIT_CORPUS_TRACE=1 force serial dispatch and print each item/lane before +# running it. # All lane hooks write only under KIT_WORK and record via kit_*, so the runner is # parallel-safe by construction. diff --git a/test/wasm/harness/wasm_tool.c b/test/wasm/harness/wasm_tool.c @@ -98,7 +98,7 @@ int main(int argc, char** argv) { return 2; } - KitTarget target; + KitTargetSpec target; if (kit_test_target_init(&target) != 0) { free(src); return 2; @@ -109,7 +109,16 @@ int main(int argc, char** argv) { ctx.diag = &g_diag; ctx.now = -1; KitCompiler* c = NULL; - if (kit_compiler_new(target, &ctx, &c) != KIT_OK || !c) { + KitTargetOptions target_opts; + KitTarget* kt = NULL; + memset(&target_opts, 0, sizeof target_opts); + target_opts.spec = target; + if (kit_target_new(&ctx, &target_opts, &kt) != KIT_OK || !kt) { + free(src); + return 2; + } + if (kit_compiler_new(kt, &ctx, &c) != KIT_OK || !c) { + kit_target_free(kt); free(src); return 2; } @@ -117,6 +126,7 @@ int main(int argc, char** argv) { KitWriter* w = NULL; if (kit_writer_mem(&g_heap, &w) != KIT_OK || !w) { kit_compiler_free(c); + kit_target_free(kt); free(src); return 2; } @@ -130,6 +140,7 @@ int main(int argc, char** argv) { if (!rc) rc = write_file(argv[3], out, out_len); kit_writer_close(w); kit_compiler_free(c); + kit_target_free(kt); free(src); return rc ? 1 : 0; }