implement add and sub

This commit is contained in:
David Gonzalez Martin 2023-11-11 11:57:11 -06:00
parent be143a8415
commit 35657715e9
8 changed files with 1415 additions and 696 deletions

42
ci.sh
View File

@ -1,12 +1,44 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -xe echo "Testing Nativity with Zig"
zig build test -Dall --summary all zig build test -Dall -Doptimize=ReleaseSafe --summary all
echo "Compiling Nativity with Zig"
time zig build -Doptimize=ReleaseSafe
failed_test_count=0
passed_test_count=0
test_directory_name=test
test_directory=$test_directory_name/*
total_test_count=$(ls 2>/dev/null -Ubad1 -- test/* | wc -l)
ran_test_count=0
test_i=1
for dir in test/* for dir in $test_directory
do do
zig build run -- $dir/main.nat MY_TESTNAME=${dir##*/}
zig build run -Doptimize=ReleaseSafe -- $dir/main.nat
if [[ "$?" == "0" ]]; then
if [[ "$OSTYPE" == "linux-gnu"* ]]; then if [[ "$OSTYPE" == "linux-gnu"* ]]; then
nat/${dir##*/} nat/$MY_TESTNAME
if [[ "$?" == "0" ]]; then
passed_test_count=$(($passed_test_count + 1))
result="PASSED"
else
failed_test_count=$(($failed_test_count + 1))
result="FAILED"
fi fi
echo "[$test_i/$total_test_count] [$result] $MY_TESTNAME"
ran_test_count=$(($ran_test_count + 1))
fi
else
"$MY_TESTNAME failed to compile"
fi
test_i=$(($test_i + 1))
done done
echo "Ran $ran_test_count tests ($passed_test_count passed, $failed_test_count failed)."
if [[ $failed_test_count == "0" ]]; then
true
else
false
fi

View File

@ -536,6 +536,22 @@ pub const Cast = struct {
pub const Allocation = List.Allocation; pub const Allocation = List.Allocation;
}; };
pub const BinaryOperation = struct {
left: Value.Index,
right: Value.Index,
type: Type.Index,
id: Id,
pub const List = BlockList(@This());
pub const Index = List.Index;
pub const Allocation = List.Allocation;
const Id = enum {
add,
sub,
};
};
pub const CallingConvention = enum { pub const CallingConvention = enum {
system_v, system_v,
}; };
@ -565,6 +581,7 @@ pub const Value = union(enum) {
extern_function: Function.Prototype.Index, extern_function: Function.Prototype.Index,
sign_extend: Cast.Index, sign_extend: Cast.Index,
zero_extend: Cast.Index, zero_extend: Cast.Index,
binary_operation: BinaryOperation.Index,
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
@ -585,6 +602,7 @@ pub const Value = union(enum) {
.bool, .void, .undefined, .function, .type, .enum_field => true, .bool, .void, .undefined, .function, .type, .enum_field => true,
.integer => |integer| integer.type.eq(Type.comptime_int), .integer => |integer| integer.type.eq(Type.comptime_int),
.call => false, .call => false,
.binary_operation => false,
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
} }
@ -598,6 +616,7 @@ pub const Value = union(enum) {
.type => Type.type, .type => Type.type,
.enum_field => |enum_field_index| module.enums.get(module.enum_fields.get(enum_field_index).parent).type, .enum_field => |enum_field_index| module.enums.get(module.enum_fields.get(enum_field_index).parent).type,
.function => |function_index| module.functions.get(function_index).prototype, .function => |function_index| module.functions.get(function_index).prototype,
.binary_operation => |binary_operation| module.binary_operations.get(binary_operation).type,
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
@ -693,6 +712,7 @@ pub const Module = struct {
function_name_map: data_structures.AutoArrayHashMap(Function.Index, u32) = .{}, function_name_map: data_structures.AutoArrayHashMap(Function.Index, u32) = .{},
arrays: BlockList(Array) = .{}, arrays: BlockList(Array) = .{},
casts: BlockList(Cast) = .{}, casts: BlockList(Cast) = .{},
binary_operations: BlockList(BinaryOperation) = .{},
string_literal_types: data_structures.AutoArrayHashMap(u32, Type.Index) = .{}, string_literal_types: data_structures.AutoArrayHashMap(u32, Type.Index) = .{},
array_types: data_structures.AutoArrayHashMap(Array, Type.Index) = .{}, array_types: data_structures.AutoArrayHashMap(Array, Type.Index) = .{},
entry_point: Function.Index = Function.Index.invalid, entry_point: Function.Index = Function.Index.invalid,
@ -1170,7 +1190,7 @@ pub fn log(comptime logger_scope: LoggerScope, logger: getLoggerScopeType(logger
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, return_address: ?usize) noreturn { pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, return_address: ?usize) noreturn {
const print_stack_trace = true; const print_stack_trace = true;
switch (print_stack_trace) { switch (print_stack_trace) {
true => std.builtin.default_panic(message, stack_trace, return_address), true => @call(.always_inline, std.builtin.default_panic, .{ message, stack_trace, return_address }),
false => { false => {
writer.writeAll("\nPANIC: ") catch {}; writer.writeAll("\nPANIC: ") catch {};
writer.writeAll(message) catch {}; writer.writeAll(message) catch {};

File diff suppressed because it is too large Load Diff

View File

@ -889,6 +889,10 @@ const ValueType = struct {
data_type: DataType, data_type: DataType,
scalarness: Scalarness, scalarness: Scalarness,
fn getSize(value_type: ValueType) usize {
return value_type.size * value_type.element_count;
}
const DataType = enum(u1) { const DataType = enum(u1) {
integer = 0, integer = 0,
float = 1, float = 1,
@ -993,6 +997,9 @@ const InstructionSelection = struct {
stack_objects: ArrayList(StackObject) = .{}, stack_objects: ArrayList(StackObject) = .{},
function: *MIR.Function, function: *MIR.Function,
instruction_cache: ArrayList(Instruction.Index) = .{}, instruction_cache: ArrayList(Instruction.Index) = .{},
register_fixups: data_structures.AutoArrayHashMap(Register.Index, Register.Index) = .{},
registers_with_fixups: data_structures.AutoArrayHashMap(Register.Index, void) = .{},
folded_loads: data_structures.AutoArrayHashMap(ir.Instruction.Index, void) = .{},
fn storeRegisterToStackSlot(instruction_selection: *InstructionSelection, mir: *MIR, insert_before_instruction_index: usize, source_register: Register.Physical, kill: bool, frame_index: u32, register_class: Register.Class, virtual_register: Register.Virtual.Index) !void { fn storeRegisterToStackSlot(instruction_selection: *InstructionSelection, mir: *MIR, insert_before_instruction_index: usize, source_register: Register.Physical, kill: bool, frame_index: u32, register_class: Register.Class, virtual_register: Register.Virtual.Index) !void {
_ = virtual_register; _ = virtual_register;
@ -1147,7 +1154,7 @@ const InstructionSelection = struct {
} }
const instruction = mir.ir.instructions.get(ir_instruction_index); const instruction = mir.ir.instructions.get(ir_instruction_index);
const defer_materialization = switch (instruction.*) { const defer_materialization = switch (instruction.u) {
.stack => !instruction_selection.stack_map.contains(ir_instruction_index), .stack => !instruction_selection.stack_map.contains(ir_instruction_index),
.load_integer => false, .load_integer => false,
else => true, else => true,
@ -1161,7 +1168,7 @@ const InstructionSelection = struct {
try instruction_selection.value_map.putNoClobber(mir.allocator, ir_instruction_index, new_register); try instruction_selection.value_map.putNoClobber(mir.allocator, ir_instruction_index, new_register);
return new_register; return new_register;
} else { } else {
const new_register = switch (instruction.*) { const new_register = switch (instruction.u) {
.load_integer => try instruction_selection.materializeInteger(mir, ir_instruction_index), .load_integer => try instruction_selection.materializeInteger(mir, ir_instruction_index),
else => unreachable, else => unreachable,
}; };
@ -1174,7 +1181,7 @@ const InstructionSelection = struct {
// Moving an immediate to a register // Moving an immediate to a register
fn materializeInteger(instruction_selection: *InstructionSelection, mir: *MIR, ir_instruction_index: ir.Instruction.Index) !Register { fn materializeInteger(instruction_selection: *InstructionSelection, mir: *MIR, ir_instruction_index: ir.Instruction.Index) !Register {
// const destination_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index); // const destination_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index);
const integer = mir.ir.instructions.get(ir_instruction_index).load_integer; const integer = mir.ir.instructions.get(ir_instruction_index).u.load_integer;
const value_type = resolveType(integer.type); const value_type = resolveType(integer.type);
// const destination_register_class = register_classes.get(value_type); // const destination_register_class = register_classes.get(value_type);
// const instruction_id: Instruction.Id = // const instruction_id: Instruction.Id =
@ -1232,12 +1239,13 @@ const InstructionSelection = struct {
}; };
const instruction_descriptor = instruction_descriptors.get(instruction_id); const instruction_descriptor = instruction_descriptors.get(instruction_id);
const operand_id = instruction_descriptor.operands[0].id; const destination_operand_id = instruction_descriptor.operands[0].id;
const source_operand_id = instruction_descriptor.operands[1].id;
const register_class = register_classes.get(value_type); const register_class = register_classes.get(value_type);
const destination_register = try mir.createVirtualRegister(register_class); const destination_register = try mir.createVirtualRegister(register_class);
const destination_operand = Operand{ const destination_operand = Operand{
.id = operand_id, .id = destination_operand_id,
.u = .{ .u = .{
.register = destination_register, .register = destination_register,
}, },
@ -1245,7 +1253,7 @@ const InstructionSelection = struct {
}; };
const source_operand = Operand{ const source_operand = Operand{
.id = .immediate, .id = source_operand_id,
.u = .{ .u = .{
.immediate = integer.value.unsigned, .immediate = integer.value.unsigned,
}, },
@ -1266,7 +1274,7 @@ const InstructionSelection = struct {
fn getAddressingModeFromIr(instruction_selection: *InstructionSelection, mir: *MIR, ir_instruction_index: ir.Instruction.Index) AddressingMode { fn getAddressingModeFromIr(instruction_selection: *InstructionSelection, mir: *MIR, ir_instruction_index: ir.Instruction.Index) AddressingMode {
const instruction = mir.ir.instructions.get(ir_instruction_index); const instruction = mir.ir.instructions.get(ir_instruction_index);
switch (instruction.*) { switch (instruction.u) {
.stack => { .stack => {
const frame_index: u32 = @intCast(instruction_selection.stack_map.getIndex(ir_instruction_index).?); const frame_index: u32 = @intCast(instruction_selection.stack_map.getIndex(ir_instruction_index).?);
return AddressingMode{ return AddressingMode{
@ -1303,7 +1311,7 @@ const InstructionSelection = struct {
for (ir_arguments) |ir_argument_instruction_index| { for (ir_arguments) |ir_argument_instruction_index| {
const ir_argument_instruction = mir.ir.instructions.get(ir_argument_instruction_index); const ir_argument_instruction = mir.ir.instructions.get(ir_argument_instruction_index);
const ir_argument = mir.ir.arguments.get(ir_argument_instruction.argument); const ir_argument = mir.ir.arguments.get(ir_argument_instruction.u.argument);
switch (ir_argument.type) { switch (ir_argument.type) {
.i8, .i16, .i32, .i64 => gp_count += 1, .i8, .i16, .i32, .i64 => gp_count += 1,
.void, .void,
@ -1322,7 +1330,7 @@ const InstructionSelection = struct {
for (ir_arguments) |ir_argument_instruction_index| { for (ir_arguments) |ir_argument_instruction_index| {
const ir_argument_instruction = mir.ir.instructions.get(ir_argument_instruction_index); const ir_argument_instruction = mir.ir.instructions.get(ir_argument_instruction_index);
const ir_argument = mir.ir.arguments.get(ir_argument_instruction.argument); const ir_argument = mir.ir.arguments.get(ir_argument_instruction.u.argument);
const value_type = resolveType(ir_argument.type); const value_type = resolveType(ir_argument.type);
const register_class = register_classes.get(value_type); const register_class = register_classes.get(value_type);
const argument_registers = calling_convention.argument_registers.get(register_class); const argument_registers = calling_convention.argument_registers.get(register_class);
@ -1442,11 +1450,33 @@ const InstructionSelection = struct {
// TODO: addLiveIn MachineBasicBlock ? unreachable; // TODO: addLiveIn MachineBasicBlock ? unreachable;
} }
} }
};
fn getRegisterClass(register: Register.Physical) Register.Class { fn tryToFoldLoad(instruction_selection: *InstructionSelection, mir: *MIR, ir_load_instruction_index: ir.Instruction.Index, ir_current: ir.Instruction.Index) !bool {
_ = register; _ = ir_current;
} // TODO: assert load one use
// TODO: check users
const ir_load_instruction = mir.ir.instructions.get(ir_load_instruction_index);
assert(ir_load_instruction.* == .load);
const ir_load = mir.ir.loads.get(ir_load_instruction.load);
if (!ir_load.@"volatile") {
const load_register = try instruction_selection.getRegisterForValue(mir, ir_load_instruction_index);
if (load_register.isValid()) {
const register_has_one_use = true;
if (register_has_one_use) {
if (!instruction_selection.registers_with_fixups.contains(load_register.index)) {
// TODO: architecture dependent part
const addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_load.instruction);
_ = addressing_mode;
unreachable;
}
}
}
}
return false;
}
};
const Instruction = struct { const Instruction = struct {
id: Id, id: Id,
@ -1454,6 +1484,9 @@ const Instruction = struct {
parent: BasicBlock.Index, parent: BasicBlock.Index,
const Id = enum { const Id = enum {
add32rr,
add32rm,
add32mr,
call64pcrel32, call64pcrel32,
copy, copy,
lea64r, lea64r,
@ -1465,9 +1498,12 @@ const Instruction = struct {
mov32ri, mov32ri,
mov32ri64, mov32ri64,
mov32rr, mov32rr,
mov32mi,
movsx64rm32, movsx64rm32,
movsx64rr32, movsx64rr32,
ret, ret,
sub32rr,
sub32rm,
syscall, syscall,
ud2, ud2,
xor32rr, xor32rr,
@ -1476,7 +1512,7 @@ const Instruction = struct {
pub const Descriptor = struct { pub const Descriptor = struct {
operands: []const Operand.Reference = &.{}, operands: []const Operand.Reference = &.{},
opcode: u16, opcode: u16,
format: Format = .pseudo, // format: Format = .pseudo,
flags: Flags = .{}, flags: Flags = .{},
const Flags = packed struct { const Flags = packed struct {
@ -1629,6 +1665,7 @@ const Instruction = struct {
} }
}; };
}; };
pub const Operand = struct { pub const Operand = struct {
id: Operand.Id, id: Operand.Id,
u: union(enum) { u: union(enum) {
@ -1661,7 +1698,10 @@ pub const Operand = struct {
gp32, gp32,
gp64, gp64,
gp64_nosp, gp64_nosp,
immediate, imm8,
imm16,
imm32,
imm64,
i64i32imm_brtarget, i64i32imm_brtarget,
lea64mem, lea64mem,
}; };
@ -1751,13 +1791,70 @@ const register_class_operand_matcher = std.EnumArray(Operand.Id, Register.Class)
.gp32 = .gp32, .gp32 = .gp32,
.gp64 = .gp64, .gp64 = .gp64,
.gp64_nosp = .gp64_nosp, .gp64_nosp = .gp64_nosp,
.immediate = .not_a_register, .imm8 = .not_a_register,
.imm16 = .not_a_register,
.imm32 = .not_a_register,
.imm64 = .not_a_register,
.lea64mem = .not_a_register, .lea64mem = .not_a_register,
}); });
const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descriptor).init(.{ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descriptor).init(.{
.add32rr = .{
// .format = .mrm_dest_reg, // right?
.opcode = 0x01,
.operands = &.{
.{
.id = .gp32,
.kind = .dst,
},
.{
.id = .gp32,
.kind = .src,
},
.{
.id = .gp32,
.kind = .src,
},
},
},
.add32mr = .{
// .format = .mrm_dest_reg, // right?
.opcode = 0x01,
.operands = &.{
.{
.id = .i32mem,
.kind = .dst,
},
.{
.id = .i32mem,
.kind = .src,
},
.{
.id = .gp32,
.kind = .src,
},
},
},
.add32rm = .{
// .format = .mrm_dest_reg, // right?
.opcode = 0x03,
.operands = &.{
.{
.id = .gp32,
.kind = .dst,
},
.{
.id = .gp32,
.kind = .src,
},
.{
.id = .i32mem,
.kind = .src,
},
},
},
.call64pcrel32 = .{ .call64pcrel32 = .{
.format = .no_operands, // .format = .no_operands,
.opcode = 0xe8, .opcode = 0xe8,
.operands = &.{ .operands = &.{
.{ .{
@ -1767,7 +1864,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
}, },
}, },
.copy = .{ .copy = .{
.format = .pseudo, // .format = .pseudo,
.opcode = 0, .opcode = 0,
.operands = &.{ .operands = &.{
.{ .{
@ -1781,7 +1878,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
}, },
}, },
.lea64r = .{ .lea64r = .{
.format = .mrm_source_mem, // .format = .mrm_source_mem,
.opcode = 0x8d, .opcode = 0x8d,
.operands = &.{ .operands = &.{
.{ .{
@ -1795,7 +1892,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
}, },
}, },
.mov32r0 = .{ .mov32r0 = .{
.format = .pseudo, // .format = .pseudo,
.opcode = 0, .opcode = 0,
.operands = &.{ .operands = &.{
.{ .{
@ -1805,7 +1902,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
}, },
}, },
.mov32rm = .{ .mov32rm = .{
.format = .mrm_source_mem, // .format = .mrm_source_mem,
.opcode = 0x8b, .opcode = 0x8b,
.operands = &.{ .operands = &.{
.{ .{
@ -1819,7 +1916,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
}, },
}, },
.mov64rm = .{ .mov64rm = .{
.format = .mrm_source_mem, // .format = .mrm_source_mem,
.opcode = 0x8b, .opcode = 0x8b,
.operands = &.{ .operands = &.{
.{ .{
@ -1833,7 +1930,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
}, },
}, },
.mov32rr = .{ .mov32rr = .{
.format = .mrm_dest_reg, // .format = .mrm_dest_reg,
.opcode = 0x89, .opcode = 0x89,
.operands = &.{ .operands = &.{
.{ .{
@ -1847,7 +1944,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
}, },
}, },
.mov32mr = .{ .mov32mr = .{
.format = .mrm_dest_mem, // .format = .mrm_dest_mem,
.opcode = 0x89, .opcode = 0x89,
.operands = &.{ .operands = &.{
.{ .{
@ -1861,7 +1958,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
}, },
}, },
.mov64mr = .{ .mov64mr = .{
.format = .mrm_dest_mem, // .format = .mrm_dest_mem,
.opcode = 0x89, .opcode = 0x89,
.operands = &.{ .operands = &.{
.{ .{
@ -1875,7 +1972,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
}, },
}, },
.mov32ri = .{ .mov32ri = .{
.format = .add_reg, // .format = .add_reg,
.opcode = 0xb8, .opcode = 0xb8,
.operands = &.{ .operands = &.{
.{ .{
@ -1883,13 +1980,13 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .dst, .kind = .dst,
}, },
.{ .{
.id = .immediate, .id = .imm32,
.kind = .src, .kind = .src,
}, },
}, },
}, },
.mov32ri64 = .{ .mov32ri64 = .{
.format = .pseudo, // .format = .pseudo,
.opcode = 0, .opcode = 0,
.operands = &.{ .operands = &.{
.{ .{
@ -1897,13 +1994,26 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .dst, .kind = .dst,
}, },
.{ .{
.id = .immediate, .id = .imm64,
.kind = .src,
},
},
},
.mov32mi = .{
.opcode = 0xc7,
.operands = &.{
.{
.id = .i32mem,
.kind = .dst,
},
.{
.id = .imm32,
.kind = .src, .kind = .src,
}, },
}, },
}, },
.movsx64rm32 = .{ .movsx64rm32 = .{
.format = .mrm_source_mem, // .format = .mrm_source_mem,
.opcode = 0x63, .opcode = 0x63,
.operands = &.{ .operands = &.{
.{ .{
@ -1917,7 +2027,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
}, },
}, },
.movsx64rr32 = .{ .movsx64rr32 = .{
.format = .mrm_source_reg, // .format = .mrm_source_reg,
.opcode = 0x63, .opcode = 0x63,
.operands = &.{ .operands = &.{
.{ .{
@ -1931,7 +2041,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
}, },
}, },
.ret = .{ .ret = .{
.format = .no_operands, // .format = .no_operands,
.opcode = 0xc3, .opcode = 0xc3,
.operands = &.{ .operands = &.{
.{ .{
@ -1940,8 +2050,44 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
}, },
}, },
}, },
.sub32rr = .{
// .format = .mrm_dest_reg, // right?
.opcode = 0x29,
.operands = &.{
.{
.id = .gp32,
.kind = .dst,
},
.{
.id = .gp32,
.kind = .src,
},
.{
.id = .gp32,
.kind = .src,
},
},
},
.sub32rm = .{
// .format = .mrm_dest_reg, // right?
.opcode = 0x2b,
.operands = &.{
.{
.id = .gp32,
.kind = .dst,
},
.{
.id = .gp32,
.kind = .src,
},
.{
.id = .i32mem,
.kind = .src,
},
},
},
.syscall = .{ .syscall = .{
.format = .no_operands, // .format = .no_operands,
.opcode = 0x05, .opcode = 0x05,
.operands = &.{}, .operands = &.{},
.flags = .{ .flags = .{
@ -1949,7 +2095,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
}, },
}, },
.ud2 = .{ .ud2 = .{
.format = .no_operands, // .format = .no_operands,
.opcode = 0x0b, .opcode = 0x0b,
.operands = &.{}, .operands = &.{},
.flags = .{ .flags = .{
@ -1957,7 +2103,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
}, },
}, },
.xor32rr = .{ .xor32rr = .{
.format = .mrm_dest_reg, // .format = .mrm_dest_reg,
.opcode = 0x31, .opcode = 0x31,
.operands = &.{ .operands = &.{
.{ .{
@ -2087,8 +2233,8 @@ pub const MIR = struct {
const ir_instruction = mir.ir.instructions.get(ir_instruction_index); const ir_instruction = mir.ir.instructions.get(ir_instruction_index);
// TODO: take into account exceptions, dynamic allocas? // TODO: take into account exceptions, dynamic allocas?
if (ir_instruction.* == .stack) { if (ir_instruction.u == .stack) {
const stack = mir.ir.stack_references.get(ir_instruction.stack); const stack = mir.ir.stack_references.get(ir_instruction.u.stack);
const ir_type = getIrType(mir.ir, ir_instruction_index); const ir_type = getIrType(mir.ir, ir_instruction_index);
const value_type = resolveType(ir_type); const value_type = resolveType(ir_type);
const type_info = value_types.get(value_type); const type_info = value_types.get(value_type);
@ -2116,8 +2262,6 @@ pub const MIR = struct {
var instruction_i: usize = ir_block.instructions.items.len; var instruction_i: usize = ir_block.instructions.items.len;
var folded_load = false;
while (instruction_i > 0) { while (instruction_i > 0) {
instruction_i -= 1; instruction_i -= 1;
@ -2128,7 +2272,7 @@ pub const MIR = struct {
logln(.codegen, .instruction_selection_new_instruction, "Instruction #{}", .{instruction_i}); logln(.codegen, .instruction_selection_new_instruction, "Instruction #{}", .{instruction_i});
switch (ir_instruction.*) { switch (ir_instruction.u) {
.ret => |ir_ret_index| { .ret => |ir_ret_index| {
const ir_ret = mir.ir.returns.get(ir_ret_index); const ir_ret = mir.ir.returns.get(ir_ret_index);
switch (ir_ret.instruction.invalid) { switch (ir_ret.instruction.invalid) {
@ -2234,7 +2378,7 @@ pub const MIR = struct {
const produce_syscall_return_value = switch (instruction_i == ir_block.instructions.items.len - 2) { const produce_syscall_return_value = switch (instruction_i == ir_block.instructions.items.len - 2) {
true => blk: { true => blk: {
const last_block_instruction = mir.ir.instructions.get(ir_block.instructions.items[ir_block.instructions.items.len - 1]); const last_block_instruction = mir.ir.instructions.get(ir_block.instructions.items[ir_block.instructions.items.len - 1]);
break :blk switch (last_block_instruction.*) { break :blk switch (last_block_instruction.u) {
.@"unreachable" => false, .@"unreachable" => false,
.ret => true, .ret => true,
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
@ -2289,32 +2433,30 @@ pub const MIR = struct {
}, },
.sign_extend => |ir_cast_index| { .sign_extend => |ir_cast_index| {
const ir_sign_extend = mir.ir.casts.get(ir_cast_index); const ir_sign_extend = mir.ir.casts.get(ir_cast_index);
assert(!folded_load); const fold_load = blk: {
const ir_source_instruction = blk: { const source_instruction = mir.ir.instructions.get(ir_sign_extend.value);
var source = ir_sign_extend.value; const result = switch (source_instruction.u) {
const source_instruction = mir.ir.instructions.get(source); .load => true,
const result = switch (source_instruction.*) { else => false,
.load => b: {
const load = mir.ir.loads.get(source_instruction.load);
folded_load = true;
break :b load.instruction;
},
else => |t| @panic(@tagName(t)),
}; };
break :blk result; break :blk result;
}; };
const destination_type = resolveType(ir_sign_extend.type); const destination_type = resolveType(ir_sign_extend.type);
const source_type = resolveType(getIrType(mir.ir, ir_source_instruction)); if (fold_load) {
const ir_load_instruction_index = ir_sign_extend.value;
const ir_load_instruction = mir.ir.instructions.get(ir_sign_extend.value);
const ir_load = mir.ir.loads.get(ir_load_instruction.u.load);
const ir_source = ir_load.instruction;
const source_type = resolveType(getIrType(mir.ir, ir_source));
if (destination_type != source_type) { if (destination_type != source_type) {
try instruction_selection.folded_loads.putNoClobber(mir.allocator, ir_load_instruction_index, {});
const instruction_id: Instruction.Id = switch (source_type) { const instruction_id: Instruction.Id = switch (source_type) {
.i32 => switch (destination_type) { .i32 => switch (destination_type) {
.i64 => switch (folded_load) { .i64 => .movsx64rm32,
true => .movsx64rm32,
false => .movsx64rr32,
},
else => unreachable, else => unreachable,
}, },
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
@ -2325,11 +2467,9 @@ pub const MIR = struct {
const destination_operand_index = 0; const destination_operand_index = 0;
const destination_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index); const destination_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index);
const destination_operand = mir.constrainOperandRegisterClass(instruction_descriptor, destination_register, destination_operand_index, .{ .type = .def }); const destination_operand = mir.constrainOperandRegisterClass(instruction_descriptor, destination_register, destination_operand_index, .{ .type = .def });
const source_operand_index = 1;
const source_operand = switch (folded_load) { const source_operand = blk: {
true => blk: { const addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_source);
const addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_source_instruction);
const memory_id: Operand.Id = switch (source_type) { const memory_id: Operand.Id = switch (source_type) {
.i32 => .i32mem, .i32 => .i32mem,
.i64 => .i64mem, .i64 => .i64mem,
@ -2345,11 +2485,6 @@ pub const MIR = struct {
.flags = .{}, .flags = .{},
}; };
break :blk operand; break :blk operand;
},
false => blk: {
const source_register = try instruction_selection.getRegisterForValue(mir, ir_source_instruction);
break :blk mir.constrainOperandRegisterClass(instruction_descriptor, source_register, source_operand_index, .{});
},
}; };
const sign_extend = try mir.buildInstruction(instruction_selection, instruction_id, &.{ const sign_extend = try mir.buildInstruction(instruction_selection, instruction_id, &.{
@ -2363,13 +2498,14 @@ pub const MIR = struct {
} else { } else {
unreachable; unreachable;
} }
} else {
// const source_register = try instruction_selection.getRegisterForValue(mir, ir_source_instruction);
// const source_operand = mir.constrainOperandRegisterClass(instruction_descriptor, source_register, source_operand_index, .{});
unreachable;
}
}, },
.load => |ir_load_index| { .load => |ir_load_index| {
if (folded_load) { if (!instruction_selection.folded_loads.swapRemove(ir_instruction_index)) {
folded_load = false;
continue;
}
const ir_load = mir.ir.loads.get(ir_load_index); const ir_load = mir.ir.loads.get(ir_load_index);
const ir_source = ir_load.instruction; const ir_source = ir_load.instruction;
const addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_source); const addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_source);
@ -2423,20 +2559,57 @@ pub const MIR = struct {
}, },
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
} }
}
}, },
.store => |ir_store_index| { .store => |ir_store_index| {
const ir_store = mir.ir.stores.get(ir_store_index); const ir_store = mir.ir.stores.get(ir_store_index);
const ir_source = ir_store.source; const ir_source_index = ir_store.source;
const ir_destination = ir_store.destination; const ir_destination = ir_store.destination;
const addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_destination); const addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_destination);
const ir_source = mir.ir.instructions.get(ir_source_index);
const source_register = try instruction_selection.getRegisterForValue(mir, ir_source); const value_type = resolveType(getIrType(mir.ir, ir_source_index));
const value_type = resolveType(getIrType(mir.ir, ir_source)); if (ir_source.u == .load_integer and value_types.get(value_type).getSize() <= @sizeOf(u32)) {
const instruction_id: Instruction.Id = switch (value_type) {
.i32 => .mov32mi,
else => unreachable,
};
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_id = instruction_descriptor.operands[source_operand_index].id;
const source_operand = Operand{
.id = source_operand_id,
.u = .{
.immediate = source_immediate.value.unsigned,
},
.flags = .{},
};
const destination_operand_id = instruction_descriptor.operands[0].id;
const destination_operand = Operand{
.id = destination_operand_id,
.u = .{
.memory = .{
.addressing_mode = addressing_mode,
},
},
.flags = .{},
};
const store = try mir.buildInstruction(instruction_selection, instruction_id, &.{
destination_operand,
source_operand,
});
try instruction_selection.instruction_cache.append(mir.allocator, store);
} else {
const source_register = try instruction_selection.getRegisterForValue(mir, ir_source_index);
switch (value_type) { switch (value_type) {
inline .i32, .i64 => |vt| { .i32, .i64 => |vt| {
const instruction_id: Instruction.Id = switch (vt) { const instruction_id: Instruction.Id = switch (vt) {
// TODO, non-temporal SSE2 MOVNT // TODO, non-temporal SSE2 MOVNT
.i32 => .mov32mr, .i32 => .mov32mr,
@ -2444,7 +2617,7 @@ pub const MIR = struct {
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
const instruction_descriptor = comptime 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 = instruction_descriptor.operands.len - 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{
@ -2475,6 +2648,7 @@ pub const MIR = struct {
}, },
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
} }
}
}, },
.stack => { .stack => {
assert(instruction_selection.stack_map.get(ir_instruction_index) != null); assert(instruction_selection.stack_map.get(ir_instruction_index) != null);
@ -2631,6 +2805,94 @@ pub const MIR = struct {
try instruction_selection.updateValueMap(mir.allocator, ir_instruction_index, virtual_register, false); try instruction_selection.updateValueMap(mir.allocator, ir_instruction_index, virtual_register, false);
}, },
.binary_operation => |ir_binary_operation_index| {
const ir_binary_operation = mir.ir.binary_operations.get(ir_binary_operation_index);
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,
};
// 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_right_load and !is_left_load) {
unreachable;
} else if (!is_right_load and is_left_load) {
unreachable;
} else if (!is_right_load and !is_left_load) {
unreachable;
} else {
// 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) {
.i32 => .add32rm,
else => unreachable,
},
.sub => switch (value_type) {
.i32 => .sub32rm,
else => unreachable,
},
};
try instruction_selection.folded_loads.putNoClobber(mir.allocator, ir_binary_operation.right, {});
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;
assert(right_operand_id == .i32mem);
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 destination_operand = Operand{
.id = destination_operand_id,
.u = .{
.register = destination_register,
},
.flags = .{
.type = .def,
},
};
const left_operand = Operand{
.id = left_operand_id,
.u = .{
.register = left_register,
},
.flags = .{},
};
const right_operand = Operand{
.id = right_operand_id,
.u = .{
.memory = .{ .addressing_mode = right_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);
try instruction_selection.updateValueMap(mir.allocator, ir_instruction_index, destination_register, false);
}
},
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
} }
@ -3159,12 +3421,6 @@ pub const MIR = struct {
const instruction = mir.instructions.get(instruction_index); const instruction = mir.instructions.get(instruction_index);
switch (instruction.id) { switch (instruction.id) {
.mov32rm => {},
.mov32r0 => {},
.mov32ri => {},
.mov64rm => {},
.lea64r => {},
.mov32ri64 => {},
.copy => { .copy => {
const operand_index = instruction.operands.items[1]; const operand_index = instruction.operands.items[1];
const operand = mir.operands.get(operand_index); const operand = mir.operands.get(operand_index);
@ -3175,7 +3431,7 @@ pub const MIR = struct {
logln(.codegen, .register_allocation_problematic_hint, "[traceCopies] Missed oportunity for register allocation tracing copy chain for VR{}", .{virtual_register_index.uniqueInteger()}); logln(.codegen, .register_allocation_problematic_hint, "[traceCopies] Missed oportunity for register allocation tracing copy chain for VR{}", .{virtual_register_index.uniqueInteger()});
}, },
else => |t| @panic(@tagName(t)), else => {},
} }
try_count += 1; try_count += 1;
@ -3742,6 +3998,7 @@ pub const MIR = struct {
const gp_register_encoding: Encoding.GP32 = switch (physical_register) { const gp_register_encoding: Encoding.GP32 = switch (physical_register) {
.eax => .a, .eax => .a,
.edi => .di, .edi => .di,
.ecx => .c,
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
@ -4173,6 +4430,105 @@ pub const MIR = struct {
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
} }
}, },
.add32rr, .sub32rr => {
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);
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 destination_register = getGP32Encoding(destination_operand.*);
const left_register = getGP32Encoding(left_operand.*);
const right_register = getGP32Encoding(right_operand.*);
assert(destination_register == left_register);
const modrm = ModRm{
.rm = @intCast(@intFromEnum(destination_register)),
.reg = @intCast(@intFromEnum(right_register)),
.mod = 0b11,
};
try image.section_manager.appendCodeByte(@bitCast(modrm));
},
.add32rm, .sub32rm => {
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);
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 destination_register = getGP32Encoding(destination_operand.*);
const left_register = getGP32Encoding(left_operand.*);
assert(destination_register == left_register);
assert(right_operand.id == .i32mem);
const right_memory = right_operand.u.memory;
switch (right_memory.addressing_mode.base) {
.register_base => unreachable,
.frame_index => |frame_index| {
const modrm = ModRm{
.rm = @intFromEnum(Encoding.GP64.bp),
.reg = @intCast(@intFromEnum(destination_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);
},
}
},
.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);
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 = 0,
.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);
},
}
const source_operand_index = instruction.operands.items[1];
const source_operand = mir.operands.get(source_operand_index);
const source_immediate: u32 = @intCast(source_operand.u.immediate);
try image.section_manager.appendCode(std.mem.asBytes(&source_immediate));
},
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
} }
@ -4376,12 +4732,32 @@ pub const MIR = struct {
if (!clamp or alignment <= stack_alignment) return alignment; if (!clamp or alignment <= stack_alignment) return alignment;
return stack_alignment; return stack_alignment;
} }
};
const ModRm = packed struct(u8) { fn isFoldedOrDeadInstruction(mir: *MIR, instruction_index: ir.Instruction.Index) bool {
rm: u3, const result = !mir.mayWriteToMemory(instruction_index) and !mir.isTerminator(instruction_index);
reg: u3, return result;
mod: u2, }
fn mayWriteToMemory(mir: *MIR, instruction_index: ir.Instruction.Index) bool {
const ir_instruction = mir.ir.instructions.get(instruction_index);
const result = switch (ir_instruction.*) {
.load => !mir.ir.loads.get(ir_instruction.load).isUnordered(),
.store => true,
else => |t| @panic(@tagName(t)),
};
return result;
}
fn isTerminator(mir: *MIR, instruction_index: ir.Instruction.Index) bool {
const ir_instruction = mir.ir.instructions.get(instruction_index);
const result = switch (ir_instruction.*) {
.load => false,
else => |t| @panic(@tagName(t)),
};
return result;
}
}; };
const Rex = packed struct(u8) { const Rex = packed struct(u8) {
@ -4423,9 +4799,21 @@ const Rex = packed struct(u8) {
// } // }
}; };
const Sib = packed struct(u8) {
base: u3,
index: u3,
scale: u2,
};
const ModRm = packed struct(u8) {
rm: u3,
reg: u3,
mod: u2,
};
fn getIrType(intermediate: *ir.Result, ir_instruction_index: ir.Instruction.Index) ir.Type { fn getIrType(intermediate: *ir.Result, ir_instruction_index: ir.Instruction.Index) ir.Type {
const ir_instruction = intermediate.instructions.get(ir_instruction_index); const ir_instruction = intermediate.instructions.get(ir_instruction_index);
return switch (ir_instruction.*) { return switch (ir_instruction.u) {
.argument => |argument_index| intermediate.arguments.get(argument_index).type, .argument => |argument_index| intermediate.arguments.get(argument_index).type,
.stack => |stack_index| intermediate.stack_references.get(stack_index).type, .stack => |stack_index| intermediate.stack_references.get(stack_index).type,
.load => |load_index| getIrType(intermediate, intermediate.loads.get(load_index).instruction), .load => |load_index| getIrType(intermediate, intermediate.loads.get(load_index).instruction),
@ -4434,6 +4822,7 @@ fn getIrType(intermediate: *ir.Result, ir_instruction_index: ir.Instruction.Inde
.load_string_literal => .i64, .load_string_literal => .i64,
.call => |call_index| intermediate.function_declarations.get(intermediate.calls.get(call_index).function).return_type, .call => |call_index| intermediate.function_declarations.get(intermediate.calls.get(call_index).function).return_type,
.sign_extend => |cast_index| intermediate.casts.get(cast_index).type, .sign_extend => |cast_index| intermediate.casts.get(cast_index).type,
.binary_operation => |binary_operation_index| intermediate.binary_operations.get(binary_operation_index).type,
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
} }

View File

@ -164,7 +164,7 @@ pub fn analyze(allocator: Allocator, text: []const u8, file_index: File.Index) !
inline else => |comptime_fixed_keyword| @field(Token.Id, "fixed_keyword_" ++ @tagName(comptime_fixed_keyword)), inline else => |comptime_fixed_keyword| @field(Token.Id, "fixed_keyword_" ++ @tagName(comptime_fixed_keyword)),
} else .identifier; } else .identifier;
}, },
'(', ')', '{', '}', '[', ']', '-', '=', ';', '#', '@', ',', '.', ':', '>', '<', '*', '!' => |operator| blk: { '(', ')', '{', '}', '[', ']', '=', ';', '#', '@', ',', '.', ':', '>', '<', '!', '+', '-', '*', '\\', '/' => |operator| blk: {
index += 1; index += 1;
break :blk @enumFromInt(operator); break :blk @enumFromInt(operator);
}, },

View File

@ -38,7 +38,17 @@ pub const Logger = enum {
block, block,
call, call,
pub var bitset = std.EnumSet(Logger).initEmpty(); pub var bitset = std.EnumSet(Logger).initMany(&.{
.type,
.identifier,
.symbol_declaration,
.scope_node,
.node,
.typecheck,
.@"switch",
.block,
.call,
});
}; };
const lexical_analyzer = @import("lexical_analyzer.zig"); const lexical_analyzer = @import("lexical_analyzer.zig");
@ -145,7 +155,7 @@ const Analyzer = struct {
try statement_nodes.append(analyzer.allocator, block_node.left); try statement_nodes.append(analyzer.allocator, block_node.left);
try statement_nodes.append(analyzer.allocator, block_node.right); try statement_nodes.append(analyzer.allocator, block_node.right);
}, },
.block, .comptime_block => unreachable, //statement_nodes = analyzer.getNodeList(scope_index, block_node.left.unwrap()), .block, .comptime_block => statement_nodes = analyzer.getScopeNodeList(scope_index, analyzer.getScopeNode(scope_index, block_node.left)),
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
} }
@ -512,6 +522,33 @@ const Analyzer = struct {
}; };
} }
fn processBinaryOperation(analyzer: *Analyzer, scope_index: Scope.Index, expect_type: ExpectType, node_index: Node.Index) !Value {
const node = analyzer.getScopeNode(scope_index, node_index);
const left_allocation = try analyzer.unresolvedAllocate(scope_index, expect_type, node.left);
const right_allocation = try analyzer.unresolvedAllocate(scope_index, expect_type, node.right);
const left_type = left_allocation.ptr.getType(analyzer.module);
const right_type = right_allocation.ptr.getType(analyzer.module);
if (!left_type.eq(right_type)) {
unreachable;
}
const binary_operation = try analyzer.module.binary_operations.append(analyzer.allocator, .{
.left = left_allocation.index,
.right = right_allocation.index,
.type = left_type,
.id = switch (node.id) {
.add => .add,
.sub => .sub,
else => |t| @panic(@tagName(t)),
},
});
return .{
.binary_operation = binary_operation.index,
};
}
const DeclarationLookup = struct { const DeclarationLookup = struct {
declaration: Declaration.Index, declaration: Declaration.Index,
scope: Scope.Index, scope: Scope.Index,
@ -968,6 +1005,9 @@ const Analyzer = struct {
.type = try analyzer.resolveType(scope_index, node_index), .type = try analyzer.resolveType(scope_index, node_index),
}, },
.@"return" => try analyzer.processReturn(scope_index, expect_type, node_index), .@"return" => try analyzer.processReturn(scope_index, expect_type, node_index),
.add,
.sub,
=> try analyzer.processBinaryOperation(scope_index, expect_type, node_index),
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
} }

View File

@ -141,6 +141,8 @@ pub const Node = packed struct(u128) {
enum_field = 58, enum_field = 58,
extern_qualifier = 59, extern_qualifier = 59,
function_prototype = 60, function_prototype = 60,
add = 61,
sub = 62,
}; };
}; };
@ -692,6 +694,33 @@ const Analyzer = struct {
return try analyzer.expressionPrecedence(0); return try analyzer.expressionPrecedence(0);
} }
const PrecedenceOperator = enum {
compare_equal,
compare_not_equal,
add,
sub,
};
const operator_precedence = std.EnumArray(PrecedenceOperator, i32).init(.{
.compare_equal = 30,
.compare_not_equal = 30,
.add = 60,
.sub = 60,
});
const operator_associativity = std.EnumArray(PrecedenceOperator, Associativity).init(.{
.compare_equal = .none,
.compare_not_equal = .none,
.add = .left,
.sub = .left,
});
const operator_node_id = std.EnumArray(PrecedenceOperator, Node.Id).init(.{
.compare_equal = .compare_equal,
.compare_not_equal = .compare_not_equal,
.add = .add,
.sub = .sub,
});
fn expressionPrecedence(analyzer: *Analyzer, minimum_precedence: i32) !Node.Index { fn expressionPrecedence(analyzer: *Analyzer, minimum_precedence: i32) !Node.Index {
var result = try analyzer.prefixExpression(); var result = try analyzer.prefixExpression();
if (!result.invalid) { if (!result.invalid) {
@ -704,55 +733,75 @@ const Analyzer = struct {
while (analyzer.token_i < analyzer.tokens.len) { while (analyzer.token_i < analyzer.tokens.len) {
const token = analyzer.tokens[analyzer.token_i]; const token = analyzer.tokens[analyzer.token_i];
// logln("Looping in expression precedence with token {}\n", .{token}); // logln("Looping in expression precedence with token {}\n", .{token});
const precedence: i32 = switch (token.id) { const operator: PrecedenceOperator = switch (token.id) {
.equal, .semicolon, .right_parenthesis, .right_brace, .comma, .period, .fixed_keyword_const, .fixed_keyword_var => -1, .equal, .semicolon, .right_parenthesis, .right_brace, .comma, .period, .fixed_keyword_const, .fixed_keyword_var => break,
.bang => switch (analyzer.tokens[analyzer.token_i + 1].id) { else => blk: {
.equal => 30, const next_token_index = analyzer.token_i + 1;
if (next_token_index < analyzer.tokens.len) {
const next_token_id = analyzer.tokens[next_token_index].id;
break :blk switch (token.id) {
.equal => switch (next_token_id) {
.equal => .compare_equal,
else => break,
},
.bang => switch (next_token_id) {
.equal => .compare_not_equal,
else => unreachable, else => unreachable,
}, },
else => |t| { .plus => switch (next_token_id) {
const start = token.start; .plus => unreachable,
logln(.parser, .precedence, "Source file:\n```\n{s}\n```\n", .{analyzer.source_file[start..]}); .equal => unreachable,
@panic(@tagName(t)); else => .add,
},
.minus => switch (next_token_id) {
.minus => unreachable,
.equal => unreachable,
else => .sub,
},
else => |t| @panic(@tagName(t)),
};
} else {
unreachable;
}
}, },
}; };
logln(.parser, .precedence, "Precedence: {} ({s}) (file #{})\n", .{ precedence, @tagName(token.id), analyzer.file_index.uniqueInteger() });
const precedence = operator_precedence.get(operator);
if (precedence < minimum_precedence) { if (precedence < minimum_precedence) {
logln(.parser, .precedence, "Breaking for minimum_precedence\n", .{}); logln(.parser, .precedence, "Breaking for minimum_precedence\n", .{});
break; break;
} }
if (precedence == banned_precedence) { if (precedence < banned_precedence) {
logln(.parser, .precedence, "Breaking for banned precedence\n", .{}); logln(.parser, .precedence, "Breaking for banned_precedence\n", .{});
break; break;
} }
const operator_token = analyzer.token_i; const operator_token = analyzer.token_i;
const is_bang_equal = analyzer.tokens[operator_token].id == .bang and analyzer.tokens[operator_token + 1].id == .equal; const extra_token = switch (operator) {
analyzer.token_i += @as(u32, 1) + @intFromBool(is_bang_equal); .add,
.sub,
=> false,
.compare_equal,
.compare_not_equal,
=> true,
// else => |t| @panic(@tagName(t)),
};
analyzer.token_i += @as(u32, 1) + @intFromBool(extra_token);
// TODO: fix this // TODO: fix this
const right = try analyzer.expressionPrecedence(precedence + 1); const right = try analyzer.expressionPrecedence(precedence + 1);
const operation_id: Node.Id = switch (is_bang_equal) { const node_id = operator_node_id.get(operator);
true => .compare_not_equal,
false => switch (analyzer.tokens[operator_token].id) {
else => |t| @panic(@tagName(t)),
},
};
result = try analyzer.addNode(.{ result = try analyzer.addNode(.{
.id = operation_id, .id = node_id,
.token = operator_token, .token = operator_token,
.left = result, .left = result,
.right = right, .right = right,
}); });
const associativity: Associativity = switch (operation_id) { const associativity = operator_associativity.get(operator);
.compare_equal, .compare_not_equal, .compare_less_than, .compare_greater_than, .compare_less_or_equal, .compare_greater_or_equal => .none,
else => .left,
};
if (associativity == .none) { if (associativity == .none) {
banned_precedence = precedence; banned_precedence = precedence;

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

@ -0,0 +1,8 @@
const main = fn() s32 {
const a: s32 = 1;
const b: s32 = 2;
const c: s32 = a + b;
const d: s32 = 3;
const e: s32 = d - c;
return e;
}