commit d4636dc6cc8b6cf84251996ab916c3da986f0043 (tree)
parent 2f413803c6d7b2dc4babebc653cb2108002dbcda
Author: Motiejus <motiejus@jakstys.lt>
Date: Sat, 7 Mar 2026 09:44:18 +0000
sema: port zirDivTrunc, zirDivFloor, zirDivExact, zirMod, zirRem, zirModRem
Add handlers for the remaining division/modulo ZIR instructions that
were previously falling through to the default void mapping.
Each function handles:
- Comptime integer folding with proper sign-magnitude arithmetic
- Runtime fallback via the corresponding AIR instruction
zirDivTrunc: truncation division (rounds toward zero)
zirDivFloor: floor division (rounds toward negative infinity)
zirDivExact: exact division (same as trunc; assumes no remainder)
zirMod: @mod — result has sign of denominator
zirRem: @rem — result has sign of numerator
zirModRem: plain '%' operator (delegates to zirRem)
Ported from src/Sema.zig zirDivTrunc/zirDivFloor/zirDivExact/
zirMod/zirRem/zirModRem.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Diffstat:
| M | stage0/sema.c | | | 189 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 189 insertions(+), 0 deletions(-)
diff --git a/stage0/sema.c b/stage0/sema.c
@@ -1904,6 +1904,171 @@ static AirInstRef zirDiv(Sema* sema, SemaBlock* block, uint32_t inst) {
return semaAddInst(block, air_tag, data);
}
+// zirDivTrunc: @divTrunc(a, b) — truncation division (toward zero).
+// Ported from src/Sema.zig zirDivTrunc.
+static AirInstRef zirDivTrunc(Sema* sema, SemaBlock* block, uint32_t inst) {
+ uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index;
+ AirInstRef lhs = resolveInst(sema, sema->code.extra[payload_index]);
+ AirInstRef rhs = resolveInst(sema, sema->code.extra[payload_index + 1]);
+ TypeIndex peer_ty = resolvePeerTypes(sema, lhs, rhs);
+ lhs = semaCoerce(sema, block, peer_ty, lhs);
+ rhs = semaCoerce(sema, block, peer_ty, rhs);
+ // Comptime float.
+ if (AIR_REF_IS_IP(lhs) && AIR_REF_IS_IP(rhs)) {
+ InternPoolKey lk = ipIndexToKey(sema->ip, AIR_REF_TO_IP(lhs));
+ InternPoolKey rk = ipIndexToKey(sema->ip, AIR_REF_TO_IP(rhs));
+ if (lk.tag == IP_KEY_FLOAT && rk.tag == IP_KEY_FLOAT
+ && rk.data.float_val.val != 0.0) {
+ double result = lk.data.float_val.val / rk.data.float_val.val;
+ // Truncate toward zero.
+ result = result >= 0.0 ? (double)(int64_t)result
+ : -(double)(int64_t)(-result);
+ InternPoolKey key;
+ memset(&key, 0, sizeof(key));
+ key.tag = IP_KEY_FLOAT;
+ key.data.float_val.ty = peer_ty;
+ key.data.float_val.val = result;
+ return AIR_REF_FROM_IP(ipIntern(sema->ip, key));
+ }
+ }
+ // Comptime int: truncation division.
+ uint64_t lhs_lo, lhs_hi, rhs_lo, rhs_hi;
+ bool lhs_neg, rhs_neg;
+ if (isComptimeIntWide(sema, lhs, &lhs_lo, &lhs_hi, &lhs_neg)
+ && isComptimeIntWide(sema, rhs, &rhs_lo, &rhs_hi, &rhs_neg)
+ && rhs_hi == 0 && rhs_lo != 0 && lhs_hi == 0) {
+ uint64_t result = lhs_lo / rhs_lo;
+ bool result_neg = (lhs_neg != rhs_neg) && result != 0;
+ return internComptimeInt(sema, peer_ty, result, 0, result_neg);
+ }
+ AirInstData data;
+ memset(&data, 0, sizeof(data));
+ data.bin_op.lhs = lhs;
+ data.bin_op.rhs = rhs;
+ return semaAddInst(block, AIR_INST_DIV_TRUNC, data);
+}
+
+// zirDivFloor: @divFloor(a, b) — floor division (toward -infinity).
+// Ported from src/Sema.zig zirDivFloor.
+static AirInstRef zirDivFloor(Sema* sema, SemaBlock* block, uint32_t inst) {
+ uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index;
+ AirInstRef lhs = resolveInst(sema, sema->code.extra[payload_index]);
+ AirInstRef rhs = resolveInst(sema, sema->code.extra[payload_index + 1]);
+ TypeIndex peer_ty = resolvePeerTypes(sema, lhs, rhs);
+ lhs = semaCoerce(sema, block, peer_ty, lhs);
+ rhs = semaCoerce(sema, block, peer_ty, rhs);
+ // Comptime int: floor division (round toward -infinity).
+ uint64_t lhs_lo, lhs_hi, rhs_lo, rhs_hi;
+ bool lhs_neg, rhs_neg;
+ if (isComptimeIntWide(sema, lhs, &lhs_lo, &lhs_hi, &lhs_neg)
+ && isComptimeIntWide(sema, rhs, &rhs_lo, &rhs_hi, &rhs_neg)
+ && rhs_hi == 0 && rhs_lo != 0 && lhs_hi == 0) {
+ uint64_t q = lhs_lo / rhs_lo;
+ uint64_t rem = lhs_lo % rhs_lo;
+ // If result is negative and has remainder, subtract 1 (floor).
+ bool result_neg = (lhs_neg != rhs_neg);
+ if (result_neg && rem != 0)
+ q++;
+ bool final_neg = result_neg && (q != 0);
+ return internComptimeInt(sema, peer_ty, q, 0, final_neg);
+ }
+ AirInstData data;
+ memset(&data, 0, sizeof(data));
+ data.bin_op.lhs = lhs;
+ data.bin_op.rhs = rhs;
+ return semaAddInst(block, AIR_INST_DIV_FLOOR, data);
+}
+
+// zirDivExact: @divExact(a, b) — exact division (no remainder).
+// Ported from src/Sema.zig zirDivExact.
+static AirInstRef zirDivExact(Sema* sema, SemaBlock* block, uint32_t inst) {
+ uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index;
+ AirInstRef lhs = resolveInst(sema, sema->code.extra[payload_index]);
+ AirInstRef rhs = resolveInst(sema, sema->code.extra[payload_index + 1]);
+ TypeIndex peer_ty = resolvePeerTypes(sema, lhs, rhs);
+ lhs = semaCoerce(sema, block, peer_ty, lhs);
+ rhs = semaCoerce(sema, block, peer_ty, rhs);
+ // Comptime int: exact division (truncate; assume no remainder).
+ uint64_t lhs_lo, lhs_hi, rhs_lo, rhs_hi;
+ bool lhs_neg, rhs_neg;
+ if (isComptimeIntWide(sema, lhs, &lhs_lo, &lhs_hi, &lhs_neg)
+ && isComptimeIntWide(sema, rhs, &rhs_lo, &rhs_hi, &rhs_neg)
+ && rhs_hi == 0 && rhs_lo != 0 && lhs_hi == 0) {
+ uint64_t result = lhs_lo / rhs_lo;
+ bool result_neg = (lhs_neg != rhs_neg) && result != 0;
+ return internComptimeInt(sema, peer_ty, result, 0, result_neg);
+ }
+ AirInstData data;
+ memset(&data, 0, sizeof(data));
+ data.bin_op.lhs = lhs;
+ data.bin_op.rhs = rhs;
+ return semaAddInst(block, AIR_INST_DIV_EXACT, data);
+}
+
+// zirMod: @mod(a, b) — result has sign of denominator (always >= 0 if b > 0).
+// Ported from src/Sema.zig zirMod.
+static AirInstRef zirMod(Sema* sema, SemaBlock* block, uint32_t inst) {
+ uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index;
+ AirInstRef lhs = resolveInst(sema, sema->code.extra[payload_index]);
+ AirInstRef rhs = resolveInst(sema, sema->code.extra[payload_index + 1]);
+ TypeIndex peer_ty = resolvePeerTypes(sema, lhs, rhs);
+ lhs = semaCoerce(sema, block, peer_ty, lhs);
+ rhs = semaCoerce(sema, block, peer_ty, rhs);
+ // Comptime int: @mod = a - @divFloor(a,b)*b, always same sign as b.
+ uint64_t lhs_lo, lhs_hi, rhs_lo, rhs_hi;
+ bool lhs_neg, rhs_neg;
+ if (isComptimeIntWide(sema, lhs, &lhs_lo, &lhs_hi, &lhs_neg)
+ && isComptimeIntWide(sema, rhs, &rhs_lo, &rhs_hi, &rhs_neg)
+ && rhs_hi == 0 && rhs_lo != 0 && lhs_hi == 0) {
+ uint64_t rem = lhs_lo % rhs_lo;
+ // @mod: if signs differ and there's a remainder, adjust.
+ if (rem != 0 && (lhs_neg != rhs_neg)) {
+ rem = rhs_lo - rem;
+ }
+ bool result_neg = rhs_neg && rem != 0;
+ return internComptimeInt(sema, peer_ty, rem, 0, result_neg);
+ }
+ AirInstData data;
+ memset(&data, 0, sizeof(data));
+ data.bin_op.lhs = lhs;
+ data.bin_op.rhs = rhs;
+ return semaAddInst(block, AIR_INST_MOD, data);
+}
+
+// zirRem: @rem(a, b) — result has sign of numerator (C-style %).
+// Ported from src/Sema.zig zirRem.
+static AirInstRef zirRem(Sema* sema, SemaBlock* block, uint32_t inst) {
+ uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index;
+ AirInstRef lhs = resolveInst(sema, sema->code.extra[payload_index]);
+ AirInstRef rhs = resolveInst(sema, sema->code.extra[payload_index + 1]);
+ TypeIndex peer_ty = resolvePeerTypes(sema, lhs, rhs);
+ lhs = semaCoerce(sema, block, peer_ty, lhs);
+ rhs = semaCoerce(sema, block, peer_ty, rhs);
+ // Comptime int: @rem = a % b (sign of numerator).
+ uint64_t lhs_lo, lhs_hi, rhs_lo, rhs_hi;
+ bool lhs_neg, rhs_neg;
+ if (isComptimeIntWide(sema, lhs, &lhs_lo, &lhs_hi, &lhs_neg)
+ && isComptimeIntWide(sema, rhs, &rhs_lo, &rhs_hi, &rhs_neg)
+ && rhs_hi == 0 && rhs_lo != 0 && lhs_hi == 0) {
+ uint64_t rem = lhs_lo % rhs_lo;
+ bool result_neg = lhs_neg && rem != 0;
+ return internComptimeInt(sema, peer_ty, rem, 0, result_neg);
+ }
+ AirInstData data;
+ memset(&data, 0, sizeof(data));
+ data.bin_op.lhs = lhs;
+ data.bin_op.rhs = rhs;
+ return semaAddInst(block, AIR_INST_REM, data);
+}
+
+// zirModRem: handle plain '%' operator (may be mod or rem depending on types).
+// Ported from src/Sema.zig zirModRem.
+static AirInstRef zirModRem(Sema* sema, SemaBlock* block, uint32_t inst) {
+ // For unsigned types: same as @rem. For signed: use @rem semantics.
+ // Ported from Sema.zig zirModRem which checks signed/unsigned.
+ return zirRem(sema, block, inst);
+}
+
// zirBitwise: handle bitwise operation ZIR instructions.
// Ported from src/Sema.zig zirBitwise.
static AirInstRef zirBitwise(
@@ -11970,6 +12135,30 @@ bool analyzeBodyInner(
instMapPut(&sema->inst_map, inst, zirDiv(sema, block, inst));
i++;
continue;
+ case ZIR_INST_DIV_TRUNC:
+ instMapPut(&sema->inst_map, inst, zirDivTrunc(sema, block, inst));
+ i++;
+ continue;
+ case ZIR_INST_DIV_FLOOR:
+ instMapPut(&sema->inst_map, inst, zirDivFloor(sema, block, inst));
+ i++;
+ continue;
+ case ZIR_INST_DIV_EXACT:
+ instMapPut(&sema->inst_map, inst, zirDivExact(sema, block, inst));
+ i++;
+ continue;
+ case ZIR_INST_MOD:
+ instMapPut(&sema->inst_map, inst, zirMod(sema, block, inst));
+ i++;
+ continue;
+ case ZIR_INST_REM:
+ instMapPut(&sema->inst_map, inst, zirRem(sema, block, inst));
+ i++;
+ continue;
+ case ZIR_INST_MOD_REM:
+ instMapPut(&sema->inst_map, inst, zirModRem(sema, block, inst));
+ i++;
+ continue;
case ZIR_INST_ADD_SAT:
instMapPut(&sema->inst_map, inst,
zirArithmetic(sema, block, inst, AIR_INST_ADD_SAT));