disambiguate enum blocks and complete distinct type operations

This commit is contained in:
2026-08-01 23:57:35 +02:00
parent 91aa601464
commit b9526b5f06
34 changed files with 1128 additions and 1963359 deletions
+46 -28
View File
@@ -417,8 +417,9 @@ emit_recovery_value :: proc(emitter: ^Emitter, instruction_id: int, instruction:
message := diagnostic_message(emitter, instruction.diagnostic, instruction.span, fallback)
emit_trap_call(emitter, message)
if types.is_runtime_value(instruction.type, &emitter.module.types) {
if !types.is_float(instruction.type, emitter.module.target) {
if !types.is_concrete_scalar(instruction.type) {
representation := types.runtime_representation(instruction.type, &emitter.module.types)
if !types.is_float(representation, emitter.module.target) {
if !types.is_concrete_scalar(representation) {
fmt.sbprintf(
&emitter.builder,
" %%v%d = freeze %s zeroinitializer\n",
@@ -583,8 +584,9 @@ emit_checked_arithmetic :: proc(
float_op: string,
overflow_message: string,
) {
representation := types.runtime_representation(instruction.type, &emitter.module.types)
type_name := llvm_type(instruction.type, &emitter.module.types)
if types.is_float(instruction.type, emitter.module.target) {
if types.is_float(representation, emitter.module.target) {
fmt.sbprintf(&emitter.builder, " %%v%d = %s %s ", instruction_index, float_op, type_name)
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
strings.write_string(&emitter.builder, ", ")
@@ -592,7 +594,7 @@ emit_checked_arithmetic :: proc(
strings.write_string(&emitter.builder, "\n")
return
}
prefix := "u" if types.is_unsigned(instruction.type, emitter.module.target) else "s"
prefix := "u" if types.is_unsigned(representation, emitter.module.target) else "s"
fmt.sbprintf(&emitter.builder, " %%pair%d = call ", instruction_index)
strings.write_string(&emitter.builder, "{ ")
fmt.sbprintf(&emitter.builder, "%s, i1 } @llvm.%s%s.with.overflow.%s(%s ", type_name, prefix, mnemonic, type_name, type_name)
@@ -625,8 +627,9 @@ emit_division_zero_guard :: proc(
instruction_index: int,
instruction: ir.Instruction,
) {
representation := types.runtime_representation(instruction.type, &emitter.module.types)
type_name := llvm_type(instruction.type, &emitter.module.types)
if types.is_float(instruction.type, emitter.module.target) {
if types.is_float(representation, emitter.module.target) {
fmt.sbprintf(&emitter.builder, " %%divzero%d = fcmp oeq %s ", instruction_index, type_name)
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
strings.write_string(&emitter.builder, ", 0.000000e+00\n")
@@ -655,7 +658,8 @@ emit_division_overflow_guard :: proc(
instruction: ir.Instruction,
) {
type_name := llvm_type(instruction.type, &emitter.module.types)
min_value := -(i128(1) << u32(types.bits(instruction.type, emitter.module.target)-1))
representation := types.runtime_representation(instruction.type, &emitter.module.types)
min_value := -(i128(1) << u32(types.bits(representation, emitter.module.target)-1))
fmt.sbprintf(&emitter.builder, " %%divminlo%d = icmp eq %s ", instruction_index, type_name)
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
fmt.sbprintf(&emitter.builder, ", %d\n %%divminhi%d = icmp eq %s ", min_value, instruction_index, type_name)
@@ -683,7 +687,8 @@ emit_float_division_builtin :: proc(
instruction: ir.Instruction,
) {
type_name := llvm_type(instruction.type, &emitter.module.types)
suffix := "f32" if types.bits(instruction.type, emitter.module.target) == 32 else "f64"
representation := types.runtime_representation(instruction.type, &emitter.module.types)
suffix := "f32" if types.bits(representation, emitter.module.target) == 32 else "f64"
if instruction.op == .Rem_Checked || instruction.op == .Mod_Checked {
name := fmt.tprintf("%%v%d", instruction_index) if instruction.op == .Rem_Checked else fmt.tprintf("%%rawrem%d", instruction_index)
@@ -740,7 +745,8 @@ emit_integer_remainder_builtin :: proc(
instruction: ir.Instruction,
) {
type_name := llvm_type(instruction.type, &emitter.module.types)
signed := types.is_signed(instruction.type, emitter.module.target)
representation := types.runtime_representation(instruction.type, &emitter.module.types)
signed := types.is_signed(representation, emitter.module.target)
raw_name := fmt.tprintf("%%v%d", instruction_index) if instruction.op == .Rem_Checked || !signed else fmt.tprintf("%%rawrem%d", instruction_index)
if !signed {
fmt.sbprintf(&emitter.builder, " %s = urem %s ", raw_name, type_name)
@@ -749,7 +755,7 @@ emit_integer_remainder_builtin :: proc(
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
strings.write_string(&emitter.builder, "\n")
} else {
min_value := -(i128(1) << u32(types.bits(instruction.type, emitter.module.target)-1))
min_value := -(i128(1) << u32(types.bits(representation, emitter.module.target)-1))
fmt.sbprintf(&emitter.builder, " %%remminlo%d = icmp eq %s ", instruction_index, type_name)
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
fmt.sbprintf(&emitter.builder, ", %d\n %%remminhi%d = icmp eq %s ", min_value, instruction_index, type_name)
@@ -782,7 +788,8 @@ emit_integer_quotient_builtin :: proc(
instruction: ir.Instruction,
) {
type_name := llvm_type(instruction.type, &emitter.module.types)
signed := types.is_signed(instruction.type, emitter.module.target)
representation := types.runtime_representation(instruction.type, &emitter.module.types)
signed := types.is_signed(representation, emitter.module.target)
operation := "sdiv" if signed else "udiv"
name := fmt.tprintf("%%v%d", instruction_index) if instruction.op == .Div_Trunc_Checked else fmt.tprintf("%%divq%d", instruction_index)
fmt.sbprintf(&emitter.builder, " %s = %s %s ", name, operation, type_name)
@@ -832,13 +839,14 @@ emit_division_builtin :: proc(
instruction: ir.Instruction,
) {
emit_division_zero_guard(emitter, instructions, instruction_index, instruction)
if types.is_float(instruction.type, emitter.module.target) {
representation := types.runtime_representation(instruction.type, &emitter.module.types)
if types.is_float(representation, emitter.module.target) {
emit_float_division_builtin(emitter, instructions, instruction_index, instruction)
return
}
quotient := instruction.op == .Div_Trunc_Checked || instruction.op == .Div_Floor_Checked ||
instruction.op == .Div_Exact_Checked || instruction.op == .Div_Ceil_Checked
if quotient && types.is_signed(instruction.type, emitter.module.target) {
if quotient && types.is_signed(representation, emitter.module.target) {
emit_division_overflow_guard(emitter, instructions, instruction_index, instruction)
}
if quotient {
@@ -858,7 +866,8 @@ emit_shift :: proc(
if valid_instruction(instructions, instruction.b) {
count_type = instructions[instruction.b].type
}
if !types.is_concrete_integer(instruction.type) ||
representation := types.runtime_representation(instruction.type, &emitter.module.types)
if !types.is_concrete_integer(representation) ||
!valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) ||
!types.is_concrete_integer(count_type) || !types.is_unsigned(count_type, emitter.module.target) ||
!valid_value(instructions, instruction.b, count_type, &emitter.module.types) {
@@ -866,7 +875,7 @@ emit_shift :: proc(
return
}
type_name := llvm_type(instruction.type, &emitter.module.types)
bits := types.bits(instruction.type, emitter.module.target)
bits := types.bits(representation, emitter.module.target)
count_bits := types.bits(count_type, emitter.module.target)
if count_bits < 64 {
fmt.sbprintf(&emitter.builder, " %%shift_count64_%d = zext %s ", instruction_index, llvm_type(count_type, &emitter.module.types))
@@ -893,7 +902,7 @@ emit_shift :: proc(
}
operation := "shl"
if instruction.op == .Shift_Right {
operation = "ashr" if types.is_signed(instruction.type, emitter.module.target) else "lshr"
operation = "ashr" if types.is_signed(representation, emitter.module.target) else "lshr"
}
fmt.sbprintf(&emitter.builder, " %%v%d = %s %s ", instruction_index, operation, type_name)
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
@@ -911,7 +920,7 @@ emit_shift :: proc(
if bits < 64 {
fmt.sbprintf(&emitter.builder, " %%shift_safe%d = trunc i64 %%shift_safe64_%d to %s\n", instruction_index, instruction_index, type_name)
}
signed := types.is_signed(instruction.type, emitter.module.target)
signed := types.is_signed(representation, emitter.module.target)
intrinsic := "sshl" if signed else "ushl"
fmt.sbprintf(&emitter.builder, " %%shift_saturated%d = call %s @llvm.%s.sat.%s(%s ", instruction_index, type_name, intrinsic, type_name, type_name)
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
@@ -1868,8 +1877,8 @@ emit_instruction_stream :: proc(
fmt.sbprintf(&emitter.builder, " to %s\n", llvm_type(instruction.type, &emitter.module.types))
case .Retype:
if !valid_instruction(instructions, instruction.a) ||
!types.can_construct_distinct(instructions[instruction.a].type, instruction.type, &emitter.module.types) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid distinct type construction")
!types.can_retype_distinct(instructions[instruction.a].type, instruction.type, &emitter.module.types) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid distinct retype")
continue
}
type_name := llvm_type(instruction.type, &emitter.module.types)
@@ -1891,7 +1900,8 @@ emit_instruction_stream :: proc(
from_repr := types.runtime_representation(from_type, &emitter.module.types)
from_item, from_item_ok := types.node(&emitter.module.types, from_type)
explicit_enum := from_item_ok && from_item.kind == .Enum && from_item.explicit_backing
valid_from := (types.is_concrete_scalar(from_type) || explicit_enum) &&
_, distinct_scalar := types.distinct_scalar_backing(from_type, &emitter.module.types)
valid_from := (types.is_concrete_scalar(from_type) || explicit_enum || distinct_scalar) &&
types.is_concrete_scalar(from_repr) && !types.is_bool(from_repr)
if !valid_from || !types.is_concrete_scalar(instruction.type) || types.is_bool(instruction.type) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid scalar cast operand")
@@ -1973,12 +1983,15 @@ emit_instruction_stream :: proc(
fmt.sbprintf(&emitter.builder, " %%v%d = insertvalue %s %%decay_slice%d, i64 %d, 1\n", instruction_index, type_name, instruction_index, array.count)
}
case .Neg_Checked:
if !valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) {
representation := types.runtime_representation(instruction.type, &emitter.module.types)
if !valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) ||
(!types.is_signed(representation, emitter.module.target) &&
!types.is_float(representation, emitter.module.target)) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid negation operand")
continue
}
type_name := llvm_type(instruction.type, &emitter.module.types)
if types.is_float(instruction.type, emitter.module.target) {
if types.is_float(representation, emitter.module.target) {
fmt.sbprintf(&emitter.builder, " %%v%d = fneg %s ", instruction_index, type_name)
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
strings.write_string(&emitter.builder, "\n")
@@ -2028,19 +2041,21 @@ emit_instruction_stream :: proc(
}
emit_checked_arithmetic(emitter, instructions, instruction_index, instruction, "mul", "fmul", "integer multiplication overflow")
case .Div_Checked:
representation := types.runtime_representation(instruction.type, &emitter.module.types)
if !valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) ||
!valid_value(instructions, instruction.b, instruction.type, &emitter.module.types) ||
!types.is_float(instruction.type, emitter.module.target) {
!types.is_float(representation, emitter.module.target) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid division operand")
continue
}
emit_checked_arithmetic(emitter, instructions, instruction_index, instruction, "div", "fdiv", "")
case .Div_Trunc_Checked, .Div_Floor_Checked, .Div_Exact_Checked, .Div_Ceil_Checked,
.Rem_Checked, .Mod_Checked:
representation := types.runtime_representation(instruction.type, &emitter.module.types)
if !valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) ||
!valid_value(instructions, instruction.b, instruction.type, &emitter.module.types) ||
(!types.is_concrete_integer(instruction.type) &&
!types.is_float(instruction.type, emitter.module.target)) {
(!types.is_concrete_integer(representation) &&
!types.is_float(representation, emitter.module.target)) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid division builtin operands")
continue
}
@@ -2062,7 +2077,8 @@ emit_instruction_stream :: proc(
write_operand(&emitter.builder, instructions, instruction.b, types.USIZE, &emitter.module.types)
strings.write_string(&emitter.builder, "\n")
case .Bit_Not:
if !types.is_concrete_integer(instruction.type) ||
representation := types.runtime_representation(instruction.type, &emitter.module.types)
if !types.is_concrete_integer(representation) ||
!valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid bitwise complement operand")
continue
@@ -2072,7 +2088,8 @@ emit_instruction_stream :: proc(
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
strings.write_string(&emitter.builder, ", -1\n")
case .Bit_And, .Bit_Or, .Bit_Xor:
if !types.is_concrete_integer(instruction.type) ||
representation := types.runtime_representation(instruction.type, &emitter.module.types)
if !types.is_concrete_integer(representation) ||
!valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) ||
!valid_value(instructions, instruction.b, instruction.type, &emitter.module.types) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid bitwise operands")
@@ -2367,10 +2384,11 @@ emit_instruction_stream :: proc(
}
predicate := ir.Compare_Predicate(instruction.integer)
type_name := llvm_type(operand_type, &emitter.module.types)
if types.is_float(operand_type, emitter.module.target) {
representation := types.runtime_representation(operand_type, &emitter.module.types)
if types.is_float(representation, emitter.module.target) {
fmt.sbprintf(&emitter.builder, " %%v%d = fcmp %s %s ", instruction_index, float_predicate(predicate), type_name)
} else {
fmt.sbprintf(&emitter.builder, " %%v%d = icmp %s %s ", instruction_index, integer_predicate(predicate, types.is_signed(operand_type, emitter.module.target)), type_name)
fmt.sbprintf(&emitter.builder, " %%v%d = icmp %s %s ", instruction_index, integer_predicate(predicate, types.is_signed(representation, emitter.module.target)), type_name)
}
write_operand(&emitter.builder, instructions, instruction.a, operand_type, &emitter.module.types)
strings.write_string(&emitter.builder, ", ")