zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

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:
Mstage0/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));