commit e6cda567d009f8cdccd1b0b1611231483d91acda (tree)
parent 072929759e570b5003bcf98e51ffe78fde6a9bd5
Author: Motiejus <motiejus@jakstys.lt>
Date: Tue, 3 Mar 2026 07:33:39 +0000
sema: port compareIntsOnlyPossibleResult, fix int coercion dedup, bump 75→80
Port compareIntsOnlyPossibleResult from upstream Sema.zig (line 32511):
when a comparison has one comptime operand, intern the other type's
min/max bounds as side effects. This matches the Zig compiler's
IP entry sequence for comparison operations.
Fix semaCoerceIntRef: switch from ipForceIntern to ipIntern for
integer type coercion. The cross-shard dedup issue is now handled
by the skip_dedup mechanism, so ipForceIntern is no longer needed.
Using ipIntern allows proper deduplication of identical coerced
values (e.g., two comparisons with `> 0` share the same int_u32(0)).
Tests 75-79 now pass (var_bitcast_and_if, shr_exact, nested_if,
conditional_bitwise_and, conditional_sub).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Diffstat:
2 files changed, 66 insertions(+), 8 deletions(-)
diff --git a/stage0/corpus.zig b/stage0/corpus.zig
@@ -3,7 +3,7 @@
/// `num_passing` controls how many files are tested and pre-generated.
/// Both build.zig and stages_test.zig import this file.
/// To enable more tests: just increment `num_passing`.
-pub const num_passing: usize = 75;
+pub const num_passing: usize = 80;
pub const files = [_][]const u8{
"stage0/sema_tests/empty.zig",
diff --git a/stage0/sema.c b/stage0/sema.c
@@ -532,12 +532,10 @@ static AirInstRef semaCoerceIntRef(
return air_ref; // already the right type
// Create a new IP entry with the target type but same value.
- // Use ipForceIntern to skip deduplication: the Zig compiler's
- // sharded IP creates separate entries in different shards (e.g.
- // CallingConvention enum values in the memoized shard vs function
- // body values in the function shard). The C sema's single IP
- // would incorrectly deduplicate, returning an index from the
- // memoized state instead of creating a new function-local entry.
+ // Use ipIntern for deduplication: entries with the same type and
+ // value are shared. Cross-shard separation is handled by the
+ // skip_dedup mechanism (preamble entries are skipped, main analysis
+ // entries dedup normally).
InternPoolKey new_key;
memset(&new_key, 0, sizeof(new_key));
new_key.tag = IP_KEY_INT;
@@ -545,7 +543,7 @@ static AirInstRef semaCoerceIntRef(
new_key.data.int_val.value_lo = key.data.int_val.value_lo;
new_key.data.int_val.value_hi = key.data.int_val.value_hi;
new_key.data.int_val.is_negative = key.data.int_val.is_negative;
- uint32_t new_ip_idx = ipForceIntern(sema->ip, new_key);
+ uint32_t new_ip_idx = ipIntern(sema->ip, new_key);
return AIR_REF_FROM_IP(new_ip_idx);
}
@@ -1324,6 +1322,66 @@ static AirInstRef zirArithmetic(
}
emit_runtime:;
+ // Ported from Sema.zig cmpNumeric → compareIntsOnlyPossibleResult
+ // (line 32299-32325): when one operand is comptime-known and the
+ // other is a runtime integer, intern the type's min/max bounds.
+ // These entries are side effects of the bounds check; even if the
+ // comparison can't be folded, the boundary values must be interned
+ // to match the Zig compiler's IP entry sequence.
+ if (air_tag >= AIR_INST_CMP_LT && air_tag <= AIR_INST_CMP_GT) {
+ TypeIndex lhs_ty = semaTypeOf(sema, lhs);
+ TypeIndex rhs_ty = semaTypeOf(sema, rhs);
+ bool lhs_is_ct = (lhs_ty == IP_INDEX_COMPTIME_INT_TYPE);
+ bool rhs_is_ct = (rhs_ty == IP_INDEX_COMPTIME_INT_TYPE);
+ if ((lhs_is_ct != rhs_is_ct) && !lhs_is_ct
+ && sema->ip->items[lhs_ty].tag == IP_KEY_INT_TYPE) {
+ // RHS is comptime, LHS is runtime int → intern LHS
+ // type bounds.
+ uint16_t bits = sema->ip->items[lhs_ty].data.int_type.bits;
+ bool is_signed = sema->ip->items[lhs_ty].data.int_type.signedness;
+ uint64_t min_val = 0;
+ uint64_t max_val
+ = (bits >= 64) ? UINT64_MAX : ((1ULL << bits) - 1);
+ if (is_signed) {
+ min_val = (bits >= 64) ? (uint64_t)INT64_MIN
+ : ~((1ULL << (bits - 1)) - 1);
+ max_val = (bits >= 64) ? (uint64_t)INT64_MAX
+ : ((1ULL << (bits - 1)) - 1);
+ }
+ InternPoolKey mk;
+ memset(&mk, 0, sizeof(mk));
+ mk.tag = IP_KEY_INT;
+ mk.data.int_val.ty = lhs_ty;
+ mk.data.int_val.value_lo = min_val;
+ (void)ipIntern(sema->ip, mk);
+ mk.data.int_val.value_lo = max_val;
+ (void)ipIntern(sema->ip, mk);
+ } else if ((lhs_is_ct != rhs_is_ct) && !rhs_is_ct
+ && sema->ip->items[rhs_ty].tag == IP_KEY_INT_TYPE) {
+ // LHS is comptime, RHS is runtime int → intern RHS
+ // type bounds.
+ uint16_t bits = sema->ip->items[rhs_ty].data.int_type.bits;
+ bool is_signed = sema->ip->items[rhs_ty].data.int_type.signedness;
+ uint64_t min_val = 0;
+ uint64_t max_val
+ = (bits >= 64) ? UINT64_MAX : ((1ULL << bits) - 1);
+ if (is_signed) {
+ min_val = (bits >= 64) ? (uint64_t)INT64_MIN
+ : ~((1ULL << (bits - 1)) - 1);
+ max_val = (bits >= 64) ? (uint64_t)INT64_MAX
+ : ((1ULL << (bits - 1)) - 1);
+ }
+ InternPoolKey mk;
+ memset(&mk, 0, sizeof(mk));
+ mk.tag = IP_KEY_INT;
+ mk.data.int_val.ty = rhs_ty;
+ mk.data.int_val.value_lo = min_val;
+ (void)ipIntern(sema->ip, mk);
+ mk.data.int_val.value_lo = max_val;
+ (void)ipIntern(sema->ip, mk);
+ }
+ }
+
TypeIndex peer_ty = semaResolvePeerTypes(sema, lhs, rhs);
lhs = semaCoerce(sema, block, peer_ty, lhs);
rhs = semaCoerce(sema, block, peer_ty, rhs);