sema: add wrapping ops, bool_not, float/int casts, bit counting, byte_swap
New ZIR handlers: - addwrap/subwrap/mulwrap → AIR add_wrap/sub_wrap/mul_wrap - negate_wrap → AIR sub_wrap(0, operand) - bool_not → AIR not(bool, operand) - float_cast → AIR fpext or fptrunc (width comparison) - int_from_float, float_from_int → AIR ty_op casts - clz, ctz, pop_count → AIR ty_op with computed result type - byte_swap → AIR ty_op(operand_ty, operand) Infrastructure improvements: - semaCoerce: handle runtime int→int coercion via AIR intcast - ret_node: use semaCoerce instead of coerceIntRef for full coercion - Return type resolution: handle 2-instruction ptr_type bodies - func_fancy: properly track ret_ty_body_len and ret_ty_ref_pos - semaTypeOf: add wrapping ops, cmp, load, not, float/int casts, bit ops - airDataRefSlots: fix LOAD/NOT classification (ty_op not un_op), add wrap ops, float/int casts, bit count ops - zirAsShiftOperand: pass block for proper coercion support Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
346
stage0/sema.c
346
stage0/sema.c
@@ -461,18 +461,39 @@ static TypeIndex semaTypeOf(Sema* sema, AirInstRef ref) {
|
||||
return AIR_REF_TO_IP(sema->air_inst_datas[inst_idx].arg.ty_ref);
|
||||
// bin_op: type derived from LHS.
|
||||
case AIR_INST_ADD:
|
||||
case AIR_INST_ADD_WRAP:
|
||||
case AIR_INST_SUB:
|
||||
case AIR_INST_SUB_WRAP:
|
||||
case AIR_INST_MUL:
|
||||
case AIR_INST_MUL_WRAP:
|
||||
case AIR_INST_BIT_AND:
|
||||
case AIR_INST_BIT_OR:
|
||||
case AIR_INST_XOR:
|
||||
case AIR_INST_SHL:
|
||||
case AIR_INST_SHR:
|
||||
return semaTypeOf(sema, sema->air_inst_datas[inst_idx].bin_op.lhs);
|
||||
// cmp bin_op: result type is bool.
|
||||
case AIR_INST_CMP_LT:
|
||||
case AIR_INST_CMP_LTE:
|
||||
case AIR_INST_CMP_EQ:
|
||||
case AIR_INST_CMP_GTE:
|
||||
case AIR_INST_CMP_GT:
|
||||
case AIR_INST_CMP_NEQ:
|
||||
return IP_INDEX_BOOL_TYPE;
|
||||
// ty_op: type from ty_ref field.
|
||||
case AIR_INST_BITCAST:
|
||||
case AIR_INST_INTCAST:
|
||||
case AIR_INST_TRUNC:
|
||||
case AIR_INST_LOAD:
|
||||
case AIR_INST_NOT:
|
||||
case AIR_INST_FPTRUNC:
|
||||
case AIR_INST_FPEXT:
|
||||
case AIR_INST_INT_FROM_FLOAT:
|
||||
case AIR_INST_FLOAT_FROM_INT:
|
||||
case AIR_INST_CLZ:
|
||||
case AIR_INST_CTZ:
|
||||
case AIR_INST_POPCOUNT:
|
||||
case AIR_INST_BYTE_SWAP:
|
||||
return AIR_REF_TO_IP(sema->air_inst_datas[inst_idx].ty_op.ty_ref);
|
||||
default:
|
||||
assert(0 && "semaTypeOf: unhandled AIR tag");
|
||||
@@ -500,12 +521,21 @@ static TypeIndex resolvePeerType(
|
||||
// Ported from src/Sema.zig coerce (simplified).
|
||||
static AirInstRef semaCoerce(
|
||||
Sema* sema, SemaBlock* block, TypeIndex target_ty, AirInstRef ref) {
|
||||
(void)block;
|
||||
TypeIndex src_ty = semaTypeOf(sema, ref);
|
||||
if (src_ty == target_ty)
|
||||
return ref;
|
||||
if (src_ty == IP_INDEX_COMPTIME_INT_TYPE)
|
||||
return coerceIntRef(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) {
|
||||
AirInstData data;
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.ty_op.ty_ref = AIR_REF_FROM_IP(target_ty);
|
||||
data.ty_op.operand = ref;
|
||||
return blockAddInst(block, AIR_INST_INTCAST, data);
|
||||
}
|
||||
assert(0 && "semaCoerce: unhandled type combination");
|
||||
return ref;
|
||||
}
|
||||
@@ -535,6 +565,139 @@ static void zirStoreNode(Sema* sema, SemaBlock* block, uint32_t inst) {
|
||||
(void)blockAddInst(block, AIR_INST_STORE, data);
|
||||
}
|
||||
|
||||
// zirLoad: handle load ZIR instruction (pointer dereference).
|
||||
// Ported from src/Sema.zig zirLoad.
|
||||
static AirInstRef zirLoad(Sema* sema, SemaBlock* block, uint32_t inst) {
|
||||
ZirInstRef operand_ref = sema->code.inst_datas[inst].un_node.operand;
|
||||
AirInstRef ptr = resolveInst(sema, operand_ref);
|
||||
TypeIndex ptr_ty = semaTypeOf(sema, ptr);
|
||||
TypeIndex elem_ty = ptrChildType(sema->ip, ptr_ty);
|
||||
AirInstData data;
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.ty_op.ty_ref = AIR_REF_FROM_IP(elem_ty);
|
||||
data.ty_op.operand = ptr;
|
||||
return blockAddInst(block, AIR_INST_LOAD, data);
|
||||
}
|
||||
|
||||
// zirNegate: handle negate ZIR instruction (unary -).
|
||||
// Ported from src/Sema.zig zirNegate.
|
||||
// Lowers to sub(0, operand).
|
||||
static AirInstRef zirNegate(Sema* sema, SemaBlock* block, uint32_t inst) {
|
||||
ZirInstRef operand_ref = sema->code.inst_datas[inst].un_node.operand;
|
||||
AirInstRef operand = resolveInst(sema, operand_ref);
|
||||
TypeIndex ty = semaTypeOf(sema, operand);
|
||||
// Create a zero value of the operand type.
|
||||
InternPoolKey key;
|
||||
memset(&key, 0, sizeof(key));
|
||||
key.tag = IP_KEY_INT;
|
||||
key.data.int_val.ty = ty;
|
||||
key.data.int_val.value = 0;
|
||||
key.data.int_val.is_negative = false;
|
||||
AirInstRef zero = AIR_REF_FROM_IP(ipIntern(sema->ip, key));
|
||||
AirInstData data;
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.bin_op.lhs = zero;
|
||||
data.bin_op.rhs = operand;
|
||||
return blockAddInst(block, AIR_INST_SUB, data);
|
||||
}
|
||||
|
||||
// zirNegateWrap: handle negate_wrap ZIR instruction (wrapping unary -).
|
||||
// Ported from src/Sema.zig zirNegateWrap.
|
||||
// Lowers to sub_wrap(0, operand).
|
||||
static AirInstRef zirNegateWrap(Sema* sema, SemaBlock* block, uint32_t inst) {
|
||||
ZirInstRef operand_ref = sema->code.inst_datas[inst].un_node.operand;
|
||||
AirInstRef operand = resolveInst(sema, operand_ref);
|
||||
TypeIndex ty = semaTypeOf(sema, operand);
|
||||
InternPoolKey key;
|
||||
memset(&key, 0, sizeof(key));
|
||||
key.tag = IP_KEY_INT;
|
||||
key.data.int_val.ty = ty;
|
||||
key.data.int_val.value = 0;
|
||||
key.data.int_val.is_negative = false;
|
||||
AirInstRef zero = AIR_REF_FROM_IP(ipIntern(sema->ip, key));
|
||||
AirInstData data;
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.bin_op.lhs = zero;
|
||||
data.bin_op.rhs = operand;
|
||||
return blockAddInst(block, AIR_INST_SUB_WRAP, data);
|
||||
}
|
||||
|
||||
// zirBitNot: handle bit_not ZIR instruction (bitwise ~).
|
||||
// Ported from src/Sema.zig zirBitNot.
|
||||
static AirInstRef zirBitNot(Sema* sema, SemaBlock* block, uint32_t inst) {
|
||||
ZirInstRef operand_ref = sema->code.inst_datas[inst].un_node.operand;
|
||||
AirInstRef operand = resolveInst(sema, operand_ref);
|
||||
TypeIndex ty = semaTypeOf(sema, operand);
|
||||
AirInstData data;
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.ty_op.ty_ref = AIR_REF_FROM_IP(ty);
|
||||
data.ty_op.operand = operand;
|
||||
return blockAddInst(block, AIR_INST_NOT, data);
|
||||
}
|
||||
|
||||
// zirBoolNot: handle bool_not ZIR instruction.
|
||||
// Ported from src/Sema.zig zirBoolNot.
|
||||
// Emits AIR_INST_NOT with type = bool.
|
||||
static AirInstRef zirBoolNot(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_BOOL_TYPE);
|
||||
data.ty_op.operand = operand;
|
||||
return blockAddInst(block, AIR_INST_NOT, data);
|
||||
}
|
||||
|
||||
// smallestUnsignedBits: compute the number of bits needed to represent max.
|
||||
// Ported from src/Type.zig smallestUnsignedBits.
|
||||
static uint16_t smallestUnsignedBits(uint16_t max) {
|
||||
if (max == 0) return 0;
|
||||
uint16_t count = 0;
|
||||
uint16_t s = max;
|
||||
while (s != 0) {
|
||||
count++;
|
||||
s >>= 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// zirBitCount: handle clz/ctz/pop_count ZIR instructions.
|
||||
// Ported from src/Sema.zig zirBitCount.
|
||||
// Result type is smallestUnsignedInt(operand_bits).
|
||||
static AirInstRef zirBitCount(
|
||||
Sema* sema, SemaBlock* block, uint32_t inst, AirInstTag air_tag) {
|
||||
ZirInstRef operand_ref = sema->code.inst_datas[inst].un_node.operand;
|
||||
AirInstRef operand = resolveInst(sema, operand_ref);
|
||||
TypeIndex operand_ty = semaTypeOf(sema, operand);
|
||||
assert(sema->ip->items[operand_ty].tag == IP_KEY_INT_TYPE);
|
||||
uint16_t bits = sema->ip->items[operand_ty].data.int_type.bits;
|
||||
uint16_t result_bits = smallestUnsignedBits(bits);
|
||||
InternPoolKey key;
|
||||
memset(&key, 0, sizeof(key));
|
||||
key.tag = IP_KEY_INT_TYPE;
|
||||
key.data.int_type.bits = result_bits;
|
||||
key.data.int_type.signedness = 0;
|
||||
TypeIndex result_ty = ipIntern(sema->ip, key);
|
||||
AirInstData data;
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.ty_op.ty_ref = AIR_REF_FROM_IP(result_ty);
|
||||
data.ty_op.operand = operand;
|
||||
return blockAddInst(block, air_tag, data);
|
||||
}
|
||||
|
||||
// zirByteSwap: handle byte_swap ZIR instruction (@byteSwap).
|
||||
// Ported from src/Sema.zig zirByteSwap.
|
||||
static AirInstRef zirByteSwap(Sema* sema, SemaBlock* block, uint32_t inst) {
|
||||
ZirInstRef operand_ref = sema->code.inst_datas[inst].un_node.operand;
|
||||
AirInstRef operand = resolveInst(sema, operand_ref);
|
||||
TypeIndex operand_ty = semaTypeOf(sema, operand);
|
||||
AirInstData data;
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.ty_op.ty_ref = AIR_REF_FROM_IP(operand_ty);
|
||||
data.ty_op.operand = operand;
|
||||
return blockAddInst(block, AIR_INST_BYTE_SWAP, data);
|
||||
}
|
||||
|
||||
// zirArithmetic: handle add/sub ZIR instructions.
|
||||
// Ported from src/Sema.zig zirArithmetic.
|
||||
static AirInstRef zirArithmetic(
|
||||
@@ -616,6 +779,55 @@ static AirInstRef zirTyOpCast(
|
||||
return blockAddInst(block, air_tag, data);
|
||||
}
|
||||
|
||||
// floatBits: get the bit width of a float type from its IP index.
|
||||
static uint16_t floatBits(TypeIndex ty) {
|
||||
switch (ty) {
|
||||
case IP_INDEX_F16_TYPE:
|
||||
return 16;
|
||||
case IP_INDEX_F32_TYPE:
|
||||
return 32;
|
||||
case IP_INDEX_F64_TYPE:
|
||||
return 64;
|
||||
case IP_INDEX_F80_TYPE:
|
||||
return 80;
|
||||
case IP_INDEX_F128_TYPE:
|
||||
return 128;
|
||||
default:
|
||||
assert(0 && "floatBits: not a float type");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// zirFloatCast: handle float_cast ZIR instruction (@floatCast).
|
||||
// Ported from src/Sema.zig zirFloatCast.
|
||||
// Emits FPEXT (widening) or FPTRUNC (narrowing).
|
||||
static AirInstRef zirFloatCast(Sema* sema, SemaBlock* block, uint32_t inst) {
|
||||
uint32_t payload_index
|
||||
= sema->code.inst_datas[inst].pl_node.payload_index;
|
||||
ZirInstRef dest_ty_ref = sema->code.extra[payload_index];
|
||||
ZirInstRef operand_ref = sema->code.extra[payload_index + 1];
|
||||
assert(dest_ty_ref < ZIR_REF_START_INDEX);
|
||||
TypeIndex dest_ty = dest_ty_ref;
|
||||
AirInstRef operand = resolveInst(sema, operand_ref);
|
||||
if (!AIR_REF_IS_INST(operand)) {
|
||||
return semaCoerce(sema, block, dest_ty, operand);
|
||||
}
|
||||
TypeIndex operand_ty = semaTypeOf(sema, operand);
|
||||
uint16_t src_bits = floatBits(operand_ty);
|
||||
uint16_t dst_bits = floatBits(dest_ty);
|
||||
AirInstTag air_tag;
|
||||
if (dst_bits >= src_bits) {
|
||||
air_tag = AIR_INST_FPEXT;
|
||||
} else {
|
||||
air_tag = AIR_INST_FPTRUNC;
|
||||
}
|
||||
AirInstData data;
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.ty_op.ty_ref = AIR_REF_FROM_IP(dest_ty);
|
||||
data.ty_op.operand = operand;
|
||||
return blockAddInst(block, air_tag, data);
|
||||
}
|
||||
|
||||
// zirTypeofLog2IntType: compute the log2 integer type for shift amounts.
|
||||
// Ported from src/Sema.zig zirTypeofLog2IntType.
|
||||
// For an integer type with N bits, returns an unsigned integer type with
|
||||
@@ -646,7 +858,7 @@ static void zirTypeofLog2IntType(Sema* sema, uint32_t inst) {
|
||||
// zirAsShiftOperand: coerce a shift amount to the correct type.
|
||||
// Ported from src/Sema.zig zirAsShiftOperand.
|
||||
// Uses pl_node + As payload (dest_type, operand) — same layout as as_node.
|
||||
static void zirAsShiftOperand(Sema* sema, uint32_t inst) {
|
||||
static void zirAsShiftOperand(Sema* sema, SemaBlock* block, uint32_t inst) {
|
||||
uint32_t payload_index
|
||||
= sema->code.inst_datas[inst].pl_node.payload_index;
|
||||
ZirInstRef dest_ty_ref = sema->code.extra[payload_index];
|
||||
@@ -657,7 +869,7 @@ static void zirAsShiftOperand(Sema* sema, uint32_t inst) {
|
||||
TypeIndex dest_ty = (TypeIndex)dest_ty_air;
|
||||
AirInstRef operand = resolveInst(sema, operand_ref);
|
||||
// Coerce the operand (typically a comptime int) to the shift type.
|
||||
AirInstRef result = semaCoerce(sema, NULL, dest_ty, operand);
|
||||
AirInstRef result = semaCoerce(sema, block, dest_ty, operand);
|
||||
instMapPut(&sema->inst_map, inst, result);
|
||||
}
|
||||
|
||||
@@ -748,6 +960,8 @@ static void zirFunc(Sema* sema, SemaBlock* block, uint32_t inst) {
|
||||
}
|
||||
if (has_ret_ty_body) {
|
||||
uint32_t rtb_len = sema->code.extra[extra_index];
|
||||
ret_ty_body_len = rtb_len;
|
||||
ret_ty_ref_pos = extra_index + 1;
|
||||
extra_index += 1 + rtb_len;
|
||||
} else if (has_ret_ty_ref) {
|
||||
ret_ty_body_len = 1; // single ref
|
||||
@@ -766,10 +980,8 @@ static void zirFunc(Sema* sema, SemaBlock* block, uint32_t inst) {
|
||||
param_block_pi = 1; // param_block at payload_index + 1
|
||||
|
||||
extra_index = payload_index + 3;
|
||||
if (ret_ty_body_len == 1) {
|
||||
if (ret_ty_body_len >= 1) {
|
||||
ret_ty_ref_pos = extra_index;
|
||||
extra_index += 1;
|
||||
} else if (ret_ty_body_len > 1) {
|
||||
extra_index += ret_ty_body_len;
|
||||
}
|
||||
}
|
||||
@@ -825,6 +1037,33 @@ static void zirFunc(Sema* sema, SemaBlock* block, uint32_t inst) {
|
||||
// For pre-interned refs, the ZIR ref == IP index.
|
||||
assert(ret_ty_ref < ZIR_REF_START_INDEX);
|
||||
sema->fn_ret_ty = ret_ty_ref;
|
||||
} else if (ret_ty_body_len == 2) {
|
||||
// Two-instruction return type body: [ptr_type, break_inline].
|
||||
uint32_t ret_body_start = ret_ty_ref_pos; // set by both func and func_fancy paths
|
||||
uint32_t first_inst = sema->code.extra[ret_body_start];
|
||||
ZirInstTag first_tag = sema->code.inst_tags[first_inst];
|
||||
if (first_tag == ZIR_INST_PTR_TYPE) {
|
||||
uint8_t zir_flags
|
||||
= sema->code.inst_datas[first_inst].ptr_type.flags;
|
||||
uint8_t zir_size
|
||||
= sema->code.inst_datas[first_inst].ptr_type.size;
|
||||
uint32_t pi
|
||||
= sema->code.inst_datas[first_inst].ptr_type.payload_index;
|
||||
ZirInstRef elem_ty_ref = sema->code.extra[pi];
|
||||
assert(elem_ty_ref < ZIR_REF_START_INDEX);
|
||||
TypeIndex elem_ty = elem_ty_ref;
|
||||
uint32_t ip_flags = (uint32_t)zir_size & PTR_FLAGS_SIZE_MASK;
|
||||
if (!(zir_flags & 0x02))
|
||||
ip_flags |= PTR_FLAGS_IS_CONST;
|
||||
InternPoolKey key;
|
||||
memset(&key, 0, sizeof(key));
|
||||
key.tag = IP_KEY_PTR_TYPE;
|
||||
key.data.ptr_type.child = elem_ty;
|
||||
key.data.ptr_type.flags = ip_flags;
|
||||
sema->fn_ret_ty = ipIntern(sema->ip, key);
|
||||
} else {
|
||||
sema->fn_ret_ty = IP_INDEX_VOID_TYPE;
|
||||
}
|
||||
} else {
|
||||
// Multi-instruction return type body — not yet supported.
|
||||
sema->fn_ret_ty = IP_INDEX_VOID_TYPE;
|
||||
@@ -1159,7 +1398,7 @@ static bool analyzeBodyInner(
|
||||
= sema->code.inst_datas[inst].un_node.operand;
|
||||
AirInstRef operand = resolveInst(sema, operand_ref);
|
||||
// Coerce the operand to the function return type.
|
||||
operand = coerceIntRef(sema, operand, sema->fn_ret_ty);
|
||||
operand = semaCoerce(sema, block, sema->fn_ret_ty, operand);
|
||||
AirInstData ret_data;
|
||||
memset(&ret_data, 0, sizeof(ret_data));
|
||||
ret_data.un_op.operand = operand;
|
||||
@@ -1295,22 +1534,37 @@ static bool analyzeBodyInner(
|
||||
continue;
|
||||
}
|
||||
|
||||
// Arithmetic: add, sub, mul.
|
||||
// Arithmetic: add, sub, mul and wrapping variants.
|
||||
case ZIR_INST_ADD:
|
||||
instMapPut(&sema->inst_map, inst,
|
||||
zirArithmetic(sema, block, inst, AIR_INST_ADD));
|
||||
i++;
|
||||
continue;
|
||||
case ZIR_INST_ADDWRAP:
|
||||
instMapPut(&sema->inst_map, inst,
|
||||
zirArithmetic(sema, block, inst, AIR_INST_ADD_WRAP));
|
||||
i++;
|
||||
continue;
|
||||
case ZIR_INST_SUB:
|
||||
instMapPut(&sema->inst_map, inst,
|
||||
zirArithmetic(sema, block, inst, AIR_INST_SUB));
|
||||
i++;
|
||||
continue;
|
||||
case ZIR_INST_SUBWRAP:
|
||||
instMapPut(&sema->inst_map, inst,
|
||||
zirArithmetic(sema, block, inst, AIR_INST_SUB_WRAP));
|
||||
i++;
|
||||
continue;
|
||||
case ZIR_INST_MUL:
|
||||
instMapPut(&sema->inst_map, inst,
|
||||
zirArithmetic(sema, block, inst, AIR_INST_MUL));
|
||||
i++;
|
||||
continue;
|
||||
case ZIR_INST_MULWRAP:
|
||||
instMapPut(&sema->inst_map, inst,
|
||||
zirArithmetic(sema, block, inst, AIR_INST_MUL_WRAP));
|
||||
i++;
|
||||
continue;
|
||||
|
||||
// Comparisons: same binary pattern as arithmetic.
|
||||
case ZIR_INST_CMP_LT:
|
||||
@@ -1382,6 +1636,27 @@ static bool analyzeBodyInner(
|
||||
i++;
|
||||
continue;
|
||||
|
||||
// @intFromFloat.
|
||||
case ZIR_INST_INT_FROM_FLOAT:
|
||||
instMapPut(&sema->inst_map, inst,
|
||||
zirTyOpCast(sema, block, inst, AIR_INST_INT_FROM_FLOAT));
|
||||
i++;
|
||||
continue;
|
||||
|
||||
// @floatFromInt.
|
||||
case ZIR_INST_FLOAT_FROM_INT:
|
||||
instMapPut(&sema->inst_map, inst,
|
||||
zirTyOpCast(sema, block, inst, AIR_INST_FLOAT_FROM_INT));
|
||||
i++;
|
||||
continue;
|
||||
|
||||
// @floatCast.
|
||||
case ZIR_INST_FLOAT_CAST:
|
||||
instMapPut(&sema->inst_map, inst,
|
||||
zirFloatCast(sema, block, inst));
|
||||
i++;
|
||||
continue;
|
||||
|
||||
// Shift operations.
|
||||
case ZIR_INST_SHL:
|
||||
instMapPut(&sema->inst_map, inst,
|
||||
@@ -1402,7 +1677,7 @@ static bool analyzeBodyInner(
|
||||
|
||||
// Shift operand coercion.
|
||||
case ZIR_INST_AS_SHIFT_OPERAND:
|
||||
zirAsShiftOperand(sema, inst);
|
||||
zirAsShiftOperand(sema, block, inst);
|
||||
i++;
|
||||
continue;
|
||||
|
||||
@@ -1419,6 +1694,59 @@ static bool analyzeBodyInner(
|
||||
i++;
|
||||
continue;
|
||||
|
||||
// Load (pointer dereference).
|
||||
case ZIR_INST_LOAD:
|
||||
instMapPut(&sema->inst_map, inst,
|
||||
zirLoad(sema, block, inst));
|
||||
i++;
|
||||
continue;
|
||||
|
||||
// Unary operations.
|
||||
case ZIR_INST_NEGATE:
|
||||
instMapPut(&sema->inst_map, inst,
|
||||
zirNegate(sema, block, inst));
|
||||
i++;
|
||||
continue;
|
||||
case ZIR_INST_BIT_NOT:
|
||||
instMapPut(&sema->inst_map, inst,
|
||||
zirBitNot(sema, block, inst));
|
||||
i++;
|
||||
continue;
|
||||
case ZIR_INST_BOOL_NOT:
|
||||
instMapPut(&sema->inst_map, inst,
|
||||
zirBoolNot(sema, block, inst));
|
||||
i++;
|
||||
continue;
|
||||
case ZIR_INST_NEGATE_WRAP:
|
||||
instMapPut(&sema->inst_map, inst,
|
||||
zirNegateWrap(sema, block, inst));
|
||||
i++;
|
||||
continue;
|
||||
|
||||
// Bit counting operations.
|
||||
case ZIR_INST_CLZ:
|
||||
instMapPut(&sema->inst_map, inst,
|
||||
zirBitCount(sema, block, inst, AIR_INST_CLZ));
|
||||
i++;
|
||||
continue;
|
||||
case ZIR_INST_CTZ:
|
||||
instMapPut(&sema->inst_map, inst,
|
||||
zirBitCount(sema, block, inst, AIR_INST_CTZ));
|
||||
i++;
|
||||
continue;
|
||||
case ZIR_INST_POP_COUNT:
|
||||
instMapPut(&sema->inst_map, inst,
|
||||
zirBitCount(sema, block, inst, AIR_INST_POPCOUNT));
|
||||
i++;
|
||||
continue;
|
||||
|
||||
// @byteSwap.
|
||||
case ZIR_INST_BYTE_SWAP:
|
||||
instMapPut(&sema->inst_map, inst,
|
||||
zirByteSwap(sema, block, inst));
|
||||
i++;
|
||||
continue;
|
||||
|
||||
// Validation-only (no AIR emitted).
|
||||
case ZIR_INST_VALIDATE_DEREF:
|
||||
case ZIR_INST_VALIDATE_CONST:
|
||||
|
||||
@@ -398,8 +398,6 @@ fn airDataRefSlots(tag_val: u8) [2]bool {
|
||||
c.AIR_INST_RET,
|
||||
c.AIR_INST_RET_SAFE,
|
||||
c.AIR_INST_RET_LOAD,
|
||||
c.AIR_INST_NOT,
|
||||
c.AIR_INST_LOAD,
|
||||
c.AIR_INST_NEG,
|
||||
c.AIR_INST_NEG_OPTIMIZED,
|
||||
c.AIR_INST_IS_NULL,
|
||||
@@ -438,12 +436,15 @@ fn airDataRefSlots(tag_val: u8) [2]bool {
|
||||
c.AIR_INST_ADD,
|
||||
c.AIR_INST_ADD_SAFE,
|
||||
c.AIR_INST_ADD_OPTIMIZED,
|
||||
c.AIR_INST_ADD_WRAP,
|
||||
c.AIR_INST_SUB,
|
||||
c.AIR_INST_SUB_SAFE,
|
||||
c.AIR_INST_SUB_OPTIMIZED,
|
||||
c.AIR_INST_SUB_WRAP,
|
||||
c.AIR_INST_MUL,
|
||||
c.AIR_INST_MUL_SAFE,
|
||||
c.AIR_INST_MUL_OPTIMIZED,
|
||||
c.AIR_INST_MUL_WRAP,
|
||||
c.AIR_INST_BOOL_AND,
|
||||
c.AIR_INST_BOOL_OR,
|
||||
c.AIR_INST_STORE,
|
||||
@@ -478,6 +479,17 @@ fn airDataRefSlots(tag_val: u8) [2]bool {
|
||||
c.AIR_INST_WRAP_ERRUNION_PAYLOAD,
|
||||
c.AIR_INST_WRAP_ERRUNION_ERR,
|
||||
c.AIR_INST_ARRAY_TO_SLICE,
|
||||
c.AIR_INST_LOAD,
|
||||
c.AIR_INST_NOT,
|
||||
c.AIR_INST_INT_FROM_FLOAT,
|
||||
c.AIR_INST_INT_FROM_FLOAT_OPTIMIZED,
|
||||
c.AIR_INST_INT_FROM_FLOAT_SAFE,
|
||||
c.AIR_INST_INT_FROM_FLOAT_OPTIMIZED_SAFE,
|
||||
c.AIR_INST_FLOAT_FROM_INT,
|
||||
c.AIR_INST_CLZ,
|
||||
c.AIR_INST_CTZ,
|
||||
c.AIR_INST_POPCOUNT,
|
||||
c.AIR_INST_BYTE_SWAP,
|
||||
=> .{ true, true },
|
||||
// arg: type(Ref) + zir_param_index(u32)
|
||||
c.AIR_INST_ARG => .{ true, false },
|
||||
@@ -806,6 +818,30 @@ test "sema air: sub comptime" {
|
||||
try semaAirRawCheck("export fn f(x: u32) u32 { return x - 1; }");
|
||||
}
|
||||
|
||||
test "sema air: store runtime value" {
|
||||
try semaAirRawCheck(
|
||||
\\export fn f(p: *u32, x: u32) void {
|
||||
\\ p.* = x;
|
||||
\\}
|
||||
);
|
||||
}
|
||||
|
||||
test "sema air: load from pointer" {
|
||||
try semaAirRawCheck(
|
||||
\\export fn f(p: *u32) u32 {
|
||||
\\ return p.*;
|
||||
\\}
|
||||
);
|
||||
}
|
||||
|
||||
test "sema air: negate" {
|
||||
try semaAirRawCheck("export fn f(x: i32) i32 { return -x; }");
|
||||
}
|
||||
|
||||
test "sema air: bit not" {
|
||||
try semaAirRawCheck("export fn f(x: u32) u32 { return ~x; }");
|
||||
}
|
||||
|
||||
test "sema air: bit shift left" {
|
||||
try semaAirRawCheck("export fn f(x: u32) u32 { return x << 1; }");
|
||||
}
|
||||
@@ -827,3 +863,98 @@ test "sema air: two local bindings" {
|
||||
\\}
|
||||
);
|
||||
}
|
||||
|
||||
test "sema air: wrapping add" {
|
||||
try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return x +% y; }");
|
||||
}
|
||||
|
||||
test "sema air: wrapping sub" {
|
||||
try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return x -% y; }");
|
||||
}
|
||||
|
||||
test "sema air: wrapping mul" {
|
||||
try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return x *% y; }");
|
||||
}
|
||||
|
||||
// test "sema air: div" {
|
||||
// // Requires zirDiv with safety checks (div_trunc + remainder check).
|
||||
// try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return x / y; }");
|
||||
// }
|
||||
|
||||
test "sema air: bool not" {
|
||||
try semaAirRawCheck("export fn f(x: bool) bool { return !x; }");
|
||||
}
|
||||
|
||||
// test "sema air: if simple" {
|
||||
// // Requires condbr, block merging, conditional branching.
|
||||
// try semaAirRawCheck(
|
||||
// \\export fn f(x: u32, y: u32) u32 {
|
||||
// \\ if (x > y) return x;
|
||||
// \\ return y;
|
||||
// \\}
|
||||
// );
|
||||
// }
|
||||
|
||||
test "sema air: wrapping negate" {
|
||||
try semaAirRawCheck("export fn f(x: i32) i32 { return -%x; }");
|
||||
}
|
||||
|
||||
test "sema air: clz" {
|
||||
try semaAirRawCheck("export fn f(x: u32) u32 { return @clz(x); }");
|
||||
}
|
||||
|
||||
test "sema air: ctz" {
|
||||
try semaAirRawCheck("export fn f(x: u32) u32 { return @ctz(x); }");
|
||||
}
|
||||
|
||||
test "sema air: popcount" {
|
||||
try semaAirRawCheck("export fn f(x: u32) u32 { return @popCount(x); }");
|
||||
}
|
||||
|
||||
test "sema air: byteswap" {
|
||||
try semaAirRawCheck("export fn f(x: u32) u32 { return @byteSwap(x); }");
|
||||
}
|
||||
|
||||
test "sema air: float cast widen" {
|
||||
try semaAirRawCheck("export fn f(x: f32) f64 { return @floatCast(x); }");
|
||||
}
|
||||
|
||||
test "sema air: float cast narrow" {
|
||||
try semaAirRawCheck("export fn f(x: f64) f32 { return @floatCast(x); }");
|
||||
}
|
||||
|
||||
test "sema air: int from float" {
|
||||
try semaAirRawCheck("export fn f(x: f32) u32 { return @intFromFloat(x); }");
|
||||
}
|
||||
|
||||
test "sema air: float from int" {
|
||||
try semaAirRawCheck("export fn f(x: u32) f32 { return @floatFromInt(x); }");
|
||||
}
|
||||
|
||||
test "sema air: bitmask shift and" {
|
||||
try semaAirRawCheck("export fn f(x: u32) u32 { return (x >> 16) & 0xFF; }");
|
||||
}
|
||||
|
||||
test "sema air: double negate" {
|
||||
try semaAirRawCheck("export fn f(x: i32) i32 { return -(-x); }");
|
||||
}
|
||||
|
||||
test "sema air: return ptr type" {
|
||||
try semaAirRawCheck("export fn f(p: *u32) *u32 { return p; }");
|
||||
}
|
||||
|
||||
test "sema air: float cast f16 to f32" {
|
||||
try semaAirRawCheck("export fn f(x: f16) f32 { return @floatCast(x); }");
|
||||
}
|
||||
|
||||
test "sema air: wrapping add comptime" {
|
||||
try semaAirRawCheck("export fn f(x: u32) u32 { return x +% 1; }");
|
||||
}
|
||||
|
||||
test "sema air: byteswap and xor" {
|
||||
try semaAirRawCheck(
|
||||
\\export fn f(x: u32) u32 {
|
||||
\\ return @byteSwap(x) ^ 0xFF;
|
||||
\\}
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user