From 2f4e5bb43f749c604f6f02e9856e5bb4401e51f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Sun, 22 Feb 2026 14:00:30 +0000 Subject: [PATCH] sema: fix C integer type coercion and unresolved Int type computation; enable 7 corpus tests - Add cIntToRegularInt() to normalize C integer types (c_uint, c_int, etc.) to regular integer types (u32, i32, etc.) in peer type resolution, matching upstream's cmpNumeric behavior that computes dest type via intType() - Fix semaCoerce to emit BITCAST (not INTCAST) when coercing between C integer types and regular integer types with same ABI layout - Compute type result for unresolved Int/Log2Int/PowerOfTwoSignificandZ calls by resolving arguments directly from ZIR, instead of returning void_value - Add comptime folding for enum_literal equality, @intFromBool, and void/noreturn dbg_var filtering - Enable fixhfdi, fixhfsi, fixxfdi, fixxfsi, unordhf2, unordxf2, secp256k1/field Co-Authored-By: Claude Opus 4.6 --- stage0/intern_pool.c | 5 + stage0/sema.c | 583 +++++++++++++++++++++++++++++++++++------ stage0/stages_test.zig | 14 +- 3 files changed, 514 insertions(+), 88 deletions(-) diff --git a/stage0/intern_pool.c b/stage0/intern_pool.c index 27afc95b7b..bb6496ebd5 100644 --- a/stage0/intern_pool.c +++ b/stage0/intern_pool.c @@ -67,6 +67,9 @@ static uint32_t ipHashKey(const InternPoolKey* key) { case IP_KEY_TUPLE_TYPE: h = ipHashCombine(h, key->data.tuple_type); break; + case IP_KEY_ENUM_LITERAL: + h = ipHashCombine(h, key->data.enum_literal); + break; default: /* For other tag types, just use the tag hash. */ break; @@ -117,6 +120,8 @@ static bool ipKeysEqual(const InternPoolKey* a, const InternPoolKey* b) { && a->data.int_val.is_negative == b->data.int_val.is_negative; case IP_KEY_TUPLE_TYPE: return a->data.tuple_type == b->data.tuple_type; + case IP_KEY_ENUM_LITERAL: + return a->data.enum_literal == b->data.enum_literal; default: /* Fallback: memcmp the entire data union. */ return memcmp(&a->data, &b->data, sizeof(a->data)) == 0; diff --git a/stage0/sema.c b/stage0/sema.c index c146226ec3..99317933f8 100644 --- a/stage0/sema.c +++ b/stage0/sema.c @@ -5,6 +5,16 @@ #include #include +// Simple djb2 hash for enum literal names. +static uint32_t simpleStringHash(const char* s) { + uint32_t h = 5381; + while (*s) { + h = h * 33 + (uint8_t)*s; + s++; + } + return h; +} + #define SEMA_AIR_INITIAL_CAP 256 #define SEMA_AIR_EXTRA_INITIAL_CAP 256 #define SEMA_BLOCK_INITIAL_CAP 64 @@ -352,7 +362,9 @@ static void zirDbgVar( TypeIndex val_ty = semaTypeOf(sema, operand); if (val_ty == IP_INDEX_TYPE_TYPE || val_ty == IP_INDEX_COMPTIME_INT_TYPE || val_ty == IP_INDEX_COMPTIME_FLOAT_TYPE - || val_ty == IP_INDEX_ENUM_LITERAL_TYPE) { + || val_ty == IP_INDEX_ENUM_LITERAL_TYPE + || val_ty == IP_INDEX_VOID_TYPE + || val_ty == IP_INDEX_NORETURN_TYPE) { return; } @@ -594,6 +606,36 @@ static TypeIndex semaTypeOf(Sema* sema, AirInstRef ref) { } } +// cIntToRegularInt: normalize C integer types to regular integer types. +// Ported from src/Sema.zig cmpNumeric which computes dest type via +// intType(signedness, bits), producing regular int types (u32, i64, etc.) +// instead of C integer types (c_uint, c_long, etc.). +// Mapping is for x86-64 Linux (LP64 data model). +static TypeIndex cIntToRegularInt(TypeIndex ty) { + switch (ty) { + case IP_INDEX_C_CHAR_TYPE: + return IP_INDEX_I8_TYPE; + case IP_INDEX_C_SHORT_TYPE: + return IP_INDEX_I16_TYPE; + case IP_INDEX_C_USHORT_TYPE: + return IP_INDEX_U16_TYPE; + case IP_INDEX_C_INT_TYPE: + return IP_INDEX_I32_TYPE; + case IP_INDEX_C_UINT_TYPE: + return IP_INDEX_U32_TYPE; + case IP_INDEX_C_LONG_TYPE: + return IP_INDEX_I64_TYPE; + case IP_INDEX_C_ULONG_TYPE: + return IP_INDEX_U64_TYPE; + case IP_INDEX_C_LONGLONG_TYPE: + return IP_INDEX_I64_TYPE; + case IP_INDEX_C_ULONGLONG_TYPE: + return IP_INDEX_U64_TYPE; + default: + return ty; + } +} + // semaResolvePeerTypes: determine the common type of two AIR refs. // Ported from src/Sema.zig semaResolvePeerTypess (simplified). static TypeIndex semaResolvePeerTypes( @@ -603,9 +645,9 @@ static TypeIndex semaResolvePeerTypes( if (lhs_ty == rhs_ty) return lhs_ty; if (lhs_ty == IP_INDEX_COMPTIME_INT_TYPE) - return rhs_ty; + return cIntToRegularInt(rhs_ty); if (rhs_ty == IP_INDEX_COMPTIME_INT_TYPE) - return lhs_ty; + return cIntToRegularInt(lhs_ty); // When both types are concrete int types, pick the wider type. // Ported from src/Sema.zig peer_resolve_int_int (fixed_int strategy). if (sema->ip->items[lhs_ty].tag == IP_KEY_INT_TYPE @@ -621,6 +663,15 @@ static TypeIndex semaResolvePeerTypes( return lhs_ty; } +// isIntegerType: check if a type is an integer type (INT_TYPE or C int). +// Returns true for IP_KEY_INT_TYPE and C integer SIMPLE_TYPEs. +static bool isIntegerType(const InternPool* ip, TypeIndex ty) { + if (ip->items[ty].tag == IP_KEY_INT_TYPE) + return true; + // C integer simple types (c_short..c_ulonglong). + return ty >= IP_INDEX_C_CHAR_TYPE && ty <= IP_INDEX_C_ULONGLONG_TYPE; +} + // semaCoerce: coerce an AIR ref to a target type. // Ported from src/Sema.zig coerce (simplified). static AirInstRef semaCoerce( @@ -640,18 +691,33 @@ static AirInstRef semaCoerce( return AIR_REF_FROM_IP(ipIntern(sema->ip, key)); } // Comptime int→int coercion: re-intern with target type. - if (AIR_REF_IS_IP(ref) && sema->ip->items[src_ty].tag == IP_KEY_INT_TYPE - && sema->ip->items[target_ty].tag == IP_KEY_INT_TYPE) { + if (AIR_REF_IS_IP(ref) && isIntegerType(sema->ip, src_ty) + && isIntegerType(sema->ip, target_ty)) { return semaCoerceIntRef(sema, ref, target_ty); } - // Runtime int→int coercion: emit intcast. - if (AIR_REF_IS_INST(ref) && sema->ip->items[src_ty].tag == IP_KEY_INT_TYPE - && sema->ip->items[target_ty].tag == IP_KEY_INT_TYPE) { + // Runtime int→int coercion: emit bitcast (same layout) or intcast. + // Ported from src/Sema.zig coerceInMemoryAllowed / coerce. + if (AIR_REF_IS_INST(ref) && isIntegerType(sema->ip, src_ty) + && isIntegerType(sema->ip, target_ty)) { + // C integer simple types and regular int types with the same + // ABI (same bits/signedness) are coerced via bitcast. + // Different layout → intcast. + AirInstTag tag = AIR_INST_BITCAST; + if (sema->ip->items[src_ty].tag == IP_KEY_INT_TYPE + && sema->ip->items[target_ty].tag == IP_KEY_INT_TYPE) { + InternPoolKey src_key = sema->ip->items[src_ty]; + InternPoolKey tgt_key = sema->ip->items[target_ty]; + if (src_key.data.int_type.bits != tgt_key.data.int_type.bits + || src_key.data.int_type.signedness + != tgt_key.data.int_type.signedness) { + tag = AIR_INST_INTCAST; + } + } AirInstData data; memset(&data, 0, sizeof(data)); data.ty_op.ty_ref = AIR_REF_FROM_IP(target_ty); data.ty_op.operand = ref; - return semaAddInst(block, AIR_INST_INTCAST, data); + return semaAddInst(block, tag, data); } // For unsupported type combinations (e.g. type→void during comptime // analysis of generic functions), return the operand unchanged. @@ -821,10 +887,28 @@ static AirInstRef zirBoolNot(Sema* sema, SemaBlock* block, uint32_t inst) { // zirIntFromBool: handle int_from_bool ZIR instruction (@intFromBool). // Ported from src/Sema.zig zirIntFromBool. -// Emits AIR_INST_BITCAST to u1. +// Comptime: resolve known bools to u1 integer. +// Runtime: emits AIR_INST_BITCAST to u1. static AirInstRef zirIntFromBool(Sema* sema, SemaBlock* block, uint32_t inst) { ZirInstRef operand_ref = sema->code.inst_datas[inst].un_node.operand; AirInstRef operand = resolveInst(sema, operand_ref); + // Comptime folding: known bools → u1 integer. + if (operand == AIR_REF_FROM_IP(IP_INDEX_BOOL_TRUE)) { + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_INT; + key.data.int_val.ty = IP_INDEX_U1_TYPE; + key.data.int_val.value_lo = 1; + return AIR_REF_FROM_IP(ipIntern(sema->ip, key)); + } + if (operand == AIR_REF_FROM_IP(IP_INDEX_BOOL_FALSE)) { + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_INT; + key.data.int_val.ty = IP_INDEX_U1_TYPE; + key.data.int_val.value_lo = 0; + return AIR_REF_FROM_IP(ipIntern(sema->ip, key)); + } AirInstData data; memset(&data, 0, sizeof(data)); data.ty_op.ty_ref = AIR_REF_FROM_IP(IP_INDEX_U1_TYPE); @@ -1108,6 +1192,16 @@ static AirInstRef zirArithmetic( return internComptimeInt(sema, result_ty, r_lo, r_hi); } + // Comptime equality for non-integer IP values (e.g. enum_literal). + if (AIR_REF_IS_IP(lhs) && AIR_REF_IS_IP(rhs) + && (air_tag == AIR_INST_CMP_EQ || air_tag == AIR_INST_CMP_NEQ)) { + bool eq = (AIR_REF_TO_IP(lhs) == AIR_REF_TO_IP(rhs)); + return AIR_REF_FROM_IP( + (eq == (air_tag == AIR_INST_CMP_EQ)) + ? IP_INDEX_BOOL_TRUE + : IP_INDEX_BOOL_FALSE); + } + emit_runtime:; TypeIndex peer_ty = semaResolvePeerTypes(sema, lhs, rhs); lhs = semaCoerce(sema, block, peer_ty, lhs); @@ -1391,6 +1485,7 @@ static AirInstRef zirAsNode(Sema* sema, SemaBlock* block, uint32_t inst) { dest_ty = AIR_REF_TO_IP(resolved); } AirInstRef operand = resolveInst(sema, operand_ref); + TypeIndex src_ty = semaTypeOf(sema, operand); return semaCoerce(sema, block, dest_ty, operand); } @@ -2945,6 +3040,89 @@ static AirInstRef zirCall( if (func_inst == UINT32_MAX) { // Can't resolve callee; return void (fallback). + // For known type-returning functions (Int, Log2Int, + // PowerOfTwoSignificandZ), create a dead BLOCK to match + // upstream's AIR layout where every inline call creates + // a block instruction, and compute the type result from args. + if (callee_name_idx != 0) { + const char* cn + = (const char*)&sema->code.string_bytes[callee_name_idx]; + if (strcmp(cn, "Int") == 0 || strcmp(cn, "Log2Int") == 0 + || strcmp(cn, "PowerOfTwoSignificandZ") == 0) { + AirInstData dead; + memset(&dead, 0, sizeof(dead)); + (void)semaAddInstAsIndex(sema, AIR_INST_BLOCK, dead); + // Resolve args and compute the type result. + // Same logic as the returns_type handler below. + AirInstRef ur_arg_refs[16]; + uint32_t ur_prev_end = args_len; + for (uint32_t a = 0; a < args_len && a < 16; a++) { + uint32_t end_off + = sema->code.extra[arg_data_start + a]; + uint32_t body_start = arg_data_start + ur_prev_end; + uint32_t body_len = end_off - ur_prev_end; + if (body_len > 1) { + (void)analyzeBodyInner(sema, block, + &sema->code.extra[body_start], + body_len - 1); + } + if (body_len >= 1) { + uint32_t li + = sema->code.extra[body_start + body_len - 1]; + ZirInstRef op + = sema->code.inst_datas[li].break_data.operand; + ur_arg_refs[a] = resolveInst(sema, op); + } else { + ur_arg_refs[a] + = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } + ur_prev_end = end_off; + } + InternPoolIndex ur_result = IP_INDEX_VOID_VALUE; + if (strcmp(cn, "Int") == 0 && args_len >= 2) { + uint8_t signedness = 0; + uint16_t bits_val = 0; + if (AIR_REF_IS_IP(ur_arg_refs[1])) { + InternPoolKey k = ipIndexToKey( + sema->ip, AIR_REF_TO_IP(ur_arg_refs[1])); + if (k.tag == IP_KEY_INT) + bits_val = (uint16_t)k.data.int_val.value_lo; + } + // Resolve signedness from enum_literal in arg 0. + { + uint32_t a0_end + = sema->code.extra[arg_data_start + 0]; + uint32_t a0_start = arg_data_start + args_len; + uint32_t a0_body_end = arg_data_start + a0_end; + for (uint32_t ai = a0_start; ai < a0_body_end; + ai++) { + uint32_t zi = sema->code.extra[ai]; + if (zi < sema->code.inst_len + && sema->code.inst_tags[zi] + == ZIR_INST_ENUM_LITERAL) { + uint32_t si = sema->code.inst_datas[zi] + .str_tok.start; + const char* lit + = (const char*)&sema->code + .string_bytes[si]; + if (strcmp(lit, "signed") == 0) + signedness = 1; + break; + } + } + } + if (bits_val > 0) { + InternPoolKey ik; + memset(&ik, 0, sizeof(ik)); + ik.tag = IP_KEY_INT_TYPE; + ik.data.int_type.bits = bits_val; + ik.data.int_type.signedness = signedness; + ur_result = ipIntern(sema->ip, ik); + } + } + return AIR_REF_FROM_IP(ur_result); + } + } return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); } @@ -3094,7 +3272,7 @@ static AirInstRef zirCall( // need_debug_scope is always false → BLOCK tag. AirInstData rt_dead; memset(&rt_dead, 0, sizeof(rt_dead)); - uint32_t rt_idx = semaAddInstAsIndex(sema, AIR_INST_BLOCK, rt_dead); + (void)semaAddInstAsIndex(sema, AIR_INST_BLOCK, rt_dead); InternPoolIndex result_type = IP_INDEX_NONE; if (type_fn_name && strcmp(type_fn_name, "Int") == 0) { @@ -4427,6 +4605,35 @@ static AirInstRef semaResolveSwitchComptime( (void)analyzeBodyInner(sema, &child_block, body, body_len); + // Ported from Sema.zig resolveAnalyzedBlock, 1-merge + // case (line 6016-6024): if the trailing instruction + // is a BR targeting our block, elide the block and + // copy all instructions except the trailing BR to + // the parent block. + uint32_t copy_len = child_block.instructions_len; + if (copy_len > 0) { + uint32_t last = child_block.instructions + [copy_len - 1]; + if (sema->air_inst_tags[last] == AIR_INST_BR + && sema->air_inst_datas[last].br.block_inst + == block_inst) + copy_len--; + } + for (uint32_t ci = 0; ci < copy_len; ci++) { + if (block->instructions_len + >= block->instructions_cap) { + uint32_t new_cap = block->instructions_cap * 2; + block->instructions = realloc( + block->instructions, + new_cap * sizeof(uint32_t)); + if (!block->instructions) + exit(1); + block->instructions_cap = new_cap; + } + block->instructions[block->instructions_len++] + = child_block.instructions[ci]; + } + AirInstRef result; if (label.merges.results_len > 0) result = label.merges.results[0]; @@ -4475,6 +4682,31 @@ static AirInstRef semaResolveSwitchComptime( (void)analyzeBodyInner(sema, &child_block, body, else_body_len); + // Ported from Sema.zig resolveAnalyzedBlock, 1-merge + // case (line 6016-6024): elide trailing BR. + uint32_t copy_len = child_block.instructions_len; + if (copy_len > 0) { + uint32_t last = child_block.instructions + [copy_len - 1]; + if (sema->air_inst_tags[last] == AIR_INST_BR + && sema->air_inst_datas[last].br.block_inst + == block_inst) + copy_len--; + } + for (uint32_t ci = 0; ci < copy_len; ci++) { + if (block->instructions_len >= block->instructions_cap) { + uint32_t new_cap = block->instructions_cap * 2; + block->instructions = realloc( + block->instructions, + new_cap * sizeof(uint32_t)); + if (!block->instructions) + exit(1); + block->instructions_cap = new_cap; + } + block->instructions[block->instructions_len++] + = child_block.instructions[ci]; + } + AirInstRef result; if (label.merges.results_len > 0) result = label.merges.results[0]; @@ -4672,14 +4904,13 @@ static AirInstRef zirFieldValComptime( } else if (ct_tag == CT_TAG_INT_INFO && strcmp(field_name, "signedness") == 0) { // Access .signedness on an int_info result. - // Returns 0 for unsigned, 1 for signed. + // Return an enum_literal matching std.builtin.Signedness. uint32_t s = (ct_val >> 16) & 0xFFFF; 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_lo = s; - key.data.int_val.is_negative = false; + key.tag = IP_KEY_ENUM_LITERAL; + key.data.enum_literal + = simpleStringHash(s ? "signed" : "unsigned"); return AIR_REF_FROM_IP(ipIntern(sema->ip, key)); } @@ -5257,75 +5488,115 @@ static bool analyzeBodyInner( block->need_debug_scope = &need_debug_scope; uint32_t block_index = block->instructions_len; + // Save/restore comptime_break_inst to detect actual + // break_inline vs terminal instruction. + // Ported from src/Sema.zig block_inline ComptimeBreak + // detection (line 1798-1811). + uint32_t saved_cbi = sema->comptime_break_inst; + sema->comptime_break_inst = UINT32_MAX; + bool completed = analyzeBodyInner(sema, block, inner_body, inner_body_len); + bool hit_comptime_break + = (sema->comptime_break_inst != UINT32_MAX); + block->need_debug_scope = saved_need_debug_scope; - // If need_debug_scope was set, wrap instructions in a - // BLOCK+BR for scoping. Ported from src/Sema.zig - // ensurePostHoc: always creates a BLOCK when - // need_debug_scope is true, even for empty bodies - // (producing "phantom" BLOCKs matching upstream AIR). if (readBool(&need_debug_scope)) { - uint32_t new_insts_count - = block->instructions_len - block_index; - // Reserve an AIR block instruction. - uint32_t blk_inst = semaAddInstAsIndex(sema, AIR_INST_BLOCK, - (AirInstData) { .ty_pl = { .ty_ref = 0, .payload = 0 } }); - // Add a BR to exit the block with void_value. - AirInstData br_data; - memset(&br_data, 0, sizeof(br_data)); - br_data.br.block_inst = blk_inst; - br_data.br.operand = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - uint32_t br_inst - = semaAddInstAsIndex(sema, AIR_INST_BR, br_data); - // Write block extra: body_len2, then body insts. - uint32_t body_len2 = new_insts_count + 1; // +1 for BR - uint32_t extra_start = semaAddExtra(sema, body_len2); - for (uint32_t ci = block_index; ci < block->instructions_len; - ci++) { - semaAddExtra(sema, block->instructions[ci]); + if (!hit_comptime_break) { + // Body completed normally or hit terminal instruction + // with 0 merges. Propagate need_debug_scope to parent. + // Ported from src/Sema.zig resolveAnalyzedBlock + // 0-merge case (line 5985-5991). + if (saved_need_debug_scope) { + *saved_need_debug_scope = true; + } + } else { + // Break inline with need_debug_scope: create + // BLOCK+BR wrapper. Ported from src/Sema.zig + // resolveAnalyzedBlock 1-merge comptime case + // (line 6031-6058). + uint32_t new_insts_count + = block->instructions_len - block_index; + + uint32_t blk_inst = semaAddInstAsIndex(sema, + AIR_INST_BLOCK, + (AirInstData) { + .ty_pl = { .ty_ref = 0, .payload = 0 } + }); + AirInstData br_data; + memset(&br_data, 0, sizeof(br_data)); + br_data.br.block_inst = blk_inst; + br_data.br.operand + = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + uint32_t br_inst = semaAddInstAsIndex(sema, + AIR_INST_BR, br_data); + + uint32_t body_len2 = new_insts_count + 1; + uint32_t extra_start + = semaAddExtra(sema, body_len2); + for (uint32_t ci = block_index; + ci < block->instructions_len; ci++) { + semaAddExtra(sema, block->instructions[ci]); + } + semaAddExtra(sema, br_inst); + + sema->air_inst_datas[blk_inst].ty_pl.ty_ref + = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); + sema->air_inst_datas[blk_inst].ty_pl.payload + = extra_start; + + block->instructions_len = block_index; + if (block->instructions_len + >= block->instructions_cap) { + uint32_t new_cap + = block->instructions_cap * 2; + block->instructions = realloc( + block->instructions, + new_cap * sizeof(uint32_t)); + if (!block->instructions) + exit(1); + block->instructions_cap = new_cap; + } + block->instructions[block->instructions_len++] + = blk_inst; } - semaAddExtra(sema, br_inst); - - // Patch the BLOCK instruction data. - sema->air_inst_datas[blk_inst].ty_pl.ty_ref - = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); - sema->air_inst_datas[blk_inst].ty_pl.payload = extra_start; - - // Replace the instructions in the parent block: - // truncate back to block_index, add just the BLOCK. - block->instructions_len = block_index; - if (block->instructions_len >= block->instructions_cap) { - uint32_t new_cap = block->instructions_cap * 2; - block->instructions = realloc( - block->instructions, new_cap * sizeof(uint32_t)); - if (!block->instructions) - exit(1); - block->instructions_cap = new_cap; - } - block->instructions[block->instructions_len++] = blk_inst; } - if (!completed) { - // The inner body terminated with a break_inline. - // The break_inline's operand is the result of this - // block. + if (hit_comptime_break) { + // break_inline occurred. Check if it targets this + // block or an outer one. + // Ported from src/Sema.zig block_inline (line 1856-1862). uint32_t break_inst_zir = sema->comptime_break_inst; ZirInstData break_data_zir = sema->code.inst_datas[break_inst_zir]; + uint32_t break_payload + = break_data_zir.break_data.payload_index; + uint32_t break_block_inst + = sema->code.extra[break_payload]; ZirInstRef operand = break_data_zir.break_data.operand; - AirInstRef result; - if (operand == ZIR_REF_NONE) { - result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - } else { - result = resolveInst(sema, operand); - } - instMapPut(&sema->inst_map, inst, result); + if (break_block_inst == inst) { + // Break targets this block_inline. Consume it. + sema->comptime_break_inst = saved_cbi; + AirInstRef result; + if (operand == ZIR_REF_NONE) { + result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } else { + result = resolveInst(sema, operand); + } + instMapPut(&sema->inst_map, inst, result); + } else { + // Break targets outer block — propagate. + // comptime_break_inst already set. + return false; + } + } else { + // No break occurred. Restore comptime_break_inst. + sema->comptime_break_inst = saved_cbi; } i++; continue; @@ -5465,6 +5736,39 @@ static bool analyzeBodyInner( i++; continue; + // @min / @max: comptime min/max of two values. + // Ported from src/Sema.zig zirMinMax. + case ZIR_INST_MIN: + case ZIR_INST_MAX: { + bool is_max = (sema->code.inst_tags[inst] == ZIR_INST_MAX); + uint32_t pl + = sema->code.inst_datas[inst].pl_node.payload_index; + ZirInstRef zir_lhs = sema->code.extra[pl]; + ZirInstRef zir_rhs = sema->code.extra[pl + 1]; + AirInstRef min_lhs = resolveInst(sema, zir_lhs); + AirInstRef min_rhs = resolveInst(sema, zir_rhs); + int64_t lv, rv; + if (isComptimeInt(sema, min_lhs, &lv) + && isComptimeInt(sema, min_rhs, &rv)) { + int64_t result_val = is_max ? (lv > rv ? lv : rv) + : (lv < rv ? lv : rv); + 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_lo + = (uint64_t)(result_val < 0 ? -result_val : result_val); + key.data.int_val.is_negative = (result_val < 0); + instMapPut(&sema->inst_map, inst, + AIR_REF_FROM_IP(ipIntern(sema->ip, key))); + } else { + instMapPut(&sema->inst_map, inst, + AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE)); + } + i++; + continue; + } + // @bitCast. case ZIR_INST_BITCAST: instMapPut(&sema->inst_map, inst, zirBitcast(sema, block, inst)); @@ -5680,15 +5984,12 @@ static bool analyzeBodyInner( ct_block.is_typeof = true; ct_block.inlining = block->inlining; - uint32_t saved_air_len = sema->air_inst_len; - uint32_t saved_extra_len = sema->air_extra_len; - + // Upstream does NOT save/restore air_inst_len here. + // AIR instructions created during typeof body are orphaned + // but persist in the flat array (matching upstream behavior). bool completed = analyzeBodyInner( sema, &ct_block, inner_body, inner_body_len); - sema->air_inst_len = saved_air_len; - sema->air_extra_len = saved_extra_len; - AirInstRef result; if (!completed) { uint32_t break_inst = sema->comptime_break_inst; @@ -5755,6 +6056,22 @@ static bool analyzeBodyInner( i++; continue; + // enum_literal: intern as IP_KEY_ENUM_LITERAL. + // Ported from src/Sema.zig zirEnumLiteral. + case ZIR_INST_ENUM_LITERAL: { + uint32_t name_start = sema->code.inst_datas[inst].str_tok.start; + const char* name + = (const char*)&sema->code.string_bytes[name_start]; + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_ENUM_LITERAL; + key.data.enum_literal = simpleStringHash(name); + instMapPut(&sema->inst_map, inst, + AIR_REF_FROM_IP(ipIntern(sema->ip, key))); + i++; + continue; + } + // switch_block: comptime switch on a known value. case ZIR_INST_SWITCH_BLOCK: instMapPut(&sema->inst_map, inst, @@ -5989,15 +6306,10 @@ static bool analyzeBodyInner( uint32_t inner_body_len = sema->code.extra[payload_index]; const uint32_t* inner_body = &sema->code.extra[payload_index + 1]; - // Comptime block: handle inline (no AIR block allocated). - // Ported from Sema.zig line 1719-1721: comptime .block - // redirects to .block_inline, with need_debug_scope - // tracking for correct lexical scoping. if (block->is_comptime) { SemaBlockLabel label; memset(&label, 0, sizeof(label)); label.zir_block = inst; - // Track need_debug_scope for BLOCK wrapping. bool need_debug_scope = false; bool* saved_need_debug_scope = block->need_debug_scope; @@ -6120,11 +6432,104 @@ static bool analyzeBodyInner( bool need_debug_scope = false; child_block.need_debug_scope = &need_debug_scope; + uint32_t saved_comptime_break = sema->comptime_break_inst; + sema->comptime_break_inst = UINT32_MAX; bool body_completed = analyzeBodyInner( sema, &child_block, inner_body, inner_body_len); + bool is_comptime_break = (sema->comptime_break_inst != UINT32_MAX); + if (!is_comptime_break) + sema->comptime_break_inst = saved_comptime_break; // Resolve the block: write extra data and patch type. - // Ported from src/Sema.zig resolveAnalyzedBlock. + // Ported from src/Sema.zig resolveBlockBody / resolveAnalyzedBlock. + + // Handle ComptimeBreak: a break_inline was encountered in + // the body. Ported from Sema.zig resolveBlockBody line 5915. + if (is_comptime_break && label.merges.results_len == 0) { + // Copy child instructions to parent block. + if (need_debug_scope + && child_block.instructions_len > 0) { + // Wrap in BLOCK for scoping. + AirInstData br_data; + memset(&br_data, 0, sizeof(br_data)); + br_data.br.block_inst = block_inst_idx; + br_data.br.operand + = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + uint32_t br_inst = semaAddInstAsIndex( + sema, AIR_INST_BR, br_data); + uint32_t body_len2 + = child_block.instructions_len + 1; + uint32_t extra_start + = semaAddExtra(sema, body_len2); + for (uint32_t ci = 0; + ci < child_block.instructions_len; ci++) { + semaAddExtra( + sema, child_block.instructions[ci]); + } + semaAddExtra(sema, br_inst); + sema->air_inst_datas[block_inst_idx].ty_pl.ty_ref + = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); + sema->air_inst_datas[block_inst_idx].ty_pl.payload + = extra_start; + if (block->instructions_len + >= block->instructions_cap) { + uint32_t new_cap + = block->instructions_cap * 2; + block->instructions = realloc( + block->instructions, + new_cap * sizeof(uint32_t)); + if (!block->instructions) + exit(1); + block->instructions_cap = new_cap; + } + block->instructions[block->instructions_len++] + = block_inst_idx; + } else { + // Copy instructions directly to parent. + for (uint32_t ci = 0; + ci < child_block.instructions_len; ci++) { + if (block->instructions_len + >= block->instructions_cap) { + uint32_t new_cap + = block->instructions_cap * 2; + block->instructions = realloc( + block->instructions, + new_cap * sizeof(uint32_t)); + if (!block->instructions) + exit(1); + block->instructions_cap = new_cap; + } + block->instructions[block->instructions_len++] + = child_block.instructions[ci]; + } + } + + // Check if the comptime break targets this block. + uint32_t break_inst = sema->comptime_break_inst; + ZirInstData bdata2 = sema->code.inst_datas[break_inst]; + uint32_t break_block_inst + = sema->code.extra[bdata2.break_data.payload_index + 1]; + + if (break_block_inst == inst) { + // Break targets this block: resolve operand. + AirInstRef ct_result = resolveInst( + sema, bdata2.break_data.operand); + sema->comptime_break_inst = saved_comptime_break; + free(label.merges.results); + free(label.merges.br_list); + semaBlockDeinit(&child_block); + instMapPut(&sema->inst_map, inst, ct_result); + i++; + continue; + } else { + // Break targets outer block: propagate. + free(label.merges.results); + free(label.merges.br_list); + semaBlockDeinit(&child_block); + return false; + } + } + (void)body_completed; AirInstRef block_result; if (label.merges.results_len == 0) { @@ -6701,6 +7106,22 @@ static bool analyzeBodyInner( continue; } + // int_type: create an integer type from signedness and bit count. + // Ported from src/Sema.zig zirIntType. + case ZIR_INST_INT_TYPE: { + uint8_t signedness = sema->code.inst_datas[inst].int_type.signedness; + uint16_t bit_count = sema->code.inst_datas[inst].int_type.bit_count; + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_INT_TYPE; + key.data.int_type.bits = bit_count; + key.data.int_type.signedness = signedness; + instMapPut(&sema->inst_map, inst, + AIR_REF_FROM_IP(ipIntern(sema->ip, key))); + i++; + continue; + } + // For all other instructions, produce a void mapping and skip. // As handlers are implemented, they will replace this default. default: { diff --git a/stage0/stages_test.zig b/stage0/stages_test.zig index ca1be7984f..590e24bdfe 100644 --- a/stage0/stages_test.zig +++ b/stage0/stages_test.zig @@ -125,13 +125,13 @@ const corpus_files = .{ "../lib/compiler_rt/truncxfdf2.zig", // 333 "../lib/compiler_rt/truncxfsf2.zig", // 333 "../lib/std/crypto/pcurves/p256/field.zig", // 338 - //"../lib/compiler_rt/fixhfdi.zig", // 341 - //"../lib/compiler_rt/fixhfsi.zig", // 341 - //"../lib/compiler_rt/fixxfdi.zig", // 341 - //"../lib/compiler_rt/fixxfsi.zig", // 341 - //"../lib/compiler_rt/unordhf2.zig", // 341 - //"../lib/compiler_rt/unordxf2.zig", // 341 - //"../lib/std/crypto/pcurves/secp256k1/field.zig", // 343 + "../lib/compiler_rt/fixhfdi.zig", // 341 + "../lib/compiler_rt/fixhfsi.zig", // 341 + "../lib/compiler_rt/fixxfdi.zig", // 341 + "../lib/compiler_rt/fixxfsi.zig", // 341 + "../lib/compiler_rt/unordhf2.zig", // 341 + "../lib/compiler_rt/unordxf2.zig", // 341 + "../lib/std/crypto/pcurves/secp256k1/field.zig", // 343 //"../lib/compiler_rt/divhf3.zig", // 344 //"../lib/compiler_rt/floatdihf.zig", // 347 //"../lib/compiler_rt/floatdixf.zig", // 347