diff --git a/stage0/sema.c b/stage0/sema.c index 4370ef17dc..95bf473a51 100644 --- a/stage0/sema.c +++ b/stage0/sema.c @@ -566,6 +566,10 @@ static TypeIndex semaTypeOf(Sema* sema, AirInstRef ref) { case AIR_INST_SHL: case AIR_INST_SHL_SAT: case AIR_INST_SHR: + case AIR_INST_MAX: + case AIR_INST_MIN: + case AIR_INST_DIV_FLOAT: + case AIR_INST_DIV_TRUNC: return semaTypeOf(sema, sema->air_inst_datas[inst_idx].bin_op.lhs); // cmp bin_op: result type is bool. case AIR_INST_CMP_LT: @@ -592,6 +596,7 @@ static TypeIndex semaTypeOf(Sema* sema, AirInstRef ref) { case AIR_INST_CTZ: case AIR_INST_POPCOUNT: case AIR_INST_BYTE_SWAP: + case AIR_INST_ABS: case AIR_INST_DBG_INLINE_BLOCK: case AIR_INST_BLOCK: case AIR_INST_STRUCT_FIELD_PTR_INDEX_0: @@ -600,6 +605,7 @@ static TypeIndex semaTypeOf(Sema* sema, AirInstRef ref) { case AIR_INST_STRUCT_FIELD_PTR_INDEX_3: return AIR_REF_TO_IP(sema->air_inst_datas[inst_idx].ty_op.ty_ref); case AIR_INST_STRUCT_FIELD_VAL: + case AIR_INST_AGGREGATE_INIT: return AIR_REF_TO_IP(sema->air_inst_datas[inst_idx].ty_pl.ty_ref); // call: return type from side table (populated by zirCall). case AIR_INST_CALL: @@ -657,6 +663,10 @@ static TypeIndex semaResolvePeerTypes( return cIntToRegularInt(rhs_ty); if (rhs_ty == IP_INDEX_COMPTIME_INT_TYPE) return cIntToRegularInt(lhs_ty); + if (lhs_ty == IP_INDEX_COMPTIME_FLOAT_TYPE) + return rhs_ty; + if (rhs_ty == IP_INDEX_COMPTIME_FLOAT_TYPE) + return lhs_ty; // When both types are concrete int types, pick the wider type. // Ported from src/Sema.zig peer_resolve_int_int (fixed_int strategy). if (sema->ip->items[lhs_ty].tag == IP_KEY_INT_TYPE @@ -1064,6 +1074,25 @@ static uint16_t smallestUnsignedBits(uint16_t max) { return count; } +// zirAbs: handle @abs ZIR instruction. +// Ported from src/Sema.zig zirAbs. +// For floats/comptime_float: result_ty = operand_ty. +// For signed ints: result_ty = toUnsigned(operand_ty). +// For unsigned ints: identity (return operand). +static AirInstRef zirAbs(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); + + // For floats, result type is the same as operand type. + // Emit AIR_INST_ABS as ty_op. + AirInstData data; + memset(&data, 0, sizeof(data)); + data.ty_op.ty_ref = AIR_REF_FROM_IP(operand_ty); + data.ty_op.operand = operand; + return semaAddInst(block, AIR_INST_ABS, data); +} + // zirBitCount: handle clz/ctz/pop_count ZIR instructions. // Ported from src/Sema.zig zirBitCount. // Result type is smallestUnsignedInt(operand_bits). @@ -1248,6 +1277,35 @@ emit_runtime:; return semaAddInst(block, air_tag, data); } +// zirDiv: handle div ZIR instruction (/ operator). +// Ported from src/Sema.zig zirDiv. +// For floats: emits AIR_INST_DIV_FLOAT (strict mode). +// For unsigned ints: emits AIR_INST_DIV_TRUNC. +static AirInstRef zirDiv(Sema* sema, SemaBlock* block, uint32_t inst) { + uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; + ZirInstRef zir_lhs = sema->code.extra[payload_index]; + ZirInstRef zir_rhs = sema->code.extra[payload_index + 1]; + AirInstRef lhs = resolveInst(sema, zir_lhs); + AirInstRef rhs = resolveInst(sema, zir_rhs); + + TypeIndex peer_ty = semaResolvePeerTypes(sema, lhs, rhs); + lhs = semaCoerce(sema, block, peer_ty, lhs); + rhs = semaCoerce(sema, block, peer_ty, rhs); + + // Determine air_tag based on scalar type. + AirInstTag air_tag = AIR_INST_DIV_FLOAT; // default for floats + InternPoolKey pk = ipIndexToKey(sema->ip, peer_ty); + if (pk.tag == IP_KEY_INT_TYPE) { + air_tag = AIR_INST_DIV_TRUNC; + } + + AirInstData data; + memset(&data, 0, sizeof(data)); + data.bin_op.lhs = lhs; + data.bin_op.rhs = rhs; + return semaAddInst(block, air_tag, data); +} + // zirBitwise: handle bitwise operation ZIR instructions. // Ported from src/Sema.zig zirBitwise. static AirInstRef zirBitwise( @@ -2259,6 +2317,11 @@ static uint32_t findFuncInstInZir(const Zir* zir, const char* func_name) { uint32_t flags_1 = zir->extra[payload + 5]; uint32_t id = (flags_1 >> 27) & 0x1F; + // Skip test declarations (unnamed_test=0, test=1, decltest=2) + // and comptime blocks (3) — only look at const/var/export decls. + if (id <= 3) + continue; + uint32_t di = payload + 6; // Extract declaration name. @@ -2846,18 +2909,6 @@ static AirInstRef zirCall( } } - // Debug: save callee name before chain resolution changes sema->code. - char dbg_callee[64] = "?"; - if (callee_name_idx) { - const char* nm - = (const char*)&sema->code.string_bytes[callee_name_idx]; - size_t nl = strlen(nm); - if (nl > 63) - nl = 63; - memcpy(dbg_callee, nm, nl); - dbg_callee[nl] = 0; - } - // Find the inline function's ZIR instruction. uint32_t func_inst = findDeclFuncInst(sema, callee_name_idx); // Fallback: during cross-module inline expansion, sema->decl_names @@ -3004,19 +3055,29 @@ static AirInstRef zirCall( zirDeinit(&cur_zir); astDeinit(&cur_ast); if (fn_zir.inst_len > 0) { - // Function should be directly in this - // module with same name. + // Try direct lookup first, then follow + // re-exports (e.g. scalbn -> ldexp). + char fn_src_dir[1024]; + computeSourceDir(NULL, cur_dir, fn_import, + fn_src_dir, sizeof(fn_src_dir)); func_inst - = findFuncInstInZir(&fn_zir, fn_name); + = findFuncInModuleZir(fn_src_dir, &fn_zir, + &fn_ast, fn_name, import_source_dir, + sizeof(import_source_dir)); if (func_inst != UINT32_MAX) { + // If findFuncInModuleZir found it + // directly (no re-export), it did + // not write import_source_dir. + // Set it to fn_src_dir as fallback. + if (import_source_dir[0] == '\0') + snprintf(import_source_dir, + sizeof(import_source_dir), "%s", + fn_src_dir); saved_code = sema->code; sema->code = fn_zir; import_zir = fn_zir; import_ast = fn_ast; is_cross_module = true; - computeSourceDir(NULL, cur_dir, fn_import, - import_source_dir, - sizeof(import_source_dir)); } else { zirDeinit(&fn_zir); astDeinit(&fn_ast); @@ -3844,19 +3905,6 @@ static AirInstRef zirCall( sema->code = arg_code; } - // Ported from src/Sema.zig line 7480: - // if (call_dbg_node) |some| try sema.zirDbgStmt(block, some); - // call_dbg_node is the ZIR instruction preceding the call (inst - 1). - // Must use the caller's ZIR for cross-module calls. - if (inst > 0 && !block->is_comptime) { - Zir dbg_code = sema->code; - if (is_cross_module) - sema->code = saved_code; - zirDbgStmt(sema, block, inst - 1); - if (is_cross_module) - sema->code = dbg_code; - } - // Handle type-returning functions whose result can be computed from // the comptime arguments without inlining. // Upstream always reserves a dead BLOCK before inlining; we match @@ -4255,8 +4303,15 @@ static AirInstRef zirCall( if (param_body[p] == ret_ty_inst && pi < args_len) { // Return type ref matches this param. // For comptime type params, the arg value IS - // the type. - ret_ty = AIR_REF_TO_IP(arg_refs[pi]); + // the type (an IP ref). For runtime params + // (including anytype), use semaTypeOf to get + // the argument's type. + if (ptag == ZIR_INST_PARAM_COMPTIME + || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) { + ret_ty = AIR_REF_TO_IP(arg_refs[pi]); + } else { + ret_ty = semaTypeOf(sema, arg_refs[pi]); + } break; } pi++; @@ -4278,12 +4333,6 @@ static AirInstRef zirCall( || ret_ty == IP_INDEX_ENUM_LITERAL_TYPE); bool is_inline_call = func_info.is_inline || block->is_comptime || is_comptime_only_ret; - if (strcmp(dbg_callee, "copysign") == 0) - fprintf(stderr, - " COPYSIGN_PATH: is_inline=%d is_comptime=%d ct_only_ret=%d" - " ret_ty=%u is_generic=%d\n", - func_info.is_inline, block->is_comptime, is_comptime_only_ret, - ret_ty, is_generic); if (!is_inline_call) { // Dummy allocs for generic runtime params are now emitted // interleaved in the arg evaluation loop above (Fix A). @@ -4359,8 +4408,12 @@ static AirInstRef zirCall( semaAddExtra(sema, runtime_arg_refs[a]); // Ported from Sema.zig:7480: AstGen ensures a dbg_stmt - // always precedes a call instruction. For cross-module - // calls, inst-1 is in the calling module's ZIR. + // always precedes a call instruction. Use the caller's ZIR + // for cross-module calls. Note: the general dbg_stmt at the + // top of zirCall (before arg evaluation) may have been + // followed by arg instructions, so emit another one here + // to directly precede the call. zirDbgStmt de-duplicates + // when the previous block instruction is already a dbg_stmt. if (is_cross_module) { Zir temp = sema->code; sema->code = saved_code; @@ -4440,6 +4493,18 @@ static AirInstRef zirCall( } } + // Ported from src/Sema.zig line 7480: emit dbg_stmt for inline + // calls, just before the inline block. Use the caller's ZIR + // for cross-module calls. + if (inst > 0 && !block->is_comptime) { + Zir dbg_code = sema->code; + if (is_cross_module) + sema->code = saved_code; + zirDbgStmt(sema, block, inst - 1); + if (is_cross_module) + sema->code = dbg_code; + } + // Upstream: need_debug_scope = !block.isComptime() && ... // When comptime (or all args comptime), use BLOCK; otherwise // DBG_INLINE_BLOCK. @@ -5884,11 +5949,11 @@ static InternPoolIndex resolveUnsignedIntType(InternPool* ip, uint32_t bits) { } } -// zirStructInitComptime: handle struct_init in comptime context. -// For @Type(.{.int = .{.signedness = .unsigned, .bits = N}}): -// - inner struct: extract signedness and bits, track as CT_TAG_INT_INFO -// - outer struct: extract field name (.int), track as CT_TAG_REIFY_INT -static AirInstRef zirStructInitComptime(Sema* sema, uint32_t inst) { +// zirStructInit: handle struct_init ZIR instruction. +// Comptime case: @Type(.{.int = .{.signedness = .unsigned, .bits = N}}) +// Runtime case: emits AIR_INST_AGGREGATE_INIT with field values. +// Ported from src/Sema.zig zirStructInit / finishStructInit. +static AirInstRef zirStructInit(Sema* sema, SemaBlock* block, uint32_t inst) { uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; // StructInit payload: abs_node, abs_line, fields_len, then Items. uint32_t fields_len = sema->code.extra[payload_index + 2]; @@ -5967,7 +6032,32 @@ static AirInstRef zirStructInitComptime(Sema* sema, uint32_t inst) { return AIR_REF_FROM_IP(marker); } - return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + // Runtime struct init: emit aggregate_init. + // Ported from Sema.zig finishStructInit line 19655. + // The field_type instructions provide the container type. + // Resolve the container type from the first field_type's container ref. + { + uint32_t ft_payload0 + = sema->code.inst_datas[items[0]].pl_node.payload_index; + ZirInstRef container_ref = sema->code.extra[ft_payload0]; + AirInstRef container_air = resolveInst(sema, container_ref); + TypeIndex container_ty = AIR_REF_IS_IP(container_air) + ? AIR_REF_TO_IP(container_air) + : IP_INDEX_VOID_TYPE; + + // Build aggregate_init extra: element refs in field order. + uint32_t extra_idx = sema->air_extra_len; + for (uint32_t f = 0; f < fields_len; f++) { + ZirInstRef init_ref2 = items[f * 2 + 1]; + AirInstRef init_air2 = resolveInst(sema, init_ref2); + semaAddExtra(sema, init_air2); + } + AirInstData data; + memset(&data, 0, sizeof(data)); + data.ty_pl.ty_ref = AIR_REF_FROM_IP(container_ty); + data.ty_pl.payload = extra_idx; + return semaAddInst(block, AIR_INST_AGGREGATE_INIT, data); + } } // zirReifyComptime: handle @Type(...) in comptime context. @@ -6629,6 +6719,10 @@ static bool analyzeBodyInner( zirArithmetic(sema, block, inst, AIR_INST_MUL_WRAP)); i++; continue; + case ZIR_INST_DIV: + instMapPut(&sema->inst_map, inst, zirDiv(sema, block, inst)); + i++; + continue; case ZIR_INST_ADD_SAT: instMapPut(&sema->inst_map, inst, zirArithmetic(sema, block, inst, AIR_INST_ADD_SAT)); @@ -6724,8 +6818,18 @@ static bool analyzeBodyInner( instMapPut(&sema->inst_map, inst, AIR_REF_FROM_IP(ipIntern(sema->ip, key))); } else { + // Runtime: emit AIR_INST_MAX or AIR_INST_MIN. + TypeIndex peer_ty + = semaResolvePeerTypes(sema, min_lhs, min_rhs); + AirInstRef c_lhs = semaCoerce(sema, block, peer_ty, min_lhs); + AirInstRef c_rhs = semaCoerce(sema, block, peer_ty, min_rhs); + AirInstData data; + memset(&data, 0, sizeof(data)); + data.bin_op.lhs = c_lhs; + data.bin_op.rhs = c_rhs; instMapPut(&sema->inst_map, inst, - AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE)); + semaAddInst( + block, is_max ? AIR_INST_MAX : AIR_INST_MIN, data)); } i++; continue; @@ -6860,9 +6964,16 @@ static bool analyzeBodyInner( i++; continue; + // @abs. + case ZIR_INST_ABS: + instMapPut(&sema->inst_map, inst, zirAbs(sema, block, inst)); + i++; + continue; + // Validation-only (no AIR emitted). case ZIR_INST_VALIDATE_DEREF: case ZIR_INST_VALIDATE_CONST: + case ZIR_INST_VALIDATE_REF_TY: i++; continue; @@ -7078,10 +7189,10 @@ static bool analyzeBodyInner( i++; continue; - // struct_init: comptime struct initialization. + // struct_init: struct initialization (comptime or runtime). case ZIR_INST_STRUCT_INIT: instMapPut( - &sema->inst_map, inst, zirStructInitComptime(sema, inst)); + &sema->inst_map, inst, zirStructInit(sema, block, inst)); i++; continue; @@ -7676,8 +7787,6 @@ static bool analyzeBodyInner( } semaAddExtra(sema, sub_br_idx); sema->air_inst_tags[br] = AIR_INST_BLOCK; - fprintf( - stderr, " REWRITE_BR_BLOCK: air_idx=%u\n", br); sema->air_inst_datas[br].ty_pl.ty_ref = AIR_REF_FROM_IP(resolved_ty); sema->air_inst_datas[br].ty_pl.payload = sub_extra; @@ -8127,7 +8236,6 @@ static bool analyzeBodyInner( // For all other instructions, produce a void mapping and skip. // As handlers are implemented, they will replace this default. default: { - fprintf(stderr, " UNHANDLED: inst=%u tag=%u\n", inst, inst_tag); AirInstRef air_ref = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); instMapPut(&sema->inst_map, inst, air_ref); i++; diff --git a/stage0/sema_test.zig b/stage0/sema_test.zig index 396749f42c..2df75d5ed3 100644 --- a/stage0/sema_test.zig +++ b/stage0/sema_test.zig @@ -456,6 +456,7 @@ fn airDataRefSlots(tag_val: u8) [2]bool { c.AIR_INST_STRUCT_FIELD_PTR, c.AIR_INST_DBG_INLINE_BLOCK, c.AIR_INST_BLOCK, + c.AIR_INST_AGGREGATE_INIT, => .{ true, false }, // bin_op: lhs(Ref) + rhs(Ref) c.AIR_INST_ADD, @@ -488,6 +489,19 @@ fn airDataRefSlots(tag_val: u8) [2]bool { c.AIR_INST_CMP_GTE, c.AIR_INST_CMP_GT, c.AIR_INST_CMP_NEQ, + c.AIR_INST_MAX, + c.AIR_INST_MIN, + c.AIR_INST_DIV_FLOAT, + c.AIR_INST_DIV_FLOAT_OPTIMIZED, + c.AIR_INST_DIV_TRUNC, + c.AIR_INST_DIV_TRUNC_OPTIMIZED, + c.AIR_INST_DIV_FLOOR, + c.AIR_INST_DIV_FLOOR_OPTIMIZED, + c.AIR_INST_DIV_EXACT, + c.AIR_INST_DIV_EXACT_OPTIMIZED, + c.AIR_INST_ADD_SAT, + c.AIR_INST_SUB_SAT, + c.AIR_INST_MUL_SAT, => .{ true, true }, // ty_op: type(Ref) + operand(Ref) c.AIR_INST_BITCAST, @@ -515,6 +529,7 @@ fn airDataRefSlots(tag_val: u8) [2]bool { c.AIR_INST_CTZ, c.AIR_INST_POPCOUNT, c.AIR_INST_BYTE_SWAP, + c.AIR_INST_ABS, c.AIR_INST_STRUCT_FIELD_PTR_INDEX_0, c.AIR_INST_STRUCT_FIELD_PTR_INDEX_1, c.AIR_INST_STRUCT_FIELD_PTR_INDEX_2, @@ -1342,3 +1357,19 @@ test "sema air: inline fn with += call inside two conditionals" { \\} ); } + +test "sema air: @abs float" { + try semaAirRawCheck("export fn f(x: f64) f64 { return @abs(x); }"); +} + +test "sema air: @max float" { + try semaAirRawCheck("export fn f(x: f64, y: f64) f64 { return @max(x, y); }"); +} + +test "sema air: @min float" { + try semaAirRawCheck("export fn f(x: f64, y: f64) f64 { return @min(x, y); }"); +} + +test "sema air: f64 div" { + try semaAirRawCheck("export fn f(x: f64, y: f64) f64 { return x / y; }"); +} diff --git a/stage0/stages_test.zig b/stage0/stages_test.zig index 394b8325ad..6d3af509d5 100644 --- a/stage0/stages_test.zig +++ b/stage0/stages_test.zig @@ -164,7 +164,7 @@ const corpus_files = .{ "../lib/compiler_rt/mulhc3.zig", // 425 "../lib/compiler_rt/mulsc3.zig", // 425 "../lib/compiler_rt/mulxc3.zig", // 425 - //"../lib/compiler_rt/divdc3.zig", // 434 + "../lib/compiler_rt/divdc3.zig", // 434 //"../lib/compiler_rt/divhc3.zig", // 434 //"../lib/compiler_rt/divsc3.zig", // 434 //"../lib/compiler_rt/divxc3.zig", // 434