implement signed division

This commit is contained in:
David Gonzalez Martin 2023-11-13 10:26:05 -06:00
parent 8ab7a0d768
commit f96cf16a2a
6 changed files with 693 additions and 194 deletions

View File

@ -553,6 +553,7 @@ pub const BinaryOperation = struct {
logical_xor, logical_xor,
logical_or, logical_or,
multiply, multiply,
divide,
}; };
}; };

View File

@ -238,7 +238,8 @@ pub const BinaryOperation = struct {
logical_and, logical_and,
logical_xor, logical_xor,
logical_or, logical_or,
multiply, signed_multiply,
signed_divide,
}; };
pub const List = BlockList(@This()); pub const List = BlockList(@This());
@ -729,6 +730,9 @@ pub const Builder = struct {
const left = try builder.emitBinaryOperationOperand(sema_binary_operation.left); const left = try builder.emitBinaryOperationOperand(sema_binary_operation.left);
const right = try builder.emitBinaryOperationOperand(sema_binary_operation.right); const right = try builder.emitBinaryOperationOperand(sema_binary_operation.right);
const sema_type = builder.ir.module.types.get(sema_binary_operation.type).*;
const binary_operation_type = try builder.translateType(sema_binary_operation.type);
const binary_operation = try builder.ir.binary_operations.append(builder.allocator, .{ const binary_operation = try builder.ir.binary_operations.append(builder.allocator, .{
.left = left, .left = left,
.right = right, .right = right,
@ -738,9 +742,24 @@ pub const Builder = struct {
.logical_and => .logical_and, .logical_and => .logical_and,
.logical_xor => .logical_xor, .logical_xor => .logical_xor,
.logical_or => .logical_or, .logical_or => .logical_or,
.multiply => .multiply, .multiply => switch (sema_type) {
.integer => |integer| switch (integer.signedness) {
.signed => .signed_multiply,
else => |t| @panic(@tagName(t)),
}, },
.type = try builder.translateType(sema_binary_operation.type), else => |t| @panic(@tagName(t)),
},
//.multiply,
.divide => switch (sema_type) {
.integer => |integer| switch (integer.signedness) {
.signed => .signed_divide,
else => |t| @panic(@tagName(t)),
},
else => |t| @panic(@tagName(t)),
},
//.divide,
},
.type = binary_operation_type,
}); });
const instruction = try builder.append(.{ const instruction = try builder.append(.{

View File

@ -40,18 +40,19 @@ pub const Logger = enum {
pub var bitset = std.EnumSet(Logger).initMany(&.{ pub var bitset = std.EnumSet(Logger).initMany(&.{
.instruction_selection_ir_function, .instruction_selection_ir_function,
.instruction_selection_mir_function, .instruction_selection_mir_function,
// .instruction_selection_register_operand_list, .instruction_selection_register_operand_list,
// .register_allocation_block, .instruction_selection_new_instruction,
// .register_allocation_problematic_hint, .register_allocation_block,
// .register_allocation_assignment, .register_allocation_problematic_hint,
// .register_allocation_reload, .register_allocation_assignment,
// .register_allocation_function_before, .register_allocation_reload,
// .register_allocation_new_instruction, .register_allocation_function_before,
// .register_allocation_new_instruction_function_before, .register_allocation_new_instruction,
// .register_allocation_instruction_avoid_copy, .register_allocation_new_instruction_function_before,
.register_allocation_instruction_avoid_copy,
.register_allocation_function_after, .register_allocation_function_after,
// .register_allocation_operand_list_verification, .register_allocation_operand_list_verification,
// .encoding, .encoding,
}); });
}; };
@ -91,6 +92,7 @@ const Register = struct {
gp32, gp32,
gp64, gp64,
gp64_nosp, gp64_nosp,
ccr,
pub const Descriptor = struct { pub const Descriptor = struct {
size: u16, size: u16,
@ -817,6 +819,11 @@ const register_class_descriptors = std.EnumArray(Register.Class, Register.Class.
.spill_size = 64, .spill_size = 64,
.spill_alignment = 64, .spill_alignment = 64,
}, },
.ccr = .{
.size = 32,
.spill_size = 32,
.spill_alignment = 32,
},
}); });
const registers_by_class = RegisterGroupMap.init(.{ const registers_by_class = RegisterGroupMap.init(.{
@ -858,6 +865,9 @@ const registers_by_class = RegisterGroupMap.init(.{
.rbp, .rbp,
.rsp, .rsp,
}, },
.ccr = &.{
.eflags,
},
.gp64_nosp = &.{}, .gp64_nosp = &.{},
}); });
@ -874,6 +884,7 @@ const system_v = CallingConvention{
.gp32 = &system_v_gp32_argument_registers, .gp32 = &system_v_gp32_argument_registers,
.gp64 = &system_v_gp64_argument_registers, .gp64 = &system_v_gp64_argument_registers,
.gp64_nosp = &.{}, .gp64_nosp = &.{},
.ccr = &.{},
}), }),
.syscall_registers = &system_v_syscall_registers, .syscall_registers = &system_v_syscall_registers,
}; };
@ -911,6 +922,7 @@ const ValueType = struct {
i32 = 5, i32 = 5,
i64 = 6, i64 = 6,
// i128 = 7, // i128 = 7,
ccr = 15,
}; };
}; };
@ -936,12 +948,20 @@ const value_types = std.EnumArray(ValueType.Id, ValueType).init(.{
.data_type = .integer, .data_type = .integer,
.scalarness = .scalar, .scalarness = .scalar,
}, },
.ccr = .{
.size = @sizeOf(u32),
.element_count = 1,
.element_type = @intFromEnum(ValueType.Id.i32),
.data_type = .integer,
.scalarness = .scalar,
},
}); });
const register_classes = std.EnumArray(ValueType.Id, Register.Class).init(.{ const register_classes = std.EnumArray(ValueType.Id, Register.Class).init(.{
.any = .any, .any = .any,
.i32 = .gp32, .i32 = .gp32,
.i64 = .gp64, .i64 = .gp64,
.ccr = .ccr,
}); });
const Memory = struct { const Memory = struct {
@ -1295,6 +1315,7 @@ const InstructionSelection = struct {
if (!gop.value_ptr.isValid()) { if (!gop.value_ptr.isValid()) {
gop.value_ptr.* = register; gop.value_ptr.* = register;
} else if (!std.meta.eql(gop.value_ptr.index, register.index)) { } else if (!std.meta.eql(gop.value_ptr.index, register.index)) {
logln(.codegen, .instruction_selection_new_instruction, "Already found register: {}\ngot: {}", .{ gop.value_ptr.index, register.index });
unreachable; unreachable;
} }
} }
@ -1488,9 +1509,13 @@ const Instruction = struct {
add32rm, add32rm,
add32mr, add32mr,
and32rm, and32rm,
and32mr,
and32rr, and32rr,
call64pcrel32, call64pcrel32,
copy, copy,
idiv32r,
idiv32m,
imul32mr,
imul32rm, imul32rm,
imul32rr, imul32rr,
lea64r, lea64r,
@ -1506,12 +1531,15 @@ const Instruction = struct {
movsx64rm32, movsx64rm32,
movsx64rr32, movsx64rr32,
or32rm, or32rm,
or32mr,
or32rr, or32rr,
ret, ret,
sub32mr,
sub32rr, sub32rr,
sub32rm, sub32rm,
syscall, syscall,
ud2, ud2,
xor32mr,
xor32rr, xor32rr,
xor32rm, xor32rm,
}; };
@ -1521,6 +1549,8 @@ const Instruction = struct {
opcode: u16, opcode: u16,
// format: Format = .pseudo, // format: Format = .pseudo,
flags: Flags = .{}, flags: Flags = .{},
implicit_definitions: []const Register.Physical = &.{},
implicit_uses: []const Register.Physical = &.{},
const Flags = packed struct { const Flags = packed struct {
implicit_def: bool = false, implicit_def: bool = false,
@ -1711,6 +1741,7 @@ pub const Operand = struct {
imm64, imm64,
i64i32imm_brtarget, i64i32imm_brtarget,
lea64mem, lea64mem,
ccr,
}; };
pub const Type = enum(u1) { pub const Type = enum(u1) {
use = 0, use = 0,
@ -1803,6 +1834,7 @@ const register_class_operand_matcher = std.EnumArray(Operand.Id, Register.Class)
.imm32 = .not_a_register, .imm32 = .not_a_register,
.imm64 = .not_a_register, .imm64 = .not_a_register,
.lea64mem = .not_a_register, .lea64mem = .not_a_register,
.ccr = .ccr,
}); });
const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descriptor).init(.{ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descriptor).init(.{
@ -1823,6 +1855,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src, .kind = .src,
}, },
}, },
.implicit_definitions = &.{.eflags},
}, },
.add32mr = .{ .add32mr = .{
// .format = .mrm_dest_reg, // right? // .format = .mrm_dest_reg, // right?
@ -1841,6 +1874,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src, .kind = .src,
}, },
}, },
.implicit_definitions = &.{.eflags},
}, },
.add32rm = .{ .add32rm = .{
// .format = .mrm_dest_reg, // right? // .format = .mrm_dest_reg, // right?
@ -1859,6 +1893,26 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src, .kind = .src,
}, },
}, },
.implicit_definitions = &.{.eflags},
},
.and32mr = .{
// .format = .mrm_dest_reg, // right?
.opcode = 0x21,
.operands = &.{
.{
.id = .i32mem,
.kind = .dst,
},
.{
.id = .i32mem,
.kind = .src,
},
.{
.id = .gp32,
.kind = .src,
},
},
.implicit_definitions = &.{.eflags},
}, },
.and32rr = .{ .and32rr = .{
// .format = .mrm_dest_reg, // right? // .format = .mrm_dest_reg, // right?
@ -1877,6 +1931,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src, .kind = .src,
}, },
}, },
.implicit_definitions = &.{.eflags},
}, },
.and32rm = .{ .and32rm = .{
// .format = .mrm_dest_reg, // right? // .format = .mrm_dest_reg, // right?
@ -1895,6 +1950,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src, .kind = .src,
}, },
}, },
.implicit_definitions = &.{.eflags},
}, },
.call64pcrel32 = .{ .call64pcrel32 = .{
// .format = .no_operands, // .format = .no_operands,
@ -1905,6 +1961,8 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src, .kind = .src,
}, },
}, },
.implicit_definitions = &.{},
.implicit_uses = &.{},
}, },
.copy = .{ .copy = .{
// .format = .pseudo, // .format = .pseudo,
@ -1920,6 +1978,51 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
}, },
}, },
}, },
.idiv32r = .{
.opcode = 0xf7,
.operands = &.{
.{
.id = .gp32,
.kind = .src,
},
},
},
.idiv32m = .{
.opcode = 0xf7,
.operands = &.{
.{
.id = .i32mem,
.kind = .src,
},
},
.implicit_definitions = &.{
.eax,
.edx,
.eflags,
},
.implicit_uses = &.{
.eax,
.edx,
},
},
.imul32mr = .{
.opcode = 0x6b,
.operands = &.{
.{
.id = .i32mem,
.kind = .dst,
},
.{
.id = .i32mem,
.kind = .src,
},
.{
.id = .gp32,
.kind = .src,
},
},
.implicit_definitions = &.{.eflags},
},
.imul32rr = .{ .imul32rr = .{
.opcode = 0x6b, .opcode = 0x6b,
.operands = &.{ .operands = &.{
@ -1936,6 +2039,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src, .kind = .src,
}, },
}, },
.implicit_definitions = &.{.eflags},
}, },
.imul32rm = .{ .imul32rm = .{
.opcode = 0x6b, .opcode = 0x6b,
@ -1953,6 +2057,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src, .kind = .src,
}, },
}, },
.implicit_definitions = &.{.eflags},
}, },
.lea64r = .{ .lea64r = .{
// .format = .mrm_source_mem, // .format = .mrm_source_mem,
@ -2134,6 +2239,26 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src, .kind = .src,
}, },
}, },
.implicit_definitions = &.{.eflags},
},
.or32mr = .{
// .format = .mrm_dest_reg,
.opcode = 0x09,
.operands = &.{
.{
.id = .i32mem,
.kind = .dst,
},
.{
.id = .i32mem,
.kind = .src,
},
.{
.id = .gp32,
.kind = .src,
},
},
.implicit_definitions = &.{.eflags},
}, },
.or32rr = .{ .or32rr = .{
// .format = .mrm_dest_reg, // .format = .mrm_dest_reg,
@ -2152,6 +2277,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src, .kind = .src,
}, },
}, },
.implicit_definitions = &.{.eflags},
}, },
.ret = .{ .ret = .{
// .format = .no_operands, // .format = .no_operands,
@ -2163,6 +2289,25 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
}, },
}, },
}, },
.sub32mr = .{
// .format = .mrm_dest_reg, // right?
.opcode = 0x29,
.operands = &.{
.{
.id = .i32mem,
.kind = .dst,
},
.{
.id = .i32mem,
.kind = .src,
},
.{
.id = .gp32,
.kind = .src,
},
},
.implicit_definitions = &.{.eflags},
},
.sub32rr = .{ .sub32rr = .{
// .format = .mrm_dest_reg, // right? // .format = .mrm_dest_reg, // right?
.opcode = 0x29, .opcode = 0x29,
@ -2180,6 +2325,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src, .kind = .src,
}, },
}, },
.implicit_definitions = &.{.eflags},
}, },
.sub32rm = .{ .sub32rm = .{
// .format = .mrm_dest_reg, // right? // .format = .mrm_dest_reg, // right?
@ -2198,6 +2344,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src, .kind = .src,
}, },
}, },
.implicit_definitions = &.{.eflags},
}, },
.syscall = .{ .syscall = .{
// .format = .no_operands, // .format = .no_operands,
@ -2232,6 +2379,26 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src, .kind = .src,
}, },
}, },
.implicit_definitions = &.{.eflags},
},
.xor32mr = .{
// .format = .mrm_dest_reg,
.opcode = 0x31,
.operands = &.{
.{
.id = .i32mem,
.kind = .dst,
},
.{
.id = .i32mem,
.kind = .src,
},
.{
.id = .gp32,
.kind = .src,
},
},
.implicit_definitions = &.{.eflags},
}, },
.xor32rr = .{ .xor32rr = .{
// .format = .mrm_dest_reg, // .format = .mrm_dest_reg,
@ -2250,6 +2417,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src, .kind = .src,
}, },
}, },
.implicit_definitions = &.{.eflags},
}, },
}); });
@ -2715,7 +2883,7 @@ pub const MIR = struct {
const source_immediate = ir_source.u.load_integer; const source_immediate = ir_source.u.load_integer;
const instruction_descriptor = instruction_descriptors.getPtrConst(instruction_id); const instruction_descriptor = instruction_descriptors.getPtrConst(instruction_id);
const source_operand_index = instruction_descriptor.operands.len - 1; const source_operand_index = 1;
const source_operand_id = instruction_descriptor.operands[source_operand_index].id; const source_operand_id = instruction_descriptor.operands[source_operand_index].id;
const source_operand = Operand{ const source_operand = Operand{
.id = source_operand_id, .id = source_operand_id,
@ -2753,7 +2921,7 @@ pub const MIR = struct {
}; };
const instruction_descriptor = instruction_descriptors.getPtrConst(instruction_id); const instruction_descriptor = instruction_descriptors.getPtrConst(instruction_id);
const source_operand_index = instruction_descriptor.operands.len - 1; const source_operand_index = 1;
const source_operand_id = instruction_descriptor.operands[source_operand_index].id; const source_operand_id = instruction_descriptor.operands[source_operand_index].id;
const source_operand = Operand{ const source_operand = Operand{
.id = source_operand_id, .id = source_operand_id,
@ -2942,30 +3110,227 @@ pub const MIR = struct {
}, },
.binary_operation => |ir_binary_operation_index| { .binary_operation => |ir_binary_operation_index| {
const ir_binary_operation = mir.ir.binary_operations.get(ir_binary_operation_index); const ir_binary_operation = mir.ir.binary_operations.get(ir_binary_operation_index);
const value_type = resolveType(ir_binary_operation.type);
const is_left_load = switch (mir.ir.instructions.get(ir_binary_operation.left).u) {
.load => true,
else => false,
};
const is_right_load = switch (mir.ir.instructions.get(ir_binary_operation.right).u) { const is_right_load = switch (mir.ir.instructions.get(ir_binary_operation.right).u) {
.load => true, .load => true,
else => false, else => false,
}; };
const is_left_load = switch (mir.ir.instructions.get(ir_binary_operation.right).u) { switch (ir_binary_operation.id) {
.load => true, .signed_divide => {
else => false, const operand_id: Operand.Id = switch (value_type) {
.i32 => .gp32,
else => unreachable,
}; };
const left_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.left);
const low_physical_register: Register.Physical = switch (value_type) {
.i32 => .eax,
else => unreachable,
};
const high_physical_register: Register.Physical = switch (value_type) {
.i32 => .edx,
else => unreachable,
};
const left_operand = Operand{
.id = operand_id,
.u = .{
.register = left_register,
},
.flags = .{},
};
const low_operand = Operand{
.id = operand_id,
.u = .{
.register = .{
.index = .{
.physical = low_physical_register,
},
},
},
.flags = .{
.type = .def,
},
};
const copy_low = try mir.buildInstruction(instruction_selection, .copy, &.{
low_operand,
left_operand,
});
try instruction_selection.instruction_cache.append(mir.allocator, copy_low);
if (is_right_load) {
try instruction_selection.folded_loads.putNoClobber(mir.allocator, ir_binary_operation.right, {});
const instruction_id: Instruction.Id = switch (value_type) {
.i32 => .idiv32m,
else => |t| @panic(@tagName(t)),
};
const instruction_descriptor = instruction_descriptors.get(instruction_id);
const right_operand_id = instruction_descriptor.operands[0].id;
const ir_load = mir.ir.loads.get(mir.ir.instructions.get(ir_binary_operation.right).u.load);
const right_operand_addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_load.instruction);
const right_operand = Operand{
.id = right_operand_id,
.u = .{
.memory = .{
.addressing_mode = right_operand_addressing_mode,
},
},
.flags = .{},
};
const div = try mir.buildInstruction(instruction_selection, instruction_id, &.{
right_operand,
});
try instruction_selection.instruction_cache.append(mir.allocator, div);
} else {
const instruction_id: Instruction.Id = switch (value_type) {
.i32 => .idiv32r,
else => |t| @panic(@tagName(t)),
};
const instruction_descriptor = instruction_descriptors.get(instruction_id);
_ = instruction_descriptor;
const right_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.right);
_ = high_physical_register;
const right_operand = Operand{
.id = operand_id,
.u = .{
.register = right_register,
},
.flags = .{},
};
const div = try mir.buildInstruction(instruction_selection, instruction_id, &.{
right_operand,
});
try instruction_selection.instruction_cache.append(mir.allocator, div);
}
const register_class = register_classes.get(value_type);
_ = register_class;
const result_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index);
const result_operand = Operand{
.id = operand_id,
.u = .{
.register = result_register,
},
.flags = .{
.type = .def,
},
};
const division_result_register = Register{
.index = .{
.physical = switch (value_type) {
.i32 => .eax,
else => unreachable,
},
},
};
const division_result_operand = Operand{
.id = operand_id,
.u = .{
.register = division_result_register,
},
.flags = .{},
};
const result_copy = try mir.buildInstruction(instruction_selection, .copy, &.{
result_operand,
division_result_operand,
});
try instruction_selection.instruction_cache.append(mir.allocator, result_copy);
try instruction_selection.updateValueMap(mir.allocator, ir_instruction_index, result_register, false);
},
else => {
// TODO: optimize if the result is going to be stored? // TODO: optimize if the result is going to be stored?
// for (ir_instruction.use_list.items) |use_index| { // for (ir_instruction.use_list.items) |use_index| {
// const use = mir.ir.instructions.get(use_index); // const use = mir.ir.instructions.get(use_index);
// std.debug.print("Use: {s}\n", .{@tagName(use.u)}); // std.debug.print("Use: {s}\n", .{@tagName(use.u)});
// } // }
const destination_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index);
const value_type = resolveType(ir_binary_operation.type);
if (!is_left_load and is_right_load) { if (!is_left_load and is_right_load) {
unreachable; unreachable;
} else if (is_left_load and !is_right_load) { } else if (is_left_load and !is_right_load) {
unreachable; try instruction_selection.folded_loads.putNoClobber(mir.allocator, ir_binary_operation.left, {});
const instruction_id: Instruction.Id = switch (ir_binary_operation.id) {
.add => switch (value_type) {
.i32 => .add32mr,
else => unreachable,
},
.sub => switch (value_type) {
.i32 => .sub32mr,
else => unreachable,
},
.logical_and => switch (value_type) {
.i32 => .and32mr,
else => unreachable,
},
.logical_xor => switch (value_type) {
.i32 => .xor32mr,
else => unreachable,
},
.logical_or => switch (value_type) {
.i32 => .or32mr,
else => unreachable,
},
.signed_multiply => switch (value_type) {
.i32 => .imul32mr,
else => unreachable,
},
.signed_divide => unreachable,
};
const instruction_descriptor = instruction_descriptors.get(instruction_id);
// const left_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.left);
const destination_operand_id = instruction_descriptor.operands[0].id;
const left_operand_id = instruction_descriptor.operands[1].id;
const right_operand_id = instruction_descriptor.operands[2].id;
// const ir_load = mir.ir.loads.get(mir.ir.instructions.get(ir_binary_operation.right).u.load);
// const right_operand_addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_load.instruction);
const ir_load = mir.ir.loads.get(mir.ir.instructions.get(ir_binary_operation.left).u.load);
const right_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.right);
const right_operand = Operand{
.id = right_operand_id,
.u = .{
.register = right_register,
},
.flags = .{},
};
const left_operand_addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_load.instruction);
const destination_operand = Operand{
.id = destination_operand_id,
.u = .{
.memory = .{ .addressing_mode = left_operand_addressing_mode },
},
.flags = .{},
};
const left_operand = Operand{
.id = left_operand_id,
.u = .{
.memory = .{ .addressing_mode = left_operand_addressing_mode },
},
.flags = .{},
};
const binary_op_instruction = try mir.buildInstruction(instruction_selection, instruction_id, &.{
destination_operand,
left_operand,
right_operand,
});
try instruction_selection.instruction_cache.append(mir.allocator, binary_op_instruction);
} else if (!is_left_load and !is_right_load) { } else if (!is_left_load and !is_right_load) {
const destination_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index);
const instruction_id: Instruction.Id = switch (ir_binary_operation.id) { const instruction_id: Instruction.Id = switch (ir_binary_operation.id) {
.add => switch (value_type) { .add => switch (value_type) {
.i32 => .add32rr, .i32 => .add32rr,
@ -2987,10 +3352,11 @@ pub const MIR = struct {
.i32 => .or32rr, .i32 => .or32rr,
else => unreachable, else => unreachable,
}, },
.multiply => switch (value_type) { .signed_multiply => switch (value_type) {
.i32 => .imul32rr, .i32 => .imul32rr,
else => unreachable, else => unreachable,
}, },
.signed_divide => unreachable,
}; };
const instruction_descriptor = instruction_descriptors.get(instruction_id); const instruction_descriptor = instruction_descriptors.get(instruction_id);
@ -3026,6 +3392,7 @@ pub const MIR = struct {
}, },
.flags = .{}, .flags = .{},
}; };
const binary_op_instruction = try mir.buildInstruction(instruction_selection, instruction_id, &.{ const binary_op_instruction = try mir.buildInstruction(instruction_selection, instruction_id, &.{
destination_operand, destination_operand,
left_operand, left_operand,
@ -3036,6 +3403,7 @@ pub const MIR = struct {
try instruction_selection.updateValueMap(mir.allocator, ir_instruction_index, destination_register, false); try instruction_selection.updateValueMap(mir.allocator, ir_instruction_index, destination_register, false);
} else { } else {
const destination_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index);
// If both operands come from memory (both operands are loads), load the left one into a register and operate from the stack with the right one, when possible // If both operands come from memory (both operands are loads), load the left one into a register and operate from the stack with the right one, when possible
const instruction_id: Instruction.Id = switch (ir_binary_operation.id) { const instruction_id: Instruction.Id = switch (ir_binary_operation.id) {
.add => switch (value_type) { .add => switch (value_type) {
@ -3058,10 +3426,11 @@ pub const MIR = struct {
.i32 => .or32rm, .i32 => .or32rm,
else => unreachable, else => unreachable,
}, },
.multiply => switch (value_type) { .signed_multiply => switch (value_type) {
.i32 => .imul32rm, .i32 => .imul32rm,
else => unreachable, else => unreachable,
}, },
.signed_divide => unreachable,
}; };
try instruction_selection.folded_loads.putNoClobber(mir.allocator, ir_binary_operation.right, {}); try instruction_selection.folded_loads.putNoClobber(mir.allocator, ir_binary_operation.right, {});
@ -3111,6 +3480,8 @@ pub const MIR = struct {
try instruction_selection.updateValueMap(mir.allocator, ir_instruction_index, destination_register, false); try instruction_selection.updateValueMap(mir.allocator, ir_instruction_index, destination_register, false);
} }
}, },
}
},
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
} }
@ -3762,20 +4133,21 @@ pub const MIR = struct {
const instruction = mir.instructions.get(instruction_index); const instruction = mir.instructions.get(instruction_index);
const operand = mir.operands.get(operand_index); const operand = mir.operands.get(operand_index);
const gop = try register_allocator.live_virtual_registers.getOrPut(mir.allocator, virtual_register); const gop = try register_allocator.live_virtual_registers.getOrPut(mir.allocator, virtual_register);
const live_register = gop.value_ptr;
if (!gop.found_existing) { if (!gop.found_existing) {
gop.value_ptr.* = .{ gop.value_ptr.* = .{
.virtual = virtual_register, .virtual = virtual_register,
}; };
if (!operand.flags.dead_or_kill) { if (!operand.flags.dead_or_kill) {
var live_out = false; if (live_register.live_out) {
if (live_out) {
// TODO // TODO
unreachable;
} else { } else {
operand.flags.dead_or_kill = true; operand.flags.dead_or_kill = true;
} }
} }
} }
const live_register = gop.value_ptr;
if (live_register.physical == .no_register) { if (live_register.physical == .no_register) {
try register_allocator.allocateVirtualRegister(mir, instruction_selection, instruction_index, live_register, null, look_at_physical_register_uses); try register_allocator.allocateVirtualRegister(mir, instruction_selection, instruction_index, live_register, null, look_at_physical_register_uses);
} else { } else {
@ -3785,6 +4157,7 @@ pub const MIR = struct {
const physical_register = live_register.physical; const physical_register = live_register.physical;
assert(physical_register != .no_register); assert(physical_register != .no_register);
if (live_register.reloaded or live_register.live_out) { if (live_register.reloaded or live_register.live_out) {
logln(.codegen, .register_allocation_problematic_hint, "Live register: {}", .{live_register});
const instruction_descriptor = instruction_descriptors.get(instruction.id); const instruction_descriptor = instruction_descriptors.get(instruction.id);
if (!instruction_descriptor.flags.implicit_def) { if (!instruction_descriptor.flags.implicit_def) {
const spill_before = mir.getNextInstructionIndex(instruction_index); const spill_before = mir.getNextInstructionIndex(instruction_index);
@ -3987,8 +4360,7 @@ pub const MIR = struct {
var early_clobber = false; var early_clobber = false;
var assign_live_throughs = false; var assign_live_throughs = false;
for (instruction.operands.items, 0..) |operand_index, operand_i| { for (instruction.operands.items) |operand_index| {
_ = operand_i;
const operand = mir.operands.get(operand_index); const operand = mir.operands.get(operand_index);
switch (operand.u) { switch (operand.u) {
.register => |register| switch (register.index) { .register => |register| switch (register.index) {
@ -4183,7 +4555,7 @@ pub const MIR = struct {
continue; continue;
} }
mir.verifyUseList(vr.use_def_list_head, vr_index); // mir.verifyUseList(vr.use_def_list_head, vr_index);
vr_index = vr_it.getCurrentIndex(); vr_index = vr_it.getCurrentIndex();
} }
@ -4296,7 +4668,6 @@ pub const MIR = struct {
switch (instruction.id) { switch (instruction.id) {
.mov32r0 => { .mov32r0 => {
assert(instruction.operands.items.len == 1);
const operand = mir.operands.get(instruction.operands.items[0]); const operand = mir.operands.get(instruction.operands.items[0]);
const gp_register_encoding = getGP32Encoding(operand.*); const gp_register_encoding = getGP32Encoding(operand.*);
const new_instruction_id = Instruction.Id.xor32rr; const new_instruction_id = Instruction.Id.xor32rr;
@ -4313,7 +4684,6 @@ pub const MIR = struct {
}, },
.ret => {}, .ret => {},
.mov32mr => { .mov32mr => {
assert(instruction.operands.items.len == 2);
const source_operand = mir.operands.get(instruction.operands.items[1]); const source_operand = mir.operands.get(instruction.operands.items[1]);
const source_gp32 = getGP32Encoding(source_operand.*); const source_gp32 = getGP32Encoding(source_operand.*);
@ -4343,8 +4713,6 @@ pub const MIR = struct {
} }
}, },
.mov64mr => { .mov64mr => {
assert(instruction.operands.items.len == 2);
const rex = Rex{ const rex = Rex{
.b = false, .b = false,
.x = false, .x = false,
@ -4382,8 +4750,6 @@ pub const MIR = struct {
} }
}, },
.mov32rm => { .mov32rm => {
assert(instruction.operands.items.len == 2);
const instruction_descriptor = instruction_descriptors.get(instruction.id); const instruction_descriptor = instruction_descriptors.get(instruction.id);
const opcode: u8 = @intCast(instruction_descriptor.opcode); const opcode: u8 = @intCast(instruction_descriptor.opcode);
try image.section_manager.appendCodeByte(opcode); try image.section_manager.appendCodeByte(opcode);
@ -4414,8 +4780,6 @@ pub const MIR = struct {
} }
}, },
.mov64rm => { .mov64rm => {
assert(instruction.operands.items.len == 2);
const rex = Rex{ const rex = Rex{
.b = false, .b = false,
.x = false, .x = false,
@ -4454,8 +4818,6 @@ pub const MIR = struct {
} }
}, },
.mov32ri => { .mov32ri => {
assert(instruction.operands.items.len == 2);
const source_operand = mir.operands.get(instruction.operands.items[1]); const source_operand = mir.operands.get(instruction.operands.items[1]);
const source_immediate: u32 = @intCast(source_operand.u.immediate); const source_immediate: u32 = @intCast(source_operand.u.immediate);
@ -4468,7 +4830,6 @@ pub const MIR = struct {
try image.section_manager.appendCode(std.mem.asBytes(&source_immediate)); try image.section_manager.appendCode(std.mem.asBytes(&source_immediate));
}, },
.mov32ri64 => { .mov32ri64 => {
assert(instruction.operands.items.len == 2);
const source_operand = mir.operands.get(instruction.operands.items[1]); const source_operand = mir.operands.get(instruction.operands.items[1]);
const source_immediate: u32 = @intCast(source_operand.u.immediate); const source_immediate: u32 = @intCast(source_operand.u.immediate);
@ -4484,8 +4845,6 @@ pub const MIR = struct {
try image.section_manager.appendCode(std.mem.asBytes(&source_immediate)); try image.section_manager.appendCode(std.mem.asBytes(&source_immediate));
}, },
.movsx64rm32 => { .movsx64rm32 => {
assert(instruction.operands.items.len == 2);
const destination_operand = mir.operands.get(instruction.operands.items[0]); const destination_operand = mir.operands.get(instruction.operands.items[0]);
const destination_register = getGP64Encoding(destination_operand.*); const destination_register = getGP64Encoding(destination_operand.*);
@ -4526,7 +4885,6 @@ pub const MIR = struct {
.ud2 => try image.section_manager.appendCode(&.{ 0x0f, 0x0b }), .ud2 => try image.section_manager.appendCode(&.{ 0x0f, 0x0b }),
.call64pcrel32 => { .call64pcrel32 => {
// TODO: emit relocation // TODO: emit relocation
assert(instruction.operands.items.len == 1);
const operand = mir.operands.get(instruction.operands.items[0]); const operand = mir.operands.get(instruction.operands.items[0]);
const instruction_descriptor = instruction_descriptors.get(instruction.id); const instruction_descriptor = instruction_descriptors.get(instruction.id);
const opcode: u8 = @intCast(instruction_descriptor.opcode); const opcode: u8 = @intCast(instruction_descriptor.opcode);
@ -4555,7 +4913,6 @@ pub const MIR = struct {
} }
}, },
.copy => { .copy => {
assert(instruction.operands.items.len == 2);
const destination_operand = mir.operands.get(instruction.operands.items[0]); const destination_operand = mir.operands.get(instruction.operands.items[0]);
const source_operand = mir.operands.get(instruction.operands.items[1]); const source_operand = mir.operands.get(instruction.operands.items[1]);
assert(destination_operand.id == source_operand.id); assert(destination_operand.id == source_operand.id);
@ -4598,7 +4955,6 @@ pub const MIR = struct {
} }
}, },
.lea64r => { .lea64r => {
assert(instruction.operands.items.len == 2);
const rex = Rex{ const rex = Rex{
.b = false, .b = false,
.x = false, .x = false,
@ -4652,7 +5008,6 @@ pub const MIR = struct {
.sub32rr, .sub32rr,
.imul32rr, .imul32rr,
=> { => {
assert(instruction.operands.items.len == 3);
const instruction_descriptor = instruction_descriptors.get(instruction.id); const instruction_descriptor = instruction_descriptors.get(instruction.id);
const opcode: u8 = @intCast(instruction_descriptor.opcode); const opcode: u8 = @intCast(instruction_descriptor.opcode);
try image.section_manager.appendCodeByte(opcode); try image.section_manager.appendCodeByte(opcode);
@ -4679,8 +5034,6 @@ pub const MIR = struct {
try image.section_manager.appendCodeByte(@bitCast(modrm)); try image.section_manager.appendCodeByte(@bitCast(modrm));
}, },
.mov32mi => { .mov32mi => {
assert(instruction.operands.items.len == 2);
const instruction_descriptor = instruction_descriptors.get(instruction.id); const instruction_descriptor = instruction_descriptors.get(instruction.id);
const opcode: u8 = @intCast(instruction_descriptor.opcode); const opcode: u8 = @intCast(instruction_descriptor.opcode);
try image.section_manager.appendCodeByte(opcode); try image.section_manager.appendCodeByte(opcode);
@ -4711,6 +5064,31 @@ pub const MIR = struct {
try image.section_manager.appendCode(std.mem.asBytes(&source_immediate)); try image.section_manager.appendCode(std.mem.asBytes(&source_immediate));
}, },
.idiv32m => {
const instruction_descriptor = instruction_descriptors.get(instruction.id);
const opcode = @as(u8, @intCast(instruction_descriptor.opcode));
try image.section_manager.appendCodeByte(opcode);
const destination_operand_index = instruction.operands.items[0];
const destination_operand = mir.operands.get(destination_operand_index);
switch (destination_operand.u.memory.addressing_mode.base) {
.register_base => unreachable,
.frame_index => |frame_index| {
const modrm = ModRm{
.rm = @intFromEnum(Encoding.GP64.bp),
.reg = 7,
.mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true),
};
try image.section_manager.appendCodeByte(@bitCast(modrm));
const stack_offset = computeStackOffset(function.instruction_selection.stack_objects.items[0 .. frame_index + 1]);
const displacement_bytes: u3 = if (std.math.cast(i8, stack_offset)) |_| @sizeOf(i8) else if (std.math.cast(i32, stack_offset)) |_| @sizeOf(i32) else unreachable;
const stack_bytes = std.mem.asBytes(&stack_offset)[0..displacement_bytes];
try image.section_manager.appendCode(stack_bytes);
},
}
},
.add32rm, .add32rm,
.sub32rm, .sub32rm,
.and32rm, .and32rm,
@ -4718,7 +5096,6 @@ pub const MIR = struct {
.or32rm, .or32rm,
.imul32rm, .imul32rm,
=> { => {
assert(instruction.operands.items.len == 3);
const instruction_descriptor = instruction_descriptors.get(instruction.id); const instruction_descriptor = instruction_descriptors.get(instruction.id);
const opcode: u8 = @intCast(instruction_descriptor.opcode); const opcode: u8 = @intCast(instruction_descriptor.opcode);
try image.section_manager.appendCodeByte(opcode); try image.section_manager.appendCodeByte(opcode);
@ -4756,6 +5133,43 @@ pub const MIR = struct {
}, },
} }
}, },
.sub32mr => {
const instruction_descriptor = instruction_descriptors.get(instruction.id);
const opcode: u8 = @intCast(instruction_descriptor.opcode);
try image.section_manager.appendCodeByte(opcode);
const destination_operand_index = instruction.operands.items[0];
const left_operand_index = instruction.operands.items[1];
const right_operand_index = instruction.operands.items[2];
const destination_operand = mir.operands.get(destination_operand_index);
const left_operand = mir.operands.get(left_operand_index);
const right_operand = mir.operands.get(right_operand_index);
const right_register = getGP32Encoding(right_operand.*);
assert(left_operand.id == .i32mem);
assert(destination_operand.id == .i32mem);
const left_memory = left_operand.u.memory;
switch (left_memory.addressing_mode.base) {
.register_base => unreachable,
.frame_index => |frame_index| {
const modrm = ModRm{
.rm = @intFromEnum(Encoding.GP64.bp),
.reg = @intCast(@intFromEnum(right_register)),
.mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true),
};
try image.section_manager.appendCodeByte(@bitCast(modrm));
const stack_offset = computeStackOffset(function.instruction_selection.stack_objects.items[0 .. frame_index + 1]);
const displacement_bytes: u3 = if (std.math.cast(i8, stack_offset)) |_| @sizeOf(i8) else if (std.math.cast(i32, stack_offset)) |_| @sizeOf(i32) else unreachable;
const stack_bytes = std.mem.asBytes(&stack_offset)[0..displacement_bytes];
try image.section_manager.appendCode(stack_bytes);
},
}
},
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
} }
@ -4872,9 +5286,9 @@ pub const MIR = struct {
fn buildInstruction(mir: *MIR, instruction_selection: *InstructionSelection, instruction: Instruction.Id, operands: []const Operand) !Instruction.Index { fn buildInstruction(mir: *MIR, instruction_selection: *InstructionSelection, instruction: Instruction.Id, operands: []const Operand) !Instruction.Index {
// Some sanity check // Some sanity check
const descriptor = instruction_descriptors.getPtrConst(instruction);
{ {
if (instruction != .copy) { if (instruction != .copy) {
const descriptor = instruction_descriptors.getPtrConst(instruction);
if (descriptor.operands.len == operands.len) { if (descriptor.operands.len == operands.len) {
for (descriptor.operands, operands) |descriptor_operand, operand| { for (descriptor.operands, operands) |descriptor_operand, operand| {
switch (descriptor_operand.id) { switch (descriptor_operand.id) {
@ -4886,13 +5300,13 @@ pub const MIR = struct {
switch (instruction) { switch (instruction) {
.ret => {}, .ret => {},
.syscall => {}, .syscall => {},
else => unreachable, else => |t| @panic(@tagName(t)),
} }
} }
} }
} }
var list = try ArrayList(Operand.Index).initCapacity(mir.allocator, operands.len); var list = try ArrayList(Operand.Index).initCapacity(mir.allocator, operands.len + descriptor.implicit_definitions.len + descriptor.implicit_uses.len);
const instruction_allocation = try mir.instructions.addOne(mir.allocator); const instruction_allocation = try mir.instructions.addOne(mir.allocator);
// TODO: MachineRegisterInfo::addRegOperandToUseList // TODO: MachineRegisterInfo::addRegOperandToUseList
for (operands) |operand_value| { for (operands) |operand_value| {
@ -4916,18 +5330,63 @@ pub const MIR = struct {
} }
} }
for (descriptor.implicit_definitions) |implicitly_defined_register| {
const operand_allocation = try mir.operands.append(mir.allocator, Operand{
.id = switch (implicitly_defined_register) {
.eflags => .ccr,
.eax => .gp32,
.edx => .gp32,
else => |t| @panic(@tagName(t)),
},
.u = .{
.register = .{
.index = .{
.physical = implicitly_defined_register,
},
},
},
.flags = .{
.type = .def,
.implicit = true,
},
});
list.appendAssumeCapacity(operand_allocation.index);
const operand_index = operand_allocation.index;
operand_allocation.ptr.parent = instruction_allocation.index;
mir.addRegisterOperandFromUseList(instruction_selection, operand_index);
}
for (descriptor.implicit_uses) |implicitly_used_register| {
const operand_allocation = try mir.operands.append(mir.allocator, Operand{
.id = switch (implicitly_used_register) {
.eax => .gp32,
.edx => .gp32,
else => |t| @panic(@tagName(t)),
},
.u = .{
.register = .{
.index = .{
.physical = implicitly_used_register,
},
},
},
.flags = .{
.type = .use,
.implicit = true,
},
});
list.appendAssumeCapacity(operand_allocation.index);
const operand_index = operand_allocation.index;
operand_allocation.ptr.parent = instruction_allocation.index;
mir.addRegisterOperandFromUseList(instruction_selection, operand_index);
}
instruction_allocation.ptr.* = .{ instruction_allocation.ptr.* = .{
.id = instruction, .id = instruction,
.operands = list, .operands = list,
.parent = instruction_selection.current_block, .parent = instruction_selection.current_block,
}; };
if (instruction == .copy) {
const i = instruction_allocation.ptr.*;
_ = i;
// print("Built copy: DST: {}. SRC: {}", .{ mir.operands.get(i.operands.items[0]).u.register.index, mir.operands.get(i.operands.items[1]).u.register.index });
}
return instruction_allocation.index; return instruction_allocation.index;
} }

View File

@ -544,6 +544,7 @@ const Analyzer = struct {
.logical_xor => .logical_xor, .logical_xor => .logical_xor,
.logical_or => .logical_or, .logical_or => .logical_or,
.multiply => .multiply, .multiply => .multiply,
.divide => .divide,
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}, },
}); });
@ -1015,6 +1016,7 @@ const Analyzer = struct {
.logical_xor, .logical_xor,
.logical_or, .logical_or,
.multiply, .multiply,
.divide,
=> try analyzer.processBinaryOperation(scope_index, expect_type, node_index), => try analyzer.processBinaryOperation(scope_index, expect_type, node_index),
.expression_group => return try analyzer.resolveNode(value, scope_index, expect_type, node.left), //unreachable, .expression_group => return try analyzer.resolveNode(value, scope_index, expect_type, node.left), //unreachable,
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),

View File

@ -159,6 +159,7 @@ pub const Node = packed struct(u128) {
expression_group = 65, expression_group = 65,
logical_or = 66, logical_or = 66,
multiply = 67, multiply = 67,
divide = 68,
}; };
}; };
@ -719,6 +720,7 @@ const Analyzer = struct {
logical_xor, logical_xor,
logical_or, logical_or,
multiply, multiply,
divide,
}; };
const operator_precedence = std.EnumArray(PrecedenceOperator, i32).init(.{ const operator_precedence = std.EnumArray(PrecedenceOperator, i32).init(.{
@ -730,6 +732,7 @@ const Analyzer = struct {
.logical_xor = 40, .logical_xor = 40,
.logical_or = 40, .logical_or = 40,
.multiply = 70, .multiply = 70,
.divide = 70,
}); });
const operator_associativity = std.EnumArray(PrecedenceOperator, Associativity).init(.{ const operator_associativity = std.EnumArray(PrecedenceOperator, Associativity).init(.{
@ -741,6 +744,7 @@ const Analyzer = struct {
.logical_xor = .left, .logical_xor = .left,
.logical_or = .left, .logical_or = .left,
.multiply = .left, .multiply = .left,
.divide = .left,
}); });
const operator_node_id = std.EnumArray(PrecedenceOperator, Node.Id).init(.{ const operator_node_id = std.EnumArray(PrecedenceOperator, Node.Id).init(.{
@ -752,6 +756,7 @@ const Analyzer = struct {
.logical_xor = .logical_xor, .logical_xor = .logical_xor,
.logical_or = .logical_or, .logical_or = .logical_or,
.multiply = .multiply, .multiply = .multiply,
.divide = .divide,
}); });
fn expressionPrecedence(analyzer: *Analyzer, minimum_precedence: i32) !Node.Index { fn expressionPrecedence(analyzer: *Analyzer, minimum_precedence: i32) !Node.Index {
@ -816,6 +821,10 @@ const Analyzer = struct {
.equal => unreachable, .equal => unreachable,
else => .multiply, else => .multiply,
}, },
.slash => switch (next_token_id) {
.equal => unreachable,
else => .divide,
},
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
} else { } else {
@ -843,6 +852,7 @@ const Analyzer = struct {
.logical_xor, .logical_xor,
.logical_or, .logical_or,
.multiply, .multiply,
.divide,
=> false, => false,
.compare_equal, .compare_equal,
.compare_not_equal, .compare_not_equal,

8
test/div/main.nat Normal file
View File

@ -0,0 +1,8 @@
const main = fn () s32 {
const dividend: s32 = 30;
const divisor: s32 = 6;
const div: s32 = dividend / divisor;
const n: s32 = 5;
return n - div;
}