bitwise operations

This commit is contained in:
2026-07-19 00:36:31 +02:00
parent f9448363e4
commit c7e3162ecb
21 changed files with 219439 additions and 140077 deletions
+122 -1
View File
@@ -260,7 +260,8 @@ valid_value :: proc(
.Neg_Checked, .Add_Checked, .Sub_Checked, .Mul_Checked, .Div_Checked,
.Div_Trunc_Checked, .Div_Floor_Checked, .Div_Exact_Checked, .Div_Ceil_Checked,
.Rem_Checked, .Mod_Checked,
.Pointer_Add, .Not, .Compare, .Call:
.Pointer_Add, .Not, .Bit_Not, .Bit_And, .Bit_Or, .Bit_Xor,
.Shift_Left, .Shift_Right, .Shift_Left_Saturating, .Compare, .Call:
return true
case .Address_Global, .Alloca, .Index_Address, .Field_Address, .Orelse_Begin,
.Store, .Fill, .Trap, .Label, .Br, .Cond_Br, .Return, .Return_Void:
@@ -814,6 +815,94 @@ emit_division_builtin :: proc(
}
}
emit_shift :: proc(
emitter: ^Emitter,
instructions: []ir.Instruction,
instruction_index: int,
instruction: ir.Instruction,
) {
count_type := types.INVALID
if valid_instruction(instructions, instruction.b) {
count_type = instructions[instruction.b].type
}
if !types.is_concrete_integer(instruction.type) ||
!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) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid shift operands")
return
}
type_name := llvm_type(instruction.type, &emitter.module.types)
bits := types.bits(instruction.type, 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))
write_operand(&emitter.builder, instructions, instruction.b, count_type, &emitter.module.types)
strings.write_string(&emitter.builder, " to i64\n")
} else {
fmt.sbprintf(&emitter.builder, " %%shift_count64_%d = select i1 true, i64 ", instruction_index)
write_operand(&emitter.builder, instructions, instruction.b, count_type, &emitter.module.types)
strings.write_string(&emitter.builder, ", i64 0\n")
}
fmt.sbprintf(&emitter.builder, " %%shift_in_range%d = icmp ult i64 %%shift_count64_%d, %d\n", instruction_index, instruction_index, bits)
if instruction.op != .Shift_Left_Saturating {
fmt.sbprintf(
&emitter.builder,
" br i1 %%shift_in_range%d, label %%shift_continue%d, label %%shift_trap%d\nshift_trap%d:\n",
instruction_index, instruction_index, instruction_index, instruction_index,
)
message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "shift count exceeds integer width")
emit_trap_call(emitter, message)
fmt.sbprintf(&emitter.builder, " unreachable\nshift_continue%d:\n", instruction_index)
if bits < 64 {
fmt.sbprintf(&emitter.builder, " %%shift_count%d = trunc i64 %%shift_count64_%d to %s\n", instruction_index, instruction_index, type_name)
}
operation := "shl"
if instruction.op == .Shift_Right {
operation = "ashr" if types.is_signed(instruction.type, 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)
if bits < 64 {
fmt.sbprintf(&emitter.builder, ", %%shift_count%d\n", instruction_index)
} else {
fmt.sbprintf(&emitter.builder, ", %%shift_count64_%d\n", instruction_index)
}
return
}
// LLVM's saturating shift intrinsics produce poison for oversized counts.
// Select zero before narrowing, then explicitly select the Zig endpoint.
fmt.sbprintf(&emitter.builder, " %%shift_safe64_%d = select i1 %%shift_in_range%d, i64 %%shift_count64_%d, i64 0\n", instruction_index, instruction_index, instruction_index)
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)
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)
if bits < 64 {
fmt.sbprintf(&emitter.builder, ", %s %%shift_safe%d)\n", type_name, instruction_index)
} else {
fmt.sbprintf(&emitter.builder, ", i64 %%shift_safe64_%d)\n", instruction_index)
}
fmt.sbprintf(&emitter.builder, " %%shift_nonzero%d = icmp ne %s ", instruction_index, type_name)
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
strings.write_string(&emitter.builder, ", 0\n")
if signed {
minimum := -(i128(1) << u32(bits-1))
maximum := (i128(1) << u32(bits-1))-1
fmt.sbprintf(&emitter.builder, " %%shift_negative%d = icmp slt %s ", instruction_index, type_name)
write_operand(&emitter.builder, instructions, instruction.a, instruction.type, &emitter.module.types)
fmt.sbprintf(&emitter.builder, ", 0\n %%shift_nonzero_endpoint%d = select i1 %%shift_negative%d, %s %d, %s %d\n", instruction_index, instruction_index, type_name, minimum, type_name, maximum)
fmt.sbprintf(&emitter.builder, " %%shift_endpoint%d = select i1 %%shift_nonzero%d, %s %%shift_nonzero_endpoint%d, %s 0\n", instruction_index, instruction_index, type_name, instruction_index, type_name)
} else {
fmt.sbprintf(&emitter.builder, " %%shift_endpoint%d = select i1 %%shift_nonzero%d, %s -1, %s 0\n", instruction_index, instruction_index, type_name, type_name)
}
fmt.sbprintf(&emitter.builder, " %%v%d = select i1 %%shift_in_range%d, %s %%shift_saturated%d, %s %%shift_endpoint%d\n", instruction_index, instruction_index, type_name, instruction_index, type_name, instruction_index)
}
emit_instruction_stream :: proc(
emitter: ^Emitter,
instructions: []ir.Instruction,
@@ -1818,6 +1907,36 @@ emit_instruction_stream :: proc(
fmt.sbprintf(&emitter.builder, " %%v%d = getelementptr %s, ptr %%v%d, i64 ", instruction_index, llvm_type(result_item.child, &emitter.module.types), instruction.a)
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) ||
!valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid bitwise complement operand")
continue
}
type_name := llvm_type(instruction.type, &emitter.module.types)
fmt.sbprintf(&emitter.builder, " %%v%d = xor %s ", instruction_index, type_name)
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) ||
!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")
continue
}
operation := "and"
#partial switch instruction.op {
case .Bit_Or: operation = "or"
case .Bit_Xor: operation = "xor"
}
type_name := llvm_type(instruction.type, &emitter.module.types)
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)
strings.write_string(&emitter.builder, ", ")
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
strings.write_string(&emitter.builder, "\n")
case .Shift_Left, .Shift_Right, .Shift_Left_Saturating:
emit_shift(emitter, instructions, instruction_index, instruction)
case .Call:
function_id := ir.as_function(instruction.target)
if function_id == ir.INVALID_FUNCTION {
@@ -2488,6 +2607,8 @@ emit_declarations :: proc(emitter: ^Emitter) {
strings.write_string(&emitter.builder, ".with.overflow.i")
fmt.sbprintf(&emitter.builder, "%d(i%d, i%d)\n", bits, bits, bits)
}
fmt.sbprintf(&emitter.builder, "declare i%d @llvm.sshl.sat.i%d(i%d, i%d)\n", bits, bits, bits, bits)
fmt.sbprintf(&emitter.builder, "declare i%d @llvm.ushl.sat.i%d(i%d, i%d)\n", bits, bits, bits, bits)
}
strings.write_string(
&emitter.builder,