diff --git a/stage0/sema.c b/stage0/sema.c index b8a23aa810..167ca7bb25 100644 --- a/stage0/sema.c +++ b/stage0/sema.c @@ -1,5 +1,7 @@ #include "sema.h" +#include "astgen.h" #include +#include #include #include @@ -124,6 +126,34 @@ static void instMapPut(InstMap* map, uint32_t zir_inst, AirInstRef ref) { map->items[zir_inst - map->start] = ref; } +// --- Comptime tracker helpers --- +// Track comptime type-info values for cross-module inline evaluation. +// tag: 0=none, 1=type_info(type), 2=float_info(bits) +#define CT_TAG_NONE 0 +#define CT_TAG_TYPE_INFO 1 +#define CT_TAG_FLOAT_INFO 2 + +static void ctTrack(Sema* sema, InternPoolIndex ip_idx, + uint8_t tag_val, uint32_t val) { + if (sema->ct_len < 16) { + sema->ct_keys[sema->ct_len] = ip_idx; + sema->ct_tags[sema->ct_len] = tag_val; + sema->ct_vals[sema->ct_len] = val; + sema->ct_len++; + } +} + +static uint8_t ctLookup(const Sema* sema, InternPoolIndex ip_idx, + uint32_t* val_out) { + for (uint32_t i = 0; i < sema->ct_len; i++) { + if (sema->ct_keys[i] == ip_idx) { + *val_out = sema->ct_vals[i]; + return sema->ct_tags[i]; + } + } + return CT_TAG_NONE; +} + // --- resolveInst --- // Ported from src/Sema.zig resolveInst. // Maps a ZIR Inst.Ref to an AIR Inst.Ref. @@ -747,9 +777,15 @@ static AirInstRef zirBitcast(Sema* sema, SemaBlock* block, uint32_t inst) { = sema->code.inst_datas[inst].pl_node.payload_index; ZirInstRef dest_ty_ref = sema->code.extra[payload_index]; ZirInstRef operand_ref = sema->code.extra[payload_index + 1]; - // Resolve destination type (must be pre-interned for now). - assert(dest_ty_ref < ZIR_REF_START_INDEX); - TypeIndex dest_ty = dest_ty_ref; + // Resolve destination type. + TypeIndex dest_ty; + if (dest_ty_ref < ZIR_REF_START_INDEX) { + dest_ty = dest_ty_ref; + } else { + AirInstRef resolved = resolveInst(sema, dest_ty_ref); + assert(AIR_REF_IS_IP(resolved)); + dest_ty = AIR_REF_TO_IP(resolved); + } AirInstRef operand = resolveInst(sema, operand_ref); AirInstData data; memset(&data, 0, sizeof(data)); @@ -900,8 +936,15 @@ static AirInstRef zirAsNode( = sema->code.inst_datas[inst].pl_node.payload_index; ZirInstRef dest_ty_ref = sema->code.extra[payload_index]; ZirInstRef operand_ref = sema->code.extra[payload_index + 1]; - assert(dest_ty_ref < ZIR_REF_START_INDEX); - TypeIndex dest_ty = dest_ty_ref; + TypeIndex dest_ty; + if (dest_ty_ref < ZIR_REF_START_INDEX) { + dest_ty = dest_ty_ref; + } else { + // Resolve through inst_map (comptime-evaluated type). + AirInstRef resolved = resolveInst(sema, dest_ty_ref); + assert(AIR_REF_IS_IP(resolved)); + dest_ty = AIR_REF_TO_IP(resolved); + } AirInstRef operand = resolveInst(sema, operand_ref); return semaCoerce(sema, block, dest_ty, operand); } @@ -986,6 +1029,209 @@ static FuncZirInfo parseFuncZir(Sema* sema, uint32_t inst) { return info; } +// findDeclImportPath: given a declaration name index, check if the +// declaration's value body contains a ZIR_INST_IMPORT. If so, return +// the import path string (from string_bytes). Returns NULL if not found. +static const char* findDeclImportPath(Sema* sema, uint32_t name_idx) { + uint32_t decl_inst = UINT32_MAX; + for (uint32_t i = 0; i < sema->num_decls; i++) { + if (sema->decl_names[i] == name_idx) { + decl_inst = sema->decl_insts[i]; + break; + } + } + if (decl_inst == UINT32_MAX) + return NULL; + + const uint32_t* value_body; + uint32_t value_body_len; + getParamBody(sema, decl_inst, &value_body, &value_body_len); + if (value_body_len == 0) + return NULL; + + for (uint32_t i = 0; i < value_body_len; i++) { + ZirInstTag itag = sema->code.inst_tags[value_body[i]]; + if (itag == ZIR_INST_IMPORT) { + uint32_t pl = sema->code.inst_datas[value_body[i]] + .pl_tok.payload_index; + // extra: {res_ty, path} where path is string_bytes index + uint32_t path_idx = sema->code.extra[pl + 1]; + return (const char*)&sema->code.string_bytes[path_idx]; + } + } + return NULL; +} + +// loadImportZir: load, parse, and AstGen an imported file. +// Returns a Zir with inst_len > 0 on success; inst_len == 0 on failure. +// The caller must call zirDeinit and astDeinit on the returned structures. +static Zir loadImportZir(const char* source_dir, + const char* import_path, Ast* out_ast) { + Zir empty_zir; + memset(&empty_zir, 0, sizeof(empty_zir)); + + // Build full path: source_dir + "/" + import_path + // Handle "./" prefix in import path. + const char* rel = import_path; + if (rel[0] == '.' && rel[1] == '/') + rel += 2; + + char full_path[1024]; + int n = snprintf(full_path, sizeof(full_path), "%s/%s", + source_dir, rel); + if (n < 0 || (size_t)n >= sizeof(full_path)) + return empty_zir; + + // Read the file. + FILE* f = fopen(full_path, "rb"); + if (!f) + return empty_zir; + fseek(f, 0, SEEK_END); + long file_size = ftell(f); + fseek(f, 0, SEEK_SET); + if (file_size <= 0) { + fclose(f); + return empty_zir; + } + char* src = malloc((size_t)file_size + 1); + if (!src) { + fclose(f); + return empty_zir; + } + size_t read_len = fread(src, 1, (size_t)file_size, f); + fclose(f); + src[read_len] = '\0'; + + // Parse. + *out_ast = astParse(src, (uint32_t)read_len); + + // AstGen. + Zir zir = astGen(out_ast); + return zir; +} + +// findFuncInstInZir: find a func/func_fancy instruction by name in a ZIR. +// Scans the ZIR's struct_decl (inst 0) for a declaration matching the +// given name, then searches its value body for a func instruction. +// Uses the same parsing as zirStructDecl. +// Returns the instruction index, or UINT32_MAX if not found. +static uint32_t findFuncInstInZir(const Zir* zir, const char* func_name) { + if (zir->inst_len == 0) + return UINT32_MAX; + + // Instruction 0 should be the struct_decl (extended with + // ZIR_EXT_STRUCT_DECL). + if (zir->inst_tags[0] != ZIR_INST_EXTENDED) + return UINT32_MAX; + if (zir->inst_datas[0].extended.opcode != ZIR_EXT_STRUCT_DECL) + return UINT32_MAX; + + // Parse struct_decl using the same logic as zirStructDecl. + uint16_t small = zir->inst_datas[0].extended.small; + uint32_t operand = zir->inst_datas[0].extended.operand; + uint32_t extra_index = operand + 6; // skip 6 fixed header u32s + + bool has_captures_len = (small & (1 << 0)) != 0; + bool has_fields_len = (small & (1 << 1)) != 0; + bool has_decls_len = (small & (1 << 2)) != 0; + bool has_backing_int = (small & (1 << 3)) != 0; + + uint32_t captures_len = 0; + if (has_captures_len) { + captures_len = zir->extra[extra_index]; + extra_index++; + } + if (has_fields_len) + extra_index++; + + uint32_t decls_len = 0; + if (has_decls_len) { + decls_len = zir->extra[extra_index]; + extra_index++; + } + + extra_index += captures_len * 2; // skip captures + + if (has_backing_int) { + uint32_t backing_int_body_len = zir->extra[extra_index]; + extra_index++; + if (backing_int_body_len == 0) + extra_index++; // backing_int_ref + else + extra_index += backing_int_body_len; + } + + // extra_index now points to the declaration instruction list. + for (uint32_t d = 0; d < decls_len; d++) { + uint32_t decl_inst = zir->extra[extra_index + d]; + if (zir->inst_tags[decl_inst] != ZIR_INST_DECLARATION) + continue; + + uint32_t payload + = zir->inst_datas[decl_inst].declaration.payload_index; + uint32_t flags_1 = zir->extra[payload + 5]; + uint32_t id = (flags_1 >> 27) & 0x1F; + + uint32_t di = payload + 6; + + // Extract declaration name. + uint32_t decl_name_idx = 0; + if (declIdHasName(id)) { + decl_name_idx = zir->extra[di]; + di++; + } + if (declIdHasLibName(id)) + di++; + + uint32_t type_body_len = 0; + if (declIdHasTypeBody(id)) { + type_body_len = zir->extra[di]; + di++; + } + + uint32_t align_body_len = 0; + uint32_t linksection_body_len = 0; + uint32_t addrspace_body_len = 0; + if (declIdHasSpecialBodies(id)) { + align_body_len = zir->extra[di]; + linksection_body_len = zir->extra[di + 1]; + addrspace_body_len = zir->extra[di + 2]; + di += 3; + } + + uint32_t value_body_len = 0; + if (declIdHasValueBody(id)) { + value_body_len = zir->extra[di]; + di++; + } + + // Compare name. + if (decl_name_idx == 0) + continue; + const char* decl_name + = (const char*)&zir->string_bytes[decl_name_idx]; + if (strcmp(decl_name, func_name) != 0) + continue; + + // Skip type, align, linksection, addrspace bodies. + di += type_body_len; + di += align_body_len; + di += linksection_body_len; + di += addrspace_body_len; + + // Search value body for a func instruction. + const uint32_t* value_body = &zir->extra[di]; + for (uint32_t v = 0; v < value_body_len; v++) { + ZirInstTag vtag = zir->inst_tags[value_body[v]]; + if (vtag == ZIR_INST_FUNC + || vtag == ZIR_INST_FUNC_FANCY + || vtag == ZIR_INST_FUNC_INFERRED) + return value_body[v]; + } + } + return UINT32_MAX; +} + // findDeclFuncInst: given a string_bytes name index, find the // func/func_fancy ZIR instruction in that declaration's value body. // Returns the instruction index, or UINT32_MAX if not found. @@ -1077,6 +1323,51 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, // Find the inline function's ZIR instruction. uint32_t func_inst = findDeclFuncInst(sema, callee_name_idx); + + // For cross-module field_call: if local lookup fails, check if + // the callee object is an import and load the imported module. + Ast import_ast; + Zir import_zir; + Zir saved_code; + bool is_cross_module = false; + memset(&import_ast, 0, sizeof(import_ast)); + memset(&import_zir, 0, sizeof(import_zir)); + memset(&saved_code, 0, sizeof(saved_code)); + + if (func_inst == UINT32_MAX && is_field_call && sema->source_dir) { + // Resolve the obj_ptr to find the import declaration name. + uint32_t obj_name_idx = 0; + if (callee_ref >= ZIR_REF_START_INDEX) { + uint32_t callee_inst = callee_ref - ZIR_REF_START_INDEX; + ZirInstTag ctag = sema->code.inst_tags[callee_inst]; + if (ctag == ZIR_INST_DECL_REF + || ctag == ZIR_INST_DECL_VAL) + obj_name_idx = sema->code.inst_datas[callee_inst] + .str_tok.start; + } + if (obj_name_idx != 0) { + const char* import_path + = findDeclImportPath(sema, obj_name_idx); + if (import_path) { + const char* field_name + = (const char*)&sema->code + .string_bytes[callee_name_idx]; + import_zir = loadImportZir( + sema->source_dir, import_path, &import_ast); + if (import_zir.inst_len > 0) { + func_inst = findFuncInstInZir( + &import_zir, field_name); + if (func_inst != UINT32_MAX) { + // Swap to imported module's ZIR. + saved_code = sema->code; + sema->code = import_zir; + is_cross_module = true; + } + } + } + } + } + if (func_inst == UINT32_MAX) { // Can't resolve callee; return void (fallback). return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); @@ -1084,11 +1375,17 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, FuncZirInfo func_info = parseFuncZir(sema, func_inst); if (func_info.body_len == 0) { + if (is_cross_module) { + sema->code = saved_code; + zirDeinit(&import_zir); + astDeinit(&import_ast); + } return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); } - // Resolve the argument values. Each arg has a body that produces - // the argument value via break_inline. + // Resolve the argument values (from the ORIGINAL module's ZIR). + // Each arg has a body that produces the argument value via + // break_inline. // Layout at arg_data_start (= extra.end for Call/FieldCall struct): // [0..args_len] = end-offset for each arg (absolute from args_body) // [args_len..end_offsets[args_len-1]] = arg body instructions @@ -1097,6 +1394,11 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, AirInstRef arg_refs[16]; assert(args_len <= 16); { + // For cross-module calls, temporarily restore original ZIR + // to resolve argument operands. + Zir arg_code = sema->code; + if (is_cross_module) + sema->code = saved_code; uint32_t prev_end = args_len; // arg 0 starts after end-offset table for (uint32_t a = 0; a < args_len; a++) { uint32_t arg_end_off @@ -1115,10 +1417,19 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, arg_refs[a] = resolveInst(sema, arg_operand); prev_end = arg_end_off; } + if (is_cross_module) + sema->code = arg_code; } // Resolve inline function return type. + // For cross-module calls with anytype params (e.g. fneg), the return + // type is typically @TypeOf(a) which equals the first arg's type. TypeIndex ret_ty = resolveFuncRetType(sema, &func_info); + if (ret_ty == IP_INDEX_VOID_TYPE && is_cross_module + && args_len > 0 && func_info.ret_ty_body_len > 1) { + // Assume return type = first argument's type (covers @TypeOf(a)). + ret_ty = semaTypeOf(sema, arg_refs[0]); + } // Parse the inline function's parameter body. uint32_t param_block_inst = sema->code.extra @@ -1248,6 +1559,13 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, free(inlining.merges.br_list); semaBlockDeinit(&child_block); + // Restore original ZIR and free imported module data. + if (is_cross_module) { + sema->code = saved_code; + zirDeinit(&import_zir); + astDeinit(&import_ast); + } + return AIR_REF_FROM_INST(block_inst_idx); } @@ -1688,6 +2006,126 @@ static void zirStructDecl(Sema* sema, SemaBlock* block, uint32_t inst) { } } +// --- Comptime ZIR handlers for cross-module inline evaluation --- + +// zirTypeof: handle @TypeOf(operand). +// Returns the type of the operand as an IP ref. +static AirInstRef zirTypeof(Sema* sema, uint32_t inst) { + ZirInstRef operand_ref + = sema->code.inst_datas[inst].un_node.operand; + AirInstRef operand = resolveInst(sema, operand_ref); + TypeIndex ty = semaTypeOf(sema, operand); + return AIR_REF_FROM_IP(ty); +} + +// zirTypeInfoComptime: handle @typeInfo(type_ref) at comptime. +// For float types, tracks the result for subsequent field_val access. +static AirInstRef zirTypeInfoComptime(Sema* sema, uint32_t inst) { + ZirInstRef operand_ref + = sema->code.inst_datas[inst].un_node.operand; + AirInstRef operand = resolveInst(sema, operand_ref); + // operand should be an IP ref to a type. + if (!AIR_REF_IS_IP(operand)) + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + + InternPoolIndex type_ip = AIR_REF_TO_IP(operand); + + // Intern a unique marker value for this type_info result. + // Use an aggregate-like IP entry to hold the marker. + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_INT; + key.data.int_val.ty = IP_INDEX_COMPTIME_INT_TYPE; + // Use a distinctive value unlikely to collide with real ints. + key.data.int_val.value = 0x7E100000 + type_ip; + key.data.int_val.is_negative = false; + InternPoolIndex marker = ipIntern(sema->ip, key); + + ctTrack(sema, marker, CT_TAG_TYPE_INFO, type_ip); + return AIR_REF_FROM_IP(marker); +} + +// floatTypeBits: get the bit count for a float type IP index. +static uint32_t floatTypeBits(InternPoolIndex ty) { + switch (ty) { + case IP_INDEX_F16_TYPE: return 16; + case IP_INDEX_F32_TYPE: return 32; + case IP_INDEX_F64_TYPE: return 64; + default: return 0; + } +} + +// zirFieldValComptime: handle field_val on comptime values. +// Resolves .float on type_info results and .bits on float_info. +static AirInstRef zirFieldValComptime(Sema* sema, uint32_t inst) { + uint32_t payload_index + = sema->code.inst_datas[inst].pl_node.payload_index; + ZirInstRef obj_ref = sema->code.extra[payload_index]; + uint32_t field_name_start = sema->code.extra[payload_index + 1]; + const char* field_name + = (const char*)&sema->code.string_bytes[field_name_start]; + + AirInstRef obj = resolveInst(sema, obj_ref); + if (!AIR_REF_IS_IP(obj)) + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + + InternPoolIndex obj_ip = AIR_REF_TO_IP(obj); + uint32_t ct_val; + uint8_t ct_tag = ctLookup(sema, obj_ip, &ct_val); + + if (ct_tag == CT_TAG_TYPE_INFO && strcmp(field_name, "float") == 0) { + // Access .float on a type_info result. + // ct_val is the type IP index. + uint32_t bits = floatTypeBits(ct_val); + if (bits > 0) { + // Intern a marker for float_info. + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_INT; + key.data.int_val.ty = IP_INDEX_COMPTIME_INT_TYPE; + key.data.int_val.value = 0x71040000 + bits; + key.data.int_val.is_negative = false; + InternPoolIndex marker = ipIntern(sema->ip, key); + ctTrack(sema, marker, CT_TAG_FLOAT_INFO, bits); + return AIR_REF_FROM_IP(marker); + } + } else if (ct_tag == CT_TAG_FLOAT_INFO + && strcmp(field_name, "bits") == 0) { + // Access .bits on a float_info result. + // ct_val is the bits count. + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_INT; + key.data.int_val.ty = IP_INDEX_COMPTIME_INT_TYPE; + key.data.int_val.value = ct_val; + key.data.int_val.is_negative = false; + return AIR_REF_FROM_IP(ipIntern(sema->ip, key)); + } + + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); +} + +// resolveUnsignedIntType: given a bits count, return the IP index +// for the corresponding unsigned integer type. +static InternPoolIndex resolveUnsignedIntType( + InternPool* ip, uint32_t bits) { + switch (bits) { + case 8: return IP_INDEX_U8_TYPE; + case 16: return IP_INDEX_U16_TYPE; + case 32: return IP_INDEX_U32_TYPE; + case 64: return IP_INDEX_U64_TYPE; + default: { + // Intern a new int type. + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_INT_TYPE; + key.data.int_type.signedness = 0; // unsigned + key.data.int_type.bits = (uint16_t)bits; + return ipIntern(ip, key); + } + } +} + // --- analyzeBodyInner --- // Ported from src/Sema.zig analyzeBodyInner. // Main dispatch loop: iterates over ZIR instructions in a body and @@ -2151,6 +2589,37 @@ static bool analyzeBodyInner( i++; continue; + // ret_type: obtains the return type of the in-scope function. + case ZIR_INST_RET_TYPE: + if (sema->fn_ret_ty != TYPE_NONE) { + instMapPut(&sema->inst_map, inst, + AIR_REF_FROM_IP(sema->fn_ret_ty)); + } else { + instMapPut(&sema->inst_map, inst, + AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE)); + } + i++; + continue; + + // save_err_ret_index: no-op in ReleaseFast; map to void. + case ZIR_INST_SAVE_ERR_RET_INDEX: + instMapPut(&sema->inst_map, inst, + AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE)); + i++; + continue; + + // @TypeOf(operand) — handled by default (void) for now. + // zirTypeof is used for cross-module comptime eval. + + // @TypeOf builtin with block body — handled by default (void) + // for now. Cross-module comptime eval uses this inline. + + // @typeInfo(type) — handled by default (void) for now. + // zirTypeInfoComptime is used for cross-module comptime eval. + + // field_val — field access: handled by default (void) for now. + // zirFieldValComptime is used for cross-module comptime eval. + // For all other instructions, produce a void mapping and skip. // As handlers are implemented, they will replace this default. default: { diff --git a/stage0/sema.h b/stage0/sema.h index 08673cf800..fce39fa4d9 100644 --- a/stage0/sema.h +++ b/stage0/sema.h @@ -161,6 +161,16 @@ typedef struct Sema { uint32_t decl_names[64]; // string_bytes index uint32_t decl_insts[64]; // ZIR instruction index uint32_t num_decls; + // Source directory for resolving relative imports. + // Set by the caller before semaAnalyze. NULL = no import resolution. + const char* source_dir; + // Comptime type-info tracker: maps IP indices returned by type_info + // and field_val to their semantic meaning. + // tag: 0=none, 1=type_info(type), 2=float_info(bits) + uint8_t ct_tags[16]; + uint32_t ct_vals[16]; // type IP index or bits count + InternPoolIndex ct_keys[16]; // the IP index this entry describes + uint32_t ct_len; } Sema; #define SEMA_DEFAULT_BRANCH_QUOTA 1000 diff --git a/stage0/stages_test.zig b/stage0/stages_test.zig index fdc630b72b..5aeaaad90d 100644 --- a/stage0/stages_test.zig +++ b/stage0/stages_test.zig @@ -57,13 +57,6 @@ fn stagesCheck(gpa: Allocator, comptime path: []const u8, source: [:0]const u8) // Stage 3: Sema — compare C and Zig raw Air arrays { - var c_ip = sc.ipInit(); - defer sc.ipDeinit(&c_ip); - var c_sema = sc.semaInit(&c_ip, @bitCast(c_zir)); - defer sc.semaDeinit(&c_sema); - var c_func_air_list = sc.semaAnalyze(&c_sema); - defer sc.semaFuncAirListDeinit(&c_func_air_list); - // Symlink to the repo root so all relative imports resolve within // the module root, while keeping logical paths under .zig-cache/tmp/ // to avoid 'std' module conflicts with lib/std/. @@ -84,6 +77,18 @@ fn stagesCheck(gpa: Allocator, comptime path: []const u8, source: [:0]const u8) std.fs.cwd().symLink(abs_repo_root, symlink_path, .{ .is_directory = true }) catch return error.SymlinkCreate; defer std.fs.cwd().deleteFile(symlink_path) catch {}; + // Compute source directory for import resolution. + const repo_dir = comptime std.fs.path.dirname(repo_relative) orelse "."; + const source_dir_path: [:0]const u8 = symlink_path ++ "/" ++ repo_dir; + + var c_ip = sc.ipInit(); + defer sc.ipDeinit(&c_ip); + var c_sema = sc.semaInit(&c_ip, @bitCast(c_zir)); + defer sc.semaDeinit(&c_sema); + c_sema.source_dir = source_dir_path.ptr; + var c_func_air_list = sc.semaAnalyze(&c_sema); + defer sc.semaFuncAirListDeinit(&c_func_air_list); + const test_src: [:0]const u8 = symlink_path ++ "/" ++ repo_relative; const module_root: [:0]const u8 = symlink_path; try sema_test.airCompare(test_src.ptr, module_root.ptr, c_func_air_list); @@ -97,7 +102,7 @@ const corpus_files = .{ "../lib/std/crypto/codecs.zig", // 165 "../lib/std/os/uefi/tables/table_header.zig", // 214 "../lib/std/zig/llvm.zig", // 247 - //"../lib/compiler_rt/neghf2.zig", // 265 -- needs cross-module inline call (field_call) + //"../lib/compiler_rt/neghf2.zig", // 265 -- cross-module ZIR loading works; needs comptime eval (reify, struct_init) //"../lib/compiler_rt/negxf2.zig", // 265 -- @export+func_fancy handled; body analysis incomplete //"../lib/compiler_rt/absvdi2.zig", // 311 -- @export+func_fancy handled; body analysis incomplete //"../lib/compiler_rt/absvsi2.zig", // 311 -- @export+func_fancy handled; body analysis incomplete