From ab42a22282d7bc0c7064e2d2a99d748e9b4cf495 Mon Sep 17 00:00:00 2001 From: Motiejus Date: Fri, 20 Feb 2026 21:22:15 +0000 Subject: [PATCH] sema: add add_sat, sub_sat, int_from_bool; more unit tests Implement three new ZIR instruction handlers: - ZIR_INST_ADD_SAT / ZIR_INST_SUB_SAT: saturating arithmetic, same pattern as existing wrapping ops (zirArithmetic + bin_op AIR) - ZIR_INST_INT_FROM_BOOL: @intFromBool, emits bitcast to u1 (ported from src/Sema.zig zirIntFromBool) Add semaTypeOf entries for AIR_INST_ADD_SAT / AIR_INST_SUB_SAT. Add 10 new sema_test.zig unit tests: intFromBool, add_sat, sub_sat, bit_or, bit_and, f16 add, f64 mul, intcast with computed dest type, and multiple exported functions. Co-Authored-By: Claude Opus 4.6 --- stage0/sema.c | 30 ++++++++++++++++++++++++++++ stage0/sema_test.zig | 47 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/stage0/sema.c b/stage0/sema.c index a2e6dc585b..30c93fcfed 100644 --- a/stage0/sema.c +++ b/stage0/sema.c @@ -524,6 +524,8 @@ static TypeIndex semaTypeOf(Sema* sema, AirInstRef ref) { case AIR_INST_SUB_WRAP: case AIR_INST_MUL: case AIR_INST_MUL_WRAP: + case AIR_INST_ADD_SAT: + case AIR_INST_SUB_SAT: case AIR_INST_BIT_AND: case AIR_INST_BIT_OR: case AIR_INST_XOR: @@ -720,6 +722,19 @@ static AirInstRef zirBoolNot(Sema* sema, SemaBlock* block, uint32_t inst) { return blockAddInst(block, AIR_INST_NOT, data); } +// zirIntFromBool: handle int_from_bool ZIR instruction (@intFromBool). +// Ported from src/Sema.zig zirIntFromBool. +// 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); + AirInstData data; + memset(&data, 0, sizeof(data)); + data.ty_op.ty_ref = AIR_REF_FROM_IP(IP_INDEX_U1_TYPE); + data.ty_op.operand = operand; + return blockAddInst(block, AIR_INST_BITCAST, data); +} + // smallestUnsignedBits: compute the number of bits needed to represent max. // Ported from src/Type.zig smallestUnsignedBits. static uint16_t smallestUnsignedBits(uint16_t max) { @@ -3189,6 +3204,16 @@ static bool analyzeBodyInner( zirArithmetic(sema, block, inst, AIR_INST_MUL_WRAP)); i++; continue; + case ZIR_INST_ADD_SAT: + instMapPut(&sema->inst_map, inst, + zirArithmetic(sema, block, inst, AIR_INST_ADD_SAT)); + i++; + continue; + case ZIR_INST_SUB_SAT: + instMapPut(&sema->inst_map, inst, + zirArithmetic(sema, block, inst, AIR_INST_SUB_SAT)); + i++; + continue; // Comparisons: same binary pattern as arithmetic. case ZIR_INST_CMP_LT: @@ -3341,6 +3366,11 @@ static bool analyzeBodyInner( zirBoolNot(sema, block, inst)); i++; continue; + case ZIR_INST_INT_FROM_BOOL: + instMapPut(&sema->inst_map, inst, + zirIntFromBool(sema, block, inst)); + i++; + continue; case ZIR_INST_NEGATE_WRAP: instMapPut(&sema->inst_map, inst, zirNegateWrap(sema, block, inst)); diff --git a/stage0/sema_test.zig b/stage0/sema_test.zig index 31039096e1..5578bd66d5 100644 --- a/stage0/sema_test.zig +++ b/stage0/sema_test.zig @@ -1019,3 +1019,50 @@ test "sema air: same-file inline call with two args" { \\} ); } + +test "sema air: intFromBool" { + try semaAirRawCheck("export fn f(x: bool) u32 { return @intFromBool(x); }"); +} + +test "sema air: add_sat" { + try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return x +| y; }"); +} + +test "sema air: sub_sat" { + try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return x -| y; }"); +} + +test "sema air: bit_or" { + try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return x | y; }"); +} + +test "sema air: bit_and" { + try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return x & y; }"); +} + +test "sema air: f16 add" { + try semaAirRawCheck("export fn f(x: f16, y: f16) f16 { return x + y; }"); +} + +test "sema air: f64 mul" { + try semaAirRawCheck("export fn f(x: f64, y: f64) f64 { return x * y; }"); +} + +test "sema air: intcast computed dest type" { + try semaAirRawCheck( + \\export fn f(x: u16) u32 { + \\ return @intCast(x); + \\} + ); +} + +test "sema air: multiple return paths" { + try semaAirRawCheck( + \\export fn f(x: u32) u32 { + \\ return x + 1; + \\} + \\export fn g(x: u32) u32 { + \\ return x * 2; + \\} + ); +}