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
set -xe
zig build test -Dall --summary all
echo "Testing Nativity with Zig"
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
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
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
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
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 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 {
system_v,
};
@ -565,6 +581,7 @@ pub const Value = union(enum) {
extern_function: Function.Prototype.Index,
sign_extend: Cast.Index,
zero_extend: Cast.Index,
binary_operation: BinaryOperation.Index,
pub const List = BlockList(@This());
pub const Index = List.Index;
@ -585,6 +602,7 @@ pub const Value = union(enum) {
.bool, .void, .undefined, .function, .type, .enum_field => true,
.integer => |integer| integer.type.eq(Type.comptime_int),
.call => false,
.binary_operation => false,
else => |t| @panic(@tagName(t)),
};
}
@ -598,6 +616,7 @@ pub const Value = union(enum) {
.type => Type.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,
.binary_operation => |binary_operation| module.binary_operations.get(binary_operation).type,
else => |t| @panic(@tagName(t)),
};
@ -693,6 +712,7 @@ pub const Module = struct {
function_name_map: data_structures.AutoArrayHashMap(Function.Index, u32) = .{},
arrays: BlockList(Array) = .{},
casts: BlockList(Cast) = .{},
binary_operations: BlockList(BinaryOperation) = .{},
string_literal_types: data_structures.AutoArrayHashMap(u32, Type.Index) = .{},
array_types: data_structures.AutoArrayHashMap(Array, Type.Index) = .{},
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 {
const print_stack_trace = true;
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 => {
writer.writeAll("\nPANIC: ") 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,
scalarness: Scalarness,
fn getSize(value_type: ValueType) usize {
return value_type.size * value_type.element_count;
}
const DataType = enum(u1) {
integer = 0,
float = 1,
@ -993,6 +997,9 @@ const InstructionSelection = struct {
stack_objects: ArrayList(StackObject) = .{},
function: *MIR.Function,
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 {
_ = virtual_register;
@ -1147,7 +1154,7 @@ const InstructionSelection = struct {
}
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),
.load_integer => false,
else => true,
@ -1161,7 +1168,7 @@ const InstructionSelection = struct {
try instruction_selection.value_map.putNoClobber(mir.allocator, ir_instruction_index, new_register);
return new_register;
} else {
const new_register = switch (instruction.*) {
const new_register = switch (instruction.u) {
.load_integer => try instruction_selection.materializeInteger(mir, ir_instruction_index),
else => unreachable,
};
@ -1174,7 +1181,7 @@ const InstructionSelection = struct {
// Moving an immediate to a 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 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 destination_register_class = register_classes.get(value_type);
// const instruction_id: Instruction.Id =
@ -1232,12 +1239,13 @@ const InstructionSelection = struct {
};
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 destination_register = try mir.createVirtualRegister(register_class);
const destination_operand = Operand{
.id = operand_id,
.id = destination_operand_id,
.u = .{
.register = destination_register,
},
@ -1245,7 +1253,7 @@ const InstructionSelection = struct {
};
const source_operand = Operand{
.id = .immediate,
.id = source_operand_id,
.u = .{
.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 {
const instruction = mir.ir.instructions.get(ir_instruction_index);
switch (instruction.*) {
switch (instruction.u) {
.stack => {
const frame_index: u32 = @intCast(instruction_selection.stack_map.getIndex(ir_instruction_index).?);
return AddressingMode{
@ -1303,7 +1311,7 @@ const InstructionSelection = struct {
for (ir_arguments) |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) {
.i8, .i16, .i32, .i64 => gp_count += 1,
.void,
@ -1322,7 +1330,7 @@ const InstructionSelection = struct {
for (ir_arguments) |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 register_class = register_classes.get(value_type);
const argument_registers = calling_convention.argument_registers.get(register_class);
@ -1442,11 +1450,33 @@ const InstructionSelection = struct {
// TODO: addLiveIn MachineBasicBlock ? unreachable;
}
}
};
fn getRegisterClass(register: Register.Physical) Register.Class {
_ = register;
}
fn tryToFoldLoad(instruction_selection: *InstructionSelection, mir: *MIR, ir_load_instruction_index: ir.Instruction.Index, ir_current: ir.Instruction.Index) !bool {
_ = 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 {
id: Id,
@ -1454,6 +1484,9 @@ const Instruction = struct {
parent: BasicBlock.Index,
const Id = enum {
add32rr,
add32rm,
add32mr,
call64pcrel32,
copy,
lea64r,
@ -1465,9 +1498,12 @@ const Instruction = struct {
mov32ri,
mov32ri64,
mov32rr,
mov32mi,
movsx64rm32,
movsx64rr32,
ret,
sub32rr,
sub32rm,
syscall,
ud2,
xor32rr,
@ -1476,7 +1512,7 @@ const Instruction = struct {
pub const Descriptor = struct {
operands: []const Operand.Reference = &.{},
opcode: u16,
format: Format = .pseudo,
// format: Format = .pseudo,
flags: Flags = .{},
const Flags = packed struct {
@ -1629,6 +1665,7 @@ const Instruction = struct {
}
};
};
pub const Operand = struct {
id: Operand.Id,
u: union(enum) {
@ -1661,7 +1698,10 @@ pub const Operand = struct {
gp32,
gp64,
gp64_nosp,
immediate,
imm8,
imm16,
imm32,
imm64,
i64i32imm_brtarget,
lea64mem,
};
@ -1751,13 +1791,70 @@ const register_class_operand_matcher = std.EnumArray(Operand.Id, Register.Class)
.gp32 = .gp32,
.gp64 = .gp64,
.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,
});
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 = .{
.format = .no_operands,
// .format = .no_operands,
.opcode = 0xe8,
.operands = &.{
.{
@ -1767,7 +1864,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
},
},
.copy = .{
.format = .pseudo,
// .format = .pseudo,
.opcode = 0,
.operands = &.{
.{
@ -1781,7 +1878,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
},
},
.lea64r = .{
.format = .mrm_source_mem,
// .format = .mrm_source_mem,
.opcode = 0x8d,
.operands = &.{
.{
@ -1795,7 +1892,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
},
},
.mov32r0 = .{
.format = .pseudo,
// .format = .pseudo,
.opcode = 0,
.operands = &.{
.{
@ -1805,7 +1902,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
},
},
.mov32rm = .{
.format = .mrm_source_mem,
// .format = .mrm_source_mem,
.opcode = 0x8b,
.operands = &.{
.{
@ -1819,7 +1916,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
},
},
.mov64rm = .{
.format = .mrm_source_mem,
// .format = .mrm_source_mem,
.opcode = 0x8b,
.operands = &.{
.{
@ -1833,7 +1930,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
},
},
.mov32rr = .{
.format = .mrm_dest_reg,
// .format = .mrm_dest_reg,
.opcode = 0x89,
.operands = &.{
.{
@ -1847,7 +1944,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
},
},
.mov32mr = .{
.format = .mrm_dest_mem,
// .format = .mrm_dest_mem,
.opcode = 0x89,
.operands = &.{
.{
@ -1861,7 +1958,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
},
},
.mov64mr = .{
.format = .mrm_dest_mem,
// .format = .mrm_dest_mem,
.opcode = 0x89,
.operands = &.{
.{
@ -1875,7 +1972,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
},
},
.mov32ri = .{
.format = .add_reg,
// .format = .add_reg,
.opcode = 0xb8,
.operands = &.{
.{
@ -1883,13 +1980,13 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .dst,
},
.{
.id = .immediate,
.id = .imm32,
.kind = .src,
},
},
},
.mov32ri64 = .{
.format = .pseudo,
// .format = .pseudo,
.opcode = 0,
.operands = &.{
.{
@ -1897,13 +1994,26 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .dst,
},
.{
.id = .immediate,
.id = .imm64,
.kind = .src,
},
},
},
.mov32mi = .{
.opcode = 0xc7,
.operands = &.{
.{
.id = .i32mem,
.kind = .dst,
},
.{
.id = .imm32,
.kind = .src,
},
},
},
.movsx64rm32 = .{
.format = .mrm_source_mem,
// .format = .mrm_source_mem,
.opcode = 0x63,
.operands = &.{
.{
@ -1917,7 +2027,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
},
},
.movsx64rr32 = .{
.format = .mrm_source_reg,
// .format = .mrm_source_reg,
.opcode = 0x63,
.operands = &.{
.{
@ -1931,7 +2041,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
},
},
.ret = .{
.format = .no_operands,
// .format = .no_operands,
.opcode = 0xc3,
.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 = .{
.format = .no_operands,
// .format = .no_operands,
.opcode = 0x05,
.operands = &.{},
.flags = .{
@ -1949,7 +2095,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
},
},
.ud2 = .{
.format = .no_operands,
// .format = .no_operands,
.opcode = 0x0b,
.operands = &.{},
.flags = .{
@ -1957,7 +2103,7 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
},
},
.xor32rr = .{
.format = .mrm_dest_reg,
// .format = .mrm_dest_reg,
.opcode = 0x31,
.operands = &.{
.{
@ -2087,8 +2233,8 @@ pub const MIR = struct {
const ir_instruction = mir.ir.instructions.get(ir_instruction_index);
// TODO: take into account exceptions, dynamic allocas?
if (ir_instruction.* == .stack) {
const stack = mir.ir.stack_references.get(ir_instruction.stack);
if (ir_instruction.u == .stack) {
const stack = mir.ir.stack_references.get(ir_instruction.u.stack);
const ir_type = getIrType(mir.ir, ir_instruction_index);
const value_type = resolveType(ir_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 folded_load = false;
while (instruction_i > 0) {
instruction_i -= 1;
@ -2128,7 +2272,7 @@ pub const MIR = struct {
logln(.codegen, .instruction_selection_new_instruction, "Instruction #{}", .{instruction_i});
switch (ir_instruction.*) {
switch (ir_instruction.u) {
.ret => |ir_ret_index| {
const ir_ret = mir.ir.returns.get(ir_ret_index);
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) {
true => blk: {
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,
.ret => true,
else => |t| @panic(@tagName(t)),
@ -2289,32 +2433,30 @@ pub const MIR = struct {
},
.sign_extend => |ir_cast_index| {
const ir_sign_extend = mir.ir.casts.get(ir_cast_index);
assert(!folded_load);
const ir_source_instruction = blk: {
var source = ir_sign_extend.value;
const source_instruction = mir.ir.instructions.get(source);
const result = switch (source_instruction.*) {
.load => b: {
const load = mir.ir.loads.get(source_instruction.load);
folded_load = true;
break :b load.instruction;
},
else => |t| @panic(@tagName(t)),
const fold_load = blk: {
const source_instruction = mir.ir.instructions.get(ir_sign_extend.value);
const result = switch (source_instruction.u) {
.load => true,
else => false,
};
break :blk result;
};
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) {
try instruction_selection.folded_loads.putNoClobber(mir.allocator, ir_load_instruction_index, {});
const instruction_id: Instruction.Id = switch (source_type) {
.i32 => switch (destination_type) {
.i64 => switch (folded_load) {
true => .movsx64rm32,
false => .movsx64rr32,
},
.i64 => .movsx64rm32,
else => unreachable,
},
else => |t| @panic(@tagName(t)),
@ -2325,11 +2467,9 @@ pub const MIR = struct {
const destination_operand_index = 0;
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 source_operand_index = 1;
const source_operand = switch (folded_load) {
true => blk: {
const addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_source_instruction);
const source_operand = blk: {
const addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_source);
const memory_id: Operand.Id = switch (source_type) {
.i32 => .i32mem,
.i64 => .i64mem,
@ -2345,11 +2485,6 @@ pub const MIR = struct {
.flags = .{},
};
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, &.{
@ -2363,13 +2498,14 @@ pub const MIR = struct {
} else {
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| {
if (folded_load) {
folded_load = false;
continue;
}
if (!instruction_selection.folded_loads.swapRemove(ir_instruction_index)) {
const ir_load = mir.ir.loads.get(ir_load_index);
const ir_source = ir_load.instruction;
const addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_source);
@ -2423,20 +2559,57 @@ pub const MIR = struct {
},
else => |t| @panic(@tagName(t)),
}
}
},
.store => |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 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) {
inline .i32, .i64 => |vt| {
.i32, .i64 => |vt| {
const instruction_id: Instruction.Id = switch (vt) {
// TODO, non-temporal SSE2 MOVNT
.i32 => .mov32mr,
@ -2444,7 +2617,7 @@ pub const MIR = struct {
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_id = instruction_descriptor.operands[source_operand_index].id;
const source_operand = Operand{
@ -2475,6 +2648,7 @@ pub const MIR = struct {
},
else => |t| @panic(@tagName(t)),
}
}
},
.stack => {
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);
},
.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)),
}
@ -3159,12 +3421,6 @@ pub const MIR = struct {
const instruction = mir.instructions.get(instruction_index);
switch (instruction.id) {
.mov32rm => {},
.mov32r0 => {},
.mov32ri => {},
.mov64rm => {},
.lea64r => {},
.mov32ri64 => {},
.copy => {
const operand_index = instruction.operands.items[1];
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()});
},
else => |t| @panic(@tagName(t)),
else => {},
}
try_count += 1;
@ -3742,6 +3998,7 @@ pub const MIR = struct {
const gp_register_encoding: Encoding.GP32 = switch (physical_register) {
.eax => .a,
.edi => .di,
.ecx => .c,
else => |t| @panic(@tagName(t)),
};
@ -4173,6 +4430,105 @@ pub const MIR = struct {
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)),
}
@ -4376,12 +4732,32 @@ pub const MIR = struct {
if (!clamp or alignment <= stack_alignment) return alignment;
return stack_alignment;
}
};
const ModRm = packed struct(u8) {
rm: u3,
reg: u3,
mod: u2,
fn isFoldedOrDeadInstruction(mir: *MIR, instruction_index: ir.Instruction.Index) bool {
const result = !mir.mayWriteToMemory(instruction_index) and !mir.isTerminator(instruction_index);
return result;
}
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) {
@ -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 {
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,
.stack => |stack_index| intermediate.stack_references.get(stack_index).type,
.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,
.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,
.binary_operation => |binary_operation_index| intermediate.binary_operations.get(binary_operation_index).type,
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)),
} else .identifier;
},
'(', ')', '{', '}', '[', ']', '-', '=', ';', '#', '@', ',', '.', ':', '>', '<', '*', '!' => |operator| blk: {
'(', ')', '{', '}', '[', ']', '=', ';', '#', '@', ',', '.', ':', '>', '<', '!', '+', '-', '*', '\\', '/' => |operator| blk: {
index += 1;
break :blk @enumFromInt(operator);
},

View File

@ -38,7 +38,17 @@ pub const Logger = enum {
block,
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");
@ -145,7 +155,7 @@ const Analyzer = struct {
try statement_nodes.append(analyzer.allocator, block_node.left);
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)),
}
@ -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 {
declaration: Declaration.Index,
scope: Scope.Index,
@ -968,6 +1005,9 @@ const Analyzer = struct {
.type = try analyzer.resolveType(scope_index, 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)),
};
}

View File

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