implement signed division
This commit is contained in:
parent
8ab7a0d768
commit
f96cf16a2a
@ -553,6 +553,7 @@ pub const BinaryOperation = struct {
|
||||
logical_xor,
|
||||
logical_or,
|
||||
multiply,
|
||||
divide,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -238,7 +238,8 @@ pub const BinaryOperation = struct {
|
||||
logical_and,
|
||||
logical_xor,
|
||||
logical_or,
|
||||
multiply,
|
||||
signed_multiply,
|
||||
signed_divide,
|
||||
};
|
||||
|
||||
pub const List = BlockList(@This());
|
||||
@ -729,6 +730,9 @@ pub const Builder = struct {
|
||||
const left = try builder.emitBinaryOperationOperand(sema_binary_operation.left);
|
||||
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, .{
|
||||
.left = left,
|
||||
.right = right,
|
||||
@ -738,9 +742,24 @@ pub const Builder = struct {
|
||||
.logical_and => .logical_and,
|
||||
.logical_xor => .logical_xor,
|
||||
.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(.{
|
||||
|
@ -40,18 +40,19 @@ pub const Logger = enum {
|
||||
pub var bitset = std.EnumSet(Logger).initMany(&.{
|
||||
.instruction_selection_ir_function,
|
||||
.instruction_selection_mir_function,
|
||||
// .instruction_selection_register_operand_list,
|
||||
// .register_allocation_block,
|
||||
// .register_allocation_problematic_hint,
|
||||
// .register_allocation_assignment,
|
||||
// .register_allocation_reload,
|
||||
// .register_allocation_function_before,
|
||||
// .register_allocation_new_instruction,
|
||||
// .register_allocation_new_instruction_function_before,
|
||||
// .register_allocation_instruction_avoid_copy,
|
||||
.instruction_selection_register_operand_list,
|
||||
.instruction_selection_new_instruction,
|
||||
.register_allocation_block,
|
||||
.register_allocation_problematic_hint,
|
||||
.register_allocation_assignment,
|
||||
.register_allocation_reload,
|
||||
.register_allocation_function_before,
|
||||
.register_allocation_new_instruction,
|
||||
.register_allocation_new_instruction_function_before,
|
||||
.register_allocation_instruction_avoid_copy,
|
||||
.register_allocation_function_after,
|
||||
// .register_allocation_operand_list_verification,
|
||||
// .encoding,
|
||||
.register_allocation_operand_list_verification,
|
||||
.encoding,
|
||||
});
|
||||
};
|
||||
|
||||
@ -91,6 +92,7 @@ const Register = struct {
|
||||
gp32,
|
||||
gp64,
|
||||
gp64_nosp,
|
||||
ccr,
|
||||
|
||||
pub const Descriptor = struct {
|
||||
size: u16,
|
||||
@ -817,6 +819,11 @@ const register_class_descriptors = std.EnumArray(Register.Class, Register.Class.
|
||||
.spill_size = 64,
|
||||
.spill_alignment = 64,
|
||||
},
|
||||
.ccr = .{
|
||||
.size = 32,
|
||||
.spill_size = 32,
|
||||
.spill_alignment = 32,
|
||||
},
|
||||
});
|
||||
|
||||
const registers_by_class = RegisterGroupMap.init(.{
|
||||
@ -858,6 +865,9 @@ const registers_by_class = RegisterGroupMap.init(.{
|
||||
.rbp,
|
||||
.rsp,
|
||||
},
|
||||
.ccr = &.{
|
||||
.eflags,
|
||||
},
|
||||
.gp64_nosp = &.{},
|
||||
});
|
||||
|
||||
@ -874,6 +884,7 @@ const system_v = CallingConvention{
|
||||
.gp32 = &system_v_gp32_argument_registers,
|
||||
.gp64 = &system_v_gp64_argument_registers,
|
||||
.gp64_nosp = &.{},
|
||||
.ccr = &.{},
|
||||
}),
|
||||
.syscall_registers = &system_v_syscall_registers,
|
||||
};
|
||||
@ -911,6 +922,7 @@ const ValueType = struct {
|
||||
i32 = 5,
|
||||
i64 = 6,
|
||||
// i128 = 7,
|
||||
ccr = 15,
|
||||
};
|
||||
};
|
||||
|
||||
@ -936,12 +948,20 @@ const value_types = std.EnumArray(ValueType.Id, ValueType).init(.{
|
||||
.data_type = .integer,
|
||||
.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(.{
|
||||
.any = .any,
|
||||
.i32 = .gp32,
|
||||
.i64 = .gp64,
|
||||
.ccr = .ccr,
|
||||
});
|
||||
|
||||
const Memory = struct {
|
||||
@ -1295,6 +1315,7 @@ const InstructionSelection = struct {
|
||||
if (!gop.value_ptr.isValid()) {
|
||||
gop.value_ptr.* = register;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
@ -1488,9 +1509,13 @@ const Instruction = struct {
|
||||
add32rm,
|
||||
add32mr,
|
||||
and32rm,
|
||||
and32mr,
|
||||
and32rr,
|
||||
call64pcrel32,
|
||||
copy,
|
||||
idiv32r,
|
||||
idiv32m,
|
||||
imul32mr,
|
||||
imul32rm,
|
||||
imul32rr,
|
||||
lea64r,
|
||||
@ -1506,12 +1531,15 @@ const Instruction = struct {
|
||||
movsx64rm32,
|
||||
movsx64rr32,
|
||||
or32rm,
|
||||
or32mr,
|
||||
or32rr,
|
||||
ret,
|
||||
sub32mr,
|
||||
sub32rr,
|
||||
sub32rm,
|
||||
syscall,
|
||||
ud2,
|
||||
xor32mr,
|
||||
xor32rr,
|
||||
xor32rm,
|
||||
};
|
||||
@ -1521,6 +1549,8 @@ const Instruction = struct {
|
||||
opcode: u16,
|
||||
// format: Format = .pseudo,
|
||||
flags: Flags = .{},
|
||||
implicit_definitions: []const Register.Physical = &.{},
|
||||
implicit_uses: []const Register.Physical = &.{},
|
||||
|
||||
const Flags = packed struct {
|
||||
implicit_def: bool = false,
|
||||
@ -1711,6 +1741,7 @@ pub const Operand = struct {
|
||||
imm64,
|
||||
i64i32imm_brtarget,
|
||||
lea64mem,
|
||||
ccr,
|
||||
};
|
||||
pub const Type = enum(u1) {
|
||||
use = 0,
|
||||
@ -1803,6 +1834,7 @@ const register_class_operand_matcher = std.EnumArray(Operand.Id, Register.Class)
|
||||
.imm32 = .not_a_register,
|
||||
.imm64 = .not_a_register,
|
||||
.lea64mem = .not_a_register,
|
||||
.ccr = .ccr,
|
||||
});
|
||||
|
||||
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,
|
||||
},
|
||||
},
|
||||
.implicit_definitions = &.{.eflags},
|
||||
},
|
||||
.add32mr = .{
|
||||
// .format = .mrm_dest_reg, // right?
|
||||
@ -1841,6 +1874,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
|
||||
.kind = .src,
|
||||
},
|
||||
},
|
||||
.implicit_definitions = &.{.eflags},
|
||||
},
|
||||
.add32rm = .{
|
||||
// .format = .mrm_dest_reg, // right?
|
||||
@ -1859,6 +1893,26 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
|
||||
.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 = .{
|
||||
// .format = .mrm_dest_reg, // right?
|
||||
@ -1877,6 +1931,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
|
||||
.kind = .src,
|
||||
},
|
||||
},
|
||||
.implicit_definitions = &.{.eflags},
|
||||
},
|
||||
.and32rm = .{
|
||||
// .format = .mrm_dest_reg, // right?
|
||||
@ -1895,6 +1950,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
|
||||
.kind = .src,
|
||||
},
|
||||
},
|
||||
.implicit_definitions = &.{.eflags},
|
||||
},
|
||||
.call64pcrel32 = .{
|
||||
// .format = .no_operands,
|
||||
@ -1905,6 +1961,8 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
|
||||
.kind = .src,
|
||||
},
|
||||
},
|
||||
.implicit_definitions = &.{},
|
||||
.implicit_uses = &.{},
|
||||
},
|
||||
.copy = .{
|
||||
// .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 = .{
|
||||
.opcode = 0x6b,
|
||||
.operands = &.{
|
||||
@ -1936,6 +2039,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
|
||||
.kind = .src,
|
||||
},
|
||||
},
|
||||
.implicit_definitions = &.{.eflags},
|
||||
},
|
||||
.imul32rm = .{
|
||||
.opcode = 0x6b,
|
||||
@ -1953,6 +2057,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
|
||||
.kind = .src,
|
||||
},
|
||||
},
|
||||
.implicit_definitions = &.{.eflags},
|
||||
},
|
||||
.lea64r = .{
|
||||
// .format = .mrm_source_mem,
|
||||
@ -2134,6 +2239,26 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
|
||||
.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 = .{
|
||||
// .format = .mrm_dest_reg,
|
||||
@ -2152,6 +2277,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
|
||||
.kind = .src,
|
||||
},
|
||||
},
|
||||
.implicit_definitions = &.{.eflags},
|
||||
},
|
||||
.ret = .{
|
||||
// .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 = .{
|
||||
// .format = .mrm_dest_reg, // right?
|
||||
.opcode = 0x29,
|
||||
@ -2180,6 +2325,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
|
||||
.kind = .src,
|
||||
},
|
||||
},
|
||||
.implicit_definitions = &.{.eflags},
|
||||
},
|
||||
.sub32rm = .{
|
||||
// .format = .mrm_dest_reg, // right?
|
||||
@ -2198,6 +2344,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
|
||||
.kind = .src,
|
||||
},
|
||||
},
|
||||
.implicit_definitions = &.{.eflags},
|
||||
},
|
||||
.syscall = .{
|
||||
// .format = .no_operands,
|
||||
@ -2232,6 +2379,26 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
|
||||
.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 = .{
|
||||
// .format = .mrm_dest_reg,
|
||||
@ -2250,6 +2417,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
|
||||
.kind = .src,
|
||||
},
|
||||
},
|
||||
.implicit_definitions = &.{.eflags},
|
||||
},
|
||||
});
|
||||
|
||||
@ -2715,7 +2883,7 @@ pub const MIR = struct {
|
||||
const source_immediate = ir_source.u.load_integer;
|
||||
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 = Operand{
|
||||
.id = source_operand_id,
|
||||
@ -2753,7 +2921,7 @@ pub const MIR = struct {
|
||||
};
|
||||
|
||||
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 = Operand{
|
||||
.id = source_operand_id,
|
||||
@ -2942,30 +3110,227 @@ pub const MIR = struct {
|
||||
},
|
||||
.binary_operation => |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) {
|
||||
.load => true,
|
||||
else => false,
|
||||
};
|
||||
|
||||
const is_left_load = switch (mir.ir.instructions.get(ir_binary_operation.right).u) {
|
||||
.load => true,
|
||||
else => false,
|
||||
switch (ir_binary_operation.id) {
|
||||
.signed_divide => {
|
||||
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?
|
||||
// for (ir_instruction.use_list.items) |use_index| {
|
||||
// const use = mir.ir.instructions.get(use_index);
|
||||
// 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) {
|
||||
unreachable;
|
||||
} 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) {
|
||||
const destination_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index);
|
||||
const instruction_id: Instruction.Id = switch (ir_binary_operation.id) {
|
||||
.add => switch (value_type) {
|
||||
.i32 => .add32rr,
|
||||
@ -2987,10 +3352,11 @@ pub const MIR = struct {
|
||||
.i32 => .or32rr,
|
||||
else => unreachable,
|
||||
},
|
||||
.multiply => switch (value_type) {
|
||||
.signed_multiply => switch (value_type) {
|
||||
.i32 => .imul32rr,
|
||||
else => unreachable,
|
||||
},
|
||||
.signed_divide => unreachable,
|
||||
};
|
||||
|
||||
const instruction_descriptor = instruction_descriptors.get(instruction_id);
|
||||
@ -3026,6 +3392,7 @@ pub const MIR = struct {
|
||||
},
|
||||
.flags = .{},
|
||||
};
|
||||
|
||||
const binary_op_instruction = try mir.buildInstruction(instruction_selection, instruction_id, &.{
|
||||
destination_operand,
|
||||
left_operand,
|
||||
@ -3036,6 +3403,7 @@ pub const MIR = struct {
|
||||
|
||||
try instruction_selection.updateValueMap(mir.allocator, ir_instruction_index, destination_register, false);
|
||||
} 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
|
||||
const instruction_id: Instruction.Id = switch (ir_binary_operation.id) {
|
||||
.add => switch (value_type) {
|
||||
@ -3058,10 +3426,11 @@ pub const MIR = struct {
|
||||
.i32 => .or32rm,
|
||||
else => unreachable,
|
||||
},
|
||||
.multiply => switch (value_type) {
|
||||
.signed_multiply => switch (value_type) {
|
||||
.i32 => .imul32rm,
|
||||
else => unreachable,
|
||||
},
|
||||
.signed_divide => unreachable,
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
|
||||
@ -3762,20 +4133,21 @@ pub const MIR = struct {
|
||||
const instruction = mir.instructions.get(instruction_index);
|
||||
const operand = mir.operands.get(operand_index);
|
||||
const gop = try register_allocator.live_virtual_registers.getOrPut(mir.allocator, virtual_register);
|
||||
const live_register = gop.value_ptr;
|
||||
if (!gop.found_existing) {
|
||||
gop.value_ptr.* = .{
|
||||
.virtual = virtual_register,
|
||||
};
|
||||
if (!operand.flags.dead_or_kill) {
|
||||
var live_out = false;
|
||||
if (live_out) {
|
||||
if (live_register.live_out) {
|
||||
// TODO
|
||||
unreachable;
|
||||
} else {
|
||||
operand.flags.dead_or_kill = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
const live_register = gop.value_ptr;
|
||||
|
||||
if (live_register.physical == .no_register) {
|
||||
try register_allocator.allocateVirtualRegister(mir, instruction_selection, instruction_index, live_register, null, look_at_physical_register_uses);
|
||||
} else {
|
||||
@ -3785,6 +4157,7 @@ pub const MIR = struct {
|
||||
const physical_register = live_register.physical;
|
||||
assert(physical_register != .no_register);
|
||||
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);
|
||||
if (!instruction_descriptor.flags.implicit_def) {
|
||||
const spill_before = mir.getNextInstructionIndex(instruction_index);
|
||||
@ -3987,8 +4360,7 @@ pub const MIR = struct {
|
||||
var early_clobber = false;
|
||||
var assign_live_throughs = false;
|
||||
|
||||
for (instruction.operands.items, 0..) |operand_index, operand_i| {
|
||||
_ = operand_i;
|
||||
for (instruction.operands.items) |operand_index| {
|
||||
const operand = mir.operands.get(operand_index);
|
||||
switch (operand.u) {
|
||||
.register => |register| switch (register.index) {
|
||||
@ -4183,7 +4555,7 @@ pub const MIR = struct {
|
||||
continue;
|
||||
}
|
||||
|
||||
mir.verifyUseList(vr.use_def_list_head, vr_index);
|
||||
// mir.verifyUseList(vr.use_def_list_head, vr_index);
|
||||
vr_index = vr_it.getCurrentIndex();
|
||||
}
|
||||
|
||||
@ -4296,7 +4668,6 @@ pub const MIR = struct {
|
||||
|
||||
switch (instruction.id) {
|
||||
.mov32r0 => {
|
||||
assert(instruction.operands.items.len == 1);
|
||||
const operand = mir.operands.get(instruction.operands.items[0]);
|
||||
const gp_register_encoding = getGP32Encoding(operand.*);
|
||||
const new_instruction_id = Instruction.Id.xor32rr;
|
||||
@ -4313,7 +4684,6 @@ pub const MIR = struct {
|
||||
},
|
||||
.ret => {},
|
||||
.mov32mr => {
|
||||
assert(instruction.operands.items.len == 2);
|
||||
const source_operand = mir.operands.get(instruction.operands.items[1]);
|
||||
const source_gp32 = getGP32Encoding(source_operand.*);
|
||||
|
||||
@ -4343,8 +4713,6 @@ pub const MIR = struct {
|
||||
}
|
||||
},
|
||||
.mov64mr => {
|
||||
assert(instruction.operands.items.len == 2);
|
||||
|
||||
const rex = Rex{
|
||||
.b = false,
|
||||
.x = false,
|
||||
@ -4382,8 +4750,6 @@ pub const MIR = struct {
|
||||
}
|
||||
},
|
||||
.mov32rm => {
|
||||
assert(instruction.operands.items.len == 2);
|
||||
|
||||
const instruction_descriptor = instruction_descriptors.get(instruction.id);
|
||||
const opcode: u8 = @intCast(instruction_descriptor.opcode);
|
||||
try image.section_manager.appendCodeByte(opcode);
|
||||
@ -4414,8 +4780,6 @@ pub const MIR = struct {
|
||||
}
|
||||
},
|
||||
.mov64rm => {
|
||||
assert(instruction.operands.items.len == 2);
|
||||
|
||||
const rex = Rex{
|
||||
.b = false,
|
||||
.x = false,
|
||||
@ -4454,8 +4818,6 @@ pub const MIR = struct {
|
||||
}
|
||||
},
|
||||
.mov32ri => {
|
||||
assert(instruction.operands.items.len == 2);
|
||||
|
||||
const source_operand = mir.operands.get(instruction.operands.items[1]);
|
||||
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));
|
||||
},
|
||||
.mov32ri64 => {
|
||||
assert(instruction.operands.items.len == 2);
|
||||
const source_operand = mir.operands.get(instruction.operands.items[1]);
|
||||
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));
|
||||
},
|
||||
.movsx64rm32 => {
|
||||
assert(instruction.operands.items.len == 2);
|
||||
|
||||
const destination_operand = mir.operands.get(instruction.operands.items[0]);
|
||||
const destination_register = getGP64Encoding(destination_operand.*);
|
||||
|
||||
@ -4526,7 +4885,6 @@ pub const MIR = struct {
|
||||
.ud2 => try image.section_manager.appendCode(&.{ 0x0f, 0x0b }),
|
||||
.call64pcrel32 => {
|
||||
// TODO: emit relocation
|
||||
assert(instruction.operands.items.len == 1);
|
||||
const operand = mir.operands.get(instruction.operands.items[0]);
|
||||
const instruction_descriptor = instruction_descriptors.get(instruction.id);
|
||||
const opcode: u8 = @intCast(instruction_descriptor.opcode);
|
||||
@ -4555,7 +4913,6 @@ pub const MIR = struct {
|
||||
}
|
||||
},
|
||||
.copy => {
|
||||
assert(instruction.operands.items.len == 2);
|
||||
const destination_operand = mir.operands.get(instruction.operands.items[0]);
|
||||
const source_operand = mir.operands.get(instruction.operands.items[1]);
|
||||
assert(destination_operand.id == source_operand.id);
|
||||
@ -4598,7 +4955,6 @@ pub const MIR = struct {
|
||||
}
|
||||
},
|
||||
.lea64r => {
|
||||
assert(instruction.operands.items.len == 2);
|
||||
const rex = Rex{
|
||||
.b = false,
|
||||
.x = false,
|
||||
@ -4652,7 +5008,6 @@ pub const MIR = struct {
|
||||
.sub32rr,
|
||||
.imul32rr,
|
||||
=> {
|
||||
assert(instruction.operands.items.len == 3);
|
||||
const instruction_descriptor = instruction_descriptors.get(instruction.id);
|
||||
const opcode: u8 = @intCast(instruction_descriptor.opcode);
|
||||
try image.section_manager.appendCodeByte(opcode);
|
||||
@ -4679,8 +5034,6 @@ pub const MIR = struct {
|
||||
try image.section_manager.appendCodeByte(@bitCast(modrm));
|
||||
},
|
||||
.mov32mi => {
|
||||
assert(instruction.operands.items.len == 2);
|
||||
|
||||
const instruction_descriptor = instruction_descriptors.get(instruction.id);
|
||||
const opcode: u8 = @intCast(instruction_descriptor.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));
|
||||
},
|
||||
.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,
|
||||
.sub32rm,
|
||||
.and32rm,
|
||||
@ -4718,7 +5096,6 @@ pub const MIR = struct {
|
||||
.or32rm,
|
||||
.imul32rm,
|
||||
=> {
|
||||
assert(instruction.operands.items.len == 3);
|
||||
const instruction_descriptor = instruction_descriptors.get(instruction.id);
|
||||
const opcode: u8 = @intCast(instruction_descriptor.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)),
|
||||
}
|
||||
|
||||
@ -4872,9 +5286,9 @@ pub const MIR = struct {
|
||||
|
||||
fn buildInstruction(mir: *MIR, instruction_selection: *InstructionSelection, instruction: Instruction.Id, operands: []const Operand) !Instruction.Index {
|
||||
// Some sanity check
|
||||
const descriptor = instruction_descriptors.getPtrConst(instruction);
|
||||
{
|
||||
if (instruction != .copy) {
|
||||
const descriptor = instruction_descriptors.getPtrConst(instruction);
|
||||
if (descriptor.operands.len == operands.len) {
|
||||
for (descriptor.operands, operands) |descriptor_operand, operand| {
|
||||
switch (descriptor_operand.id) {
|
||||
@ -4886,13 +5300,13 @@ pub const MIR = struct {
|
||||
switch (instruction) {
|
||||
.ret => {},
|
||||
.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);
|
||||
// TODO: MachineRegisterInfo::addRegOperandToUseList
|
||||
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.* = .{
|
||||
.id = instruction,
|
||||
.operands = list,
|
||||
.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;
|
||||
}
|
||||
|
||||
|
@ -544,6 +544,7 @@ const Analyzer = struct {
|
||||
.logical_xor => .logical_xor,
|
||||
.logical_or => .logical_or,
|
||||
.multiply => .multiply,
|
||||
.divide => .divide,
|
||||
else => |t| @panic(@tagName(t)),
|
||||
},
|
||||
});
|
||||
@ -1015,6 +1016,7 @@ const Analyzer = struct {
|
||||
.logical_xor,
|
||||
.logical_or,
|
||||
.multiply,
|
||||
.divide,
|
||||
=> try analyzer.processBinaryOperation(scope_index, expect_type, node_index),
|
||||
.expression_group => return try analyzer.resolveNode(value, scope_index, expect_type, node.left), //unreachable,
|
||||
else => |t| @panic(@tagName(t)),
|
||||
|
@ -159,6 +159,7 @@ pub const Node = packed struct(u128) {
|
||||
expression_group = 65,
|
||||
logical_or = 66,
|
||||
multiply = 67,
|
||||
divide = 68,
|
||||
};
|
||||
};
|
||||
|
||||
@ -719,6 +720,7 @@ const Analyzer = struct {
|
||||
logical_xor,
|
||||
logical_or,
|
||||
multiply,
|
||||
divide,
|
||||
};
|
||||
|
||||
const operator_precedence = std.EnumArray(PrecedenceOperator, i32).init(.{
|
||||
@ -730,6 +732,7 @@ const Analyzer = struct {
|
||||
.logical_xor = 40,
|
||||
.logical_or = 40,
|
||||
.multiply = 70,
|
||||
.divide = 70,
|
||||
});
|
||||
|
||||
const operator_associativity = std.EnumArray(PrecedenceOperator, Associativity).init(.{
|
||||
@ -741,6 +744,7 @@ const Analyzer = struct {
|
||||
.logical_xor = .left,
|
||||
.logical_or = .left,
|
||||
.multiply = .left,
|
||||
.divide = .left,
|
||||
});
|
||||
|
||||
const operator_node_id = std.EnumArray(PrecedenceOperator, Node.Id).init(.{
|
||||
@ -752,6 +756,7 @@ const Analyzer = struct {
|
||||
.logical_xor = .logical_xor,
|
||||
.logical_or = .logical_or,
|
||||
.multiply = .multiply,
|
||||
.divide = .divide,
|
||||
});
|
||||
|
||||
fn expressionPrecedence(analyzer: *Analyzer, minimum_precedence: i32) !Node.Index {
|
||||
@ -816,6 +821,10 @@ const Analyzer = struct {
|
||||
.equal => unreachable,
|
||||
else => .multiply,
|
||||
},
|
||||
.slash => switch (next_token_id) {
|
||||
.equal => unreachable,
|
||||
else => .divide,
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
} else {
|
||||
@ -843,6 +852,7 @@ const Analyzer = struct {
|
||||
.logical_xor,
|
||||
.logical_or,
|
||||
.multiply,
|
||||
.divide,
|
||||
=> false,
|
||||
.compare_equal,
|
||||
.compare_not_equal,
|
||||
|
8
test/div/main.nat
Normal file
8
test/div/main.nat
Normal 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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user