From 4c358c2f89b828dd92655e3893c149a169deef46 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Mon, 13 Nov 2023 20:49:24 -0600 Subject: [PATCH] implement shifts --- ci.sh | 33 +- src/Compilation.zig | 9 +- src/backend/intermediate_representation.zig | 19 +- src/backend/macho.zig | 58 +- src/backend/pe.zig | 15 +- src/backend/x86_64.zig | 904 ++++++++++++++------ src/frontend/semantic_analyzer.zig | 87 +- src/frontend/syntactic_analyzer.zig | 28 +- test/imul/main.nat | 2 +- test/shifts/main.nat | 7 + 10 files changed, 824 insertions(+), 338 deletions(-) create mode 100644 test/shifts/main.nat diff --git a/ci.sh b/ci.sh index ee742b7..3d15aef 100755 --- a/ci.sh +++ b/ci.sh @@ -2,7 +2,7 @@ echo "Testing Nativity with Zig" echo "Compiling Nativity with Zig" -zig build -Doptimize=ReleaseSafe +zig build failed_test_count=0 passed_test_count=0 test_directory_name=test @@ -10,12 +10,17 @@ test_directory=$test_directory_name/* total_test_count=$(ls 2>/dev/null -Ubad1 -- test/* | wc -l) ran_test_count=0 test_i=1 +passed_compilation_count=0 +failed_compilation_count=0 +failed_compilations=() +failed_tests=() for dir in $test_directory do MY_TESTNAME=${dir##*/} - zig build run -Doptimize=ReleaseSafe -- $dir/main.nat + zig build run -- $dir/main.nat if [[ "$?" == "0" ]]; then + passed_compilation_count=$(($passed_compilation_count + 1)) if [[ "$OSTYPE" == "linux-gnu"* ]]; then nat/$MY_TESTNAME if [[ "$?" == "0" ]]; then @@ -24,19 +29,39 @@ do else failed_test_count=$(($failed_test_count + 1)) result="FAILED" + failed_tests+=("$test_i. $MY_TESTNAME") fi echo "[$test_i/$total_test_count] [$result] $MY_TESTNAME" ran_test_count=$(($ran_test_count + 1)) fi else - "$MY_TESTNAME failed to compile" + failed_compilation_count=$(($failed_compilation_count + 1)) + echo "$MY_TESTNAME failed to compile" + failed_compilations+=("$test_i. $MY_TESTNAME") fi test_i=$(($test_i + 1)) done +echo "Ran $total_test_count compilations ($passed_compilation_count succeeded, $failed_compilation_count failed)." echo "Ran $ran_test_count tests ($passed_test_count passed, $failed_test_count failed)." -if [[ $failed_test_count == "0" ]]; then +if [[ "$failed_compilation_count" != "0" ]]; then + echo "Failed compilations:" + for failed_compilation in "${failed_compilations[@]}" + do + echo "$failed_compilation" + done +fi + +if [[ "$failed_test_count" != "0" ]]; then + echo "Failed tests:" + for failed_test in "${failed_tests[@]}" + do + echo "$failed_test" + done +fi + +if [[ "$failed_test_count" == "0" && "$failed_compilation_count" == "0" ]]; then true else false diff --git a/src/Compilation.zig b/src/Compilation.zig index a3bb10a..69be14b 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -546,7 +546,7 @@ pub const BinaryOperation = struct { pub const Index = List.Index; pub const Allocation = List.Allocation; - const Id = enum { + pub const Id = enum { add, sub, logical_and, @@ -554,6 +554,8 @@ pub const BinaryOperation = struct { logical_or, multiply, divide, + shift_left, + shift_right, }; }; @@ -602,12 +604,13 @@ pub const Value = union(enum) { } }; - pub fn isComptime(value: Value) bool { - return switch (value) { + pub fn isComptime(value: *Value, module: *Module) bool { + return switch (value.*) { .bool, .void, .undefined, .function, .type, .enum_field => true, .integer => |integer| integer.type.eq(Type.comptime_int), .call => false, .binary_operation => false, + .declaration_reference => |declaration_reference| module.declarations.get(declaration_reference.value).mutability == .@"const" and isComptime(module.values.get(module.declarations.get(declaration_reference.value).init_value), module), else => |t| @panic(@tagName(t)), }; } diff --git a/src/backend/intermediate_representation.zig b/src/backend/intermediate_representation.zig index 9025aa9..d2a5a34 100644 --- a/src/backend/intermediate_representation.zig +++ b/src/backend/intermediate_representation.zig @@ -240,6 +240,8 @@ pub const BinaryOperation = struct { logical_or, signed_multiply, signed_divide, + shift_left, + shift_right, }; pub const List = BlockList(@This()); @@ -749,7 +751,6 @@ pub const Builder = struct { }, else => |t| @panic(@tagName(t)), }, - //.multiply, .divide => switch (sema_type) { .integer => |integer| switch (integer.signedness) { .signed => .signed_divide, @@ -757,7 +758,8 @@ pub const Builder = struct { }, else => |t| @panic(@tagName(t)), }, - //.divide, + .shift_left => .shift_left, + .shift_right => .shift_right, }, .type = binary_operation_type, }); @@ -917,6 +919,19 @@ pub const Builder = struct { } }, .call => |sema_call_index| _ = try builder.processCall(sema_call_index), + .assign => |sema_assignment_index| { + const sema_assignment = builder.ir.module.assignments.get(sema_assignment_index); + const sema_left = builder.ir.module.values.get(sema_assignment.store); + assert(sema_left.* == .declaration_reference); + const sema_declaration_index = sema_left.declaration_reference.value; + const stack = builder.currentFunction().stack_map.get(sema_declaration_index).?; + const value_index = try builder.emitDeclarationInitValue(sema_assignment.load); + const store_instruction = try builder.store(.{ + .source = value_index, + .destination = stack, + }); + _ = store_instruction; + }, else => |t| @panic(@tagName(t)), } } diff --git a/src/backend/macho.zig b/src/backend/macho.zig index fa84c7b..00e2ff8 100644 --- a/src/backend/macho.zig +++ b/src/backend/macho.zig @@ -2,7 +2,6 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; const equal = std.mem.eql; -const print = std.debug.print; const Compilation = @import("../Compilation.zig"); @@ -487,7 +486,7 @@ pub fn interpretFile(allocator: Allocator, descriptor: Compilation.Module.Descri _ = allocator; _ = descriptor; const header: *const Header = @ptrCast(@alignCast(file.ptr)); - print("Header : {}", .{header}); + //print("Header : {}", .{header}); assert(header.magic == Header.magic); var text_segment: LoadCommand.Segment64 = undefined; @@ -503,69 +502,83 @@ pub fn interpretFile(allocator: Allocator, descriptor: Compilation.Module.Descri if (equal(u8, segment_load_command.name[0..text_segment_name.len], text_segment_name)) { text_segment = segment_load_command.*; } - print("SLC: {}", .{segment_load_command}); - print("segment name: {s}", .{segment_load_command.name}); + //print("SLC: {}", .{segment_load_command}); + //print("segment name: {s}", .{segment_load_command.name}); const section_ptr: [*]const LoadCommand.Segment64.Section = @ptrFromInt(@intFromPtr(segment_load_command) + @sizeOf(LoadCommand.Segment64)); const sections = section_ptr[0..segment_load_command.section_count]; for (sections) |section| { - print("{}", .{section}); - print("Section name: {s}. Segment name: {s}", .{ section.name, section.segment_name }); + _ = section; + //print("{}", .{section}); + //print("Section name: {s}. Segment name: {s}", .{ section.name, section.segment_name }); } }, .dyld_chained_fixups => { const command: *const LoadCommand.LinkeditData = @ptrCast(@alignCast(load_command_ptr)); - print("command: {}", .{command}); + _ = command; + //print("command: {}", .{command}); }, .dyld_exports_trie => { const command: *const LoadCommand.LinkeditData = @ptrCast(@alignCast(load_command_ptr)); - print("command: {}", .{command}); + _ = command; + //print("command: {}", .{command}); }, .symbol_table => { const command: *const LoadCommand.SymbolTable = @ptrCast(@alignCast(load_command_ptr)); - print("command: {}", .{command}); + _ = command; + //print("command: {}", .{command}); }, .symbol_table_information => { const command: *const LoadCommand.SymbolTableInformation = @ptrCast(@alignCast(load_command_ptr)); - print("command: {}", .{command}); + _ = command; + //print("command: {}", .{command}); }, .load_dylinker => { const command: *const LoadCommand.Dylinker = @ptrCast(@alignCast(load_command_ptr)); - print("command: {}", .{command}); + //print("command: {}", .{command}); const name: [*:0]const u8 = @ptrFromInt(@intFromPtr(command) + command.name_offset); - print("Name: {s}", .{name}); + _ = name; + //print("Name: {s}", .{name}); }, .uuid_number => { const command: *const LoadCommand.Uuid = @ptrCast(@alignCast(load_command_ptr)); - print("command: {}", .{command}); + _ = command; + //print("command: {}", .{command}); }, .minimum_os_version => { const command: *const LoadCommand.MinimumVersion = @ptrCast(@alignCast(load_command_ptr)); - print("command: {}", .{command}); + _ = command; + //print("command: {}", .{command}); }, .source_version => { const command: *const LoadCommand.SourceVersion = @ptrCast(@alignCast(load_command_ptr)); - print("command: {}", .{command}); + _ = command; + //print("command: {}", .{command}); }, .dyld_main_entry_point => { const command: *const LoadCommand.EntryPoint = @ptrCast(@alignCast(load_command_ptr)); - print("command: {}", .{command}); + _ = command; + //print("command: {}", .{command}); }, .load_dylib => { const command: *const LoadCommand.Dylib = @ptrCast(@alignCast(load_command_ptr)); - print("command: {}", .{command}); - print("Dylib: {s}", .{@as([*:0]const u8, @ptrFromInt(@intFromPtr(load_command_ptr) + @sizeOf(LoadCommand.Dylib)))}); + _ = command; + //print("command: {}", .{command}); + //print("Dylib: {s}", .{@as([*:0]const u8, @ptrFromInt(@intFromPtr(load_command_ptr) + @sizeOf(LoadCommand.Dylib)))}); }, .function_starts => { const command: *const LoadCommand.LinkeditData = @ptrCast(@alignCast(load_command_ptr)); - print("command: {}", .{command}); + _ = command; + //print("command: {}", .{command}); }, .data_in_code => { const command: *const LoadCommand.LinkeditData = @ptrCast(@alignCast(load_command_ptr)); - print("command: {}", .{command}); + _ = command; + //print("command: {}", .{command}); }, .code_signature => { const command: *const LoadCommand.LinkeditData = @ptrCast(@alignCast(load_command_ptr)); - print("command: {}", .{command}); + _ = command; + //print("command: {}", .{command}); }, else => |t| @panic(@tagName(t)), } @@ -666,7 +679,8 @@ pub fn interpretFile(allocator: Allocator, descriptor: Compilation.Module.Descri writer.index = writer.segment_offset + @sizeOf(LoadCommand.Segment64); for (file[16384 + 56 ..][0..48]) |b| { - print("0x{x}, ", .{b}); + _ = b; + //print("0x{x}, ", .{b}); } const chained_fixup_bytes = &.{ 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; diff --git a/src/backend/pe.zig b/src/backend/pe.zig index dc5b0d3..e6d0424 100644 --- a/src/backend/pe.zig +++ b/src/backend/pe.zig @@ -1,6 +1,5 @@ const std = @import("std"); const assert = std.debug.assert; -const print = std.debug.print; const Allocator = std.mem.Allocator; const data_structures = @import("../data_structures.zig"); @@ -22,21 +21,23 @@ pub const Writer = struct { } pub fn writeToMemory(writer: *Writer, image: *const emit.Result) !void { - print("File len: {}", .{writer.in_file.len}); + //print("File len: {}", .{writer.in_file.len}); const dos_header: *const ImageDosHeader = @ptrCast(@alignCast(writer.in_file.ptr)); - print("File address: {}", .{dos_header.file_address_of_new_exe_header}); - print("File: {s}", .{writer.in_file[0x40..]}); + //print("File address: {}", .{dos_header.file_address_of_new_exe_header}); + //print("File: {s}", .{writer.in_file[0x40..]}); for (writer.in_file[0x40..], 0..) |byte, index| { + _ = index; if (byte == 'T') { - print("Index: {}", .{index}); + //print("Index: {}", .{index}); break; } } assert(dos_header.magic_number == ImageDosHeader.magic); // assert(dos_header.file_address_of_new_exe_header == @sizeOf(ImageDosHeader)); - print("{}", .{dos_header}); + //print("{}", .{dos_header}); const file_header: *const ImageFileHeader = @ptrCast(@alignCast(writer.in_file[dos_header.file_address_of_new_exe_header + 4 ..].ptr)); - print("File header: {}", .{file_header}); + _ = file_header; + //print("File header: {}", .{file_header}); writer.append(std.mem.asBytes(&ImageDosHeader{ .file_address_of_new_exe_header = 208, diff --git a/src/backend/x86_64.zig b/src/backend/x86_64.zig index e652aaf..a9eb53e 100644 --- a/src/backend/x86_64.zig +++ b/src/backend/x86_64.zig @@ -87,8 +87,8 @@ const Register = struct { const Class = enum { not_a_register, any, - // gp8, - // gp16, + gp8, + gp16, gp32, gp64, gp64_nosp, @@ -804,31 +804,81 @@ const zero_register_class_descriptor = Register.Class.Descriptor{ const register_class_descriptors = std.EnumArray(Register.Class, Register.Class.Descriptor).init(.{ .not_a_register = zero_register_class_descriptor, .any = zero_register_class_descriptor, + .gp8 = .{ + .size = @sizeOf(u8), + .spill_size = @sizeOf(u8), + .spill_alignment = @sizeOf(u8), + }, + .gp16 = .{ + .size = @sizeOf(u16), + .spill_size = @sizeOf(u16), + .spill_alignment = @sizeOf(u16), + }, .gp32 = .{ - .size = 32, - .spill_size = 32, - .spill_alignment = 32, + .size = @sizeOf(u32), + .spill_size = @sizeOf(u32), + .spill_alignment = @sizeOf(u32), }, .gp64 = .{ - .size = 64, - .spill_size = 64, - .spill_alignment = 64, + .size = @sizeOf(u64), + .spill_size = @sizeOf(u64), + .spill_alignment = @sizeOf(u64), }, .gp64_nosp = .{ - .size = 64, - .spill_size = 64, - .spill_alignment = 64, + .size = @sizeOf(u64), + .spill_size = @sizeOf(u64), + .spill_alignment = @sizeOf(u64), }, .ccr = .{ - .size = 32, - .spill_size = 32, - .spill_alignment = 32, + .size = @sizeOf(u32), + .spill_size = @sizeOf(u32), + .spill_alignment = @sizeOf(u32), }, }); const registers_by_class = RegisterGroupMap.init(.{ .not_a_register = &.{}, .any = &.{}, + .gp8 = &.{ + .al, + .cl, + .dl, + .ah, + .ch, + .dh, + .bl, + .bh, + .sil, + .dil, + .bpl, + .spl, + .r8b, + .r9b, + .r10b, + .r11b, + .r14b, + .r15b, + .r12b, + .r13b, + }, + .gp16 = &.{ + .ax, + .cx, + .dx, + .si, + .di, + .bx, + .bp, + .sp, + .r8w, + .r9w, + .r10w, + .r11w, + .r14w, + .r15w, + .r12w, + .r13w, + }, .gp32 = &.{ .eax, .ecx, @@ -881,6 +931,8 @@ const system_v = CallingConvention{ .argument_registers = RegisterGroupMap.init(.{ .not_a_register = &.{}, .any = &.{}, + .gp8 = &.{}, + .gp16 = &.{}, .gp32 = &system_v_gp32_argument_registers, .gp64 = &system_v_gp64_argument_registers, .gp64_nosp = &.{}, @@ -917,8 +969,8 @@ const ValueType = struct { any = 0, // other = 1, // i1 = 2, - // i8 = 3, - // i16 = 4, + i8 = 3, + i16 = 4, i32 = 5, i64 = 6, // i128 = 7, @@ -934,6 +986,20 @@ const value_types = std.EnumArray(ValueType.Id, ValueType).init(.{ .data_type = .integer, .scalarness = .scalar, }, + .i8 = .{ + .size = @sizeOf(u8), + .element_count = 1, + .element_type = @intFromEnum(ValueType.Id.i8), + .data_type = .integer, + .scalarness = .scalar, + }, + .i16 = .{ + .size = @sizeOf(u16), + .element_count = 1, + .element_type = @intFromEnum(ValueType.Id.i16), + .data_type = .integer, + .scalarness = .scalar, + }, .i32 = .{ .size = @sizeOf(u32), .element_count = 1, @@ -959,6 +1025,8 @@ const value_types = std.EnumArray(ValueType.Id, ValueType).init(.{ const register_classes = std.EnumArray(ValueType.Id, Register.Class).init(.{ .any = .any, + .i8 = .gp8, + .i16 = .gp16, .i32 = .gp32, .i64 = .gp64, .ccr = .ccr, @@ -1076,7 +1144,7 @@ const InstructionSelection = struct { fn loadRegisterFromStackSlot(instruction_selection: *InstructionSelection, mir: *MIR, insert_before_instruction_index: usize, destination_register: Register.Physical, frame_index: u32, register_class: Register.Class, virtual_register: Register.Virtual.Index) !void { _ = virtual_register; const stack_object = instruction_selection.stack_objects.items[frame_index]; - switch (@divExact(stack_object.size, 8)) { + switch (stack_object.size) { @sizeOf(u64) => { switch (register_class) { .gp64 => { @@ -1203,8 +1271,6 @@ const InstructionSelection = struct { // const destination_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index); 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 = switch (integer.value.unsigned == 0) { true => { const instruction_id: Instruction.Id = switch (value_type) { @@ -1534,6 +1600,10 @@ const Instruction = struct { or32mr, or32rr, ret, + shl32mi, + shl32ri, + shr32mi, + shr32ri, sub32mr, sub32rr, sub32rm, @@ -2289,6 +2359,78 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri }, }, }, + .shl32ri = .{ + .opcode = 0xc1, + .operands = &.{ + .{ + .id = .gp32, + .kind = .dst, + }, + .{ + .id = .gp32, + .kind = .src, + }, + .{ + .id = .imm8, + .kind = .src, + }, + }, + .implicit_definitions = &.{.eflags}, + }, + .shr32ri = .{ + .opcode = 0xc1, + .operands = &.{ + .{ + .id = .gp32, + .kind = .dst, + }, + .{ + .id = .gp32, + .kind = .src, + }, + .{ + .id = .imm8, + .kind = .src, + }, + }, + .implicit_definitions = &.{.eflags}, + }, + .shl32mi = .{ + .opcode = 0xc1, + .operands = &.{ + .{ + .id = .i32mem, + .kind = .dst, + }, + .{ + .id = .i32mem, + .kind = .src, + }, + .{ + .id = .imm8, + .kind = .src, + }, + }, + .implicit_definitions = &.{.eflags}, + }, + .shr32mi = .{ + .opcode = 0xc1, + .operands = &.{ + .{ + .id = .i32mem, + .kind = .dst, + }, + .{ + .id = .i32mem, + .kind = .src, + }, + .{ + .id = .imm8, + .kind = .src, + }, + }, + .implicit_definitions = &.{.eflags}, + }, .sub32mr = .{ // .format = .mrm_dest_reg, // right? .opcode = 0x29, @@ -2542,6 +2684,7 @@ pub const MIR = struct { const value_type = resolveType(ir_type); const type_info = value_types.get(value_type); const total_size = type_info.size * stack.count; + assert(total_size <= 8); const frame_index = try mir.createStackObject(instruction_selection, total_size, @intCast(stack.alignment), ir_instruction_index, false); try instruction_selection.stack_map.putNoClobber(allocator, ir_instruction_index, frame_index); } @@ -3112,13 +3255,21 @@ pub const MIR = struct { 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 BinaryOperandKind = enum { + load, + immediate, + rest, }; - const is_right_load = switch (mir.ir.instructions.get(ir_binary_operation.right).u) { - .load => true, - else => false, + + const left_kind: BinaryOperandKind = switch (mir.ir.instructions.get(ir_binary_operation.left).u) { + .load => .load, + .load_integer => .immediate, + else => .rest, + }; + const right_kind: BinaryOperandKind = switch (mir.ir.instructions.get(ir_binary_operation.right).u) { + .load => .load, + .load_integer => .immediate, + else => .rest, }; switch (ir_binary_operation.id) { @@ -3163,7 +3314,7 @@ pub const MIR = struct { }); try instruction_selection.instruction_cache.append(mir.allocator, copy_low); - if (is_right_load) { + if (right_kind == .load) { try instruction_selection.folded_loads.putNoClobber(mir.allocator, ir_binary_operation.right, {}); const instruction_id: Instruction.Id = switch (value_type) { @@ -3254,231 +3405,386 @@ pub const MIR = struct { // const use = mir.ir.instructions.get(use_index); // std.debug.print("Use: {s}\n", .{@tagName(use.u)}); // } + switch (left_kind) { + .load => switch (right_kind) { + .load => { + // 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 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 => .add32rm, + else => unreachable, + }, + .sub => switch (value_type) { + .i32 => .sub32rm, + else => unreachable, + }, + .logical_and => switch (value_type) { + .i32 => .and32rm, + else => unreachable, + }, + .logical_xor => switch (value_type) { + .i32 => .xor32rm, + else => unreachable, + }, + .logical_or => switch (value_type) { + .i32 => .or32rm, + else => unreachable, + }, + .signed_multiply => switch (value_type) { + .i32 => .imul32rm, + else => unreachable, + }, + .signed_divide => unreachable, + .shift_left => unreachable, + .shift_right => unreachable, + }; - if (!is_left_load and is_right_load) { - unreachable; - } else if (is_left_load and !is_right_load) { - 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, - }; + 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; - // 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 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 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 destination_operand = Operand{ + .id = destination_operand_id, + .u = .{ + .register = destination_register, + }, + .flags = .{ + .type = .def, + }, + }; - 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 = .{ + .register = left_register, + }, + .flags = .{}, + }; - const left_operand = Operand{ - .id = left_operand_id, - .u = .{ - .memory = .{ .addressing_mode = left_operand_addressing_mode }, - }, - .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, - }); + 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, - else => unreachable, - }, - .sub => switch (value_type) { - .i32 => .sub32rr, - else => unreachable, - }, - .logical_and => switch (value_type) { - .i32 => .and32rr, - else => unreachable, - }, - .logical_xor => switch (value_type) { - .i32 => .xor32rr, - else => unreachable, - }, - .logical_or => switch (value_type) { - .i32 => .or32rr, - else => unreachable, - }, - .signed_multiply => switch (value_type) { - .i32 => .imul32rr, - else => unreachable, - }, - .signed_divide => unreachable, - }; + try instruction_selection.instruction_cache.append(mir.allocator, binary_op_instruction); - const instruction_descriptor = instruction_descriptors.get(instruction_id); - const left_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.left); - const right_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.right); - 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 destination_operand = Operand{ - .id = destination_operand_id, - .u = .{ - .register = destination_register, + try instruction_selection.updateValueMap(mir.allocator, ir_instruction_index, destination_register, false); }, - .flags = .{ - .type = .def, - }, - }; + .immediate => { + const destination_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index); + const instruction_id: Instruction.Id = switch (ir_binary_operation.id) { + .shift_left => .shl32ri, + .shift_right => .shr32ri, + else => |t| @panic(@tagName(t)), + }; + const instruction_descriptor = instruction_descriptors.get(instruction_id); + // const left_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.left); + 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 left_operand = Operand{ - .id = left_operand_id, - .u = .{ - .register = left_register, - }, - .flags = .{}, - }; + const destination_operand = Operand{ + .id = destination_operand_id, + .u = .{ + .register = destination_register, + }, + .flags = .{ + .type = .def, + }, + }; - const right_operand = Operand{ - .id = right_operand_id, - .u = .{ - .register = right_register, - }, - .flags = .{}, - }; + const left_operand = Operand{ + .id = left_operand_id, + .u = .{ + .register = left_register, + }, + .flags = .{}, + }; - const binary_op_instruction = try mir.buildInstruction(instruction_selection, instruction_id, &.{ - destination_operand, - left_operand, - right_operand, - }); + const right_immediate = mir.ir.instructions.get(ir_binary_operation.right).u.load_integer; + const right_value_type: ValueType.Id = switch (right_immediate.type) { + .i8 => .i8, + else => unreachable, + }; + _ = right_value_type; + const right_operand = Operand{ + .id = .imm8, + .u = .{ + .immediate = right_immediate.value.unsigned, + }, + .flags = .{}, + }; - try instruction_selection.instruction_cache.append(mir.allocator, binary_op_instruction); + const binary_op_instruction = try mir.buildInstruction(instruction_selection, instruction_id, &.{ + destination_operand, + left_operand, + right_operand, + }); - 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) { - .i32 => .add32rm, - else => unreachable, - }, - .sub => switch (value_type) { - .i32 => .sub32rm, - else => unreachable, - }, - .logical_and => switch (value_type) { - .i32 => .and32rm, - else => unreachable, - }, - .logical_xor => switch (value_type) { - .i32 => .xor32rm, - else => unreachable, - }, - .logical_or => switch (value_type) { - .i32 => .or32rm, - else => unreachable, - }, - .signed_multiply => switch (value_type) { - .i32 => .imul32rm, - else => unreachable, - }, - .signed_divide => unreachable, - }; + try instruction_selection.instruction_cache.append(mir.allocator, binary_op_instruction); - 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; - 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, + try instruction_selection.updateValueMap(mir.allocator, ir_instruction_index, destination_register, false); }, - .flags = .{ - .type = .def, + .rest => { + 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, + else => unreachable, + }, + .sub => switch (value_type) { + .i32 => .sub32rr, + else => unreachable, + }, + .logical_and => switch (value_type) { + .i32 => .and32rr, + else => unreachable, + }, + .logical_xor => switch (value_type) { + .i32 => .xor32rr, + else => unreachable, + }, + .logical_or => switch (value_type) { + .i32 => .or32rr, + else => unreachable, + }, + .signed_multiply => switch (value_type) { + .i32 => .imul32rr, + else => unreachable, + }, + .signed_divide => unreachable, + .shift_left => unreachable, + .shift_right => unreachable, + }; + + const instruction_descriptor = instruction_descriptors.get(instruction_id); + const left_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.left); + const right_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.right); + 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 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 = .{ + .register = right_register, + }, + .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); }, - }; + }, + .rest => switch (right_kind) { + .load => unreachable, + .immediate => unreachable, + .rest => { + 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, + else => unreachable, + }, + .sub => switch (value_type) { + .i32 => .sub32rr, + else => unreachable, + }, + .logical_and => switch (value_type) { + .i32 => .and32rr, + else => unreachable, + }, + .logical_xor => switch (value_type) { + .i32 => .xor32rr, + else => unreachable, + }, + .logical_or => switch (value_type) { + .i32 => .or32rr, + else => unreachable, + }, + .signed_multiply => switch (value_type) { + .i32 => .imul32rr, + else => unreachable, + }, + .signed_divide => unreachable, + .shift_left => unreachable, + .shift_right => unreachable, + }; - const left_operand = Operand{ - .id = left_operand_id, - .u = .{ - .register = left_register, + const instruction_descriptor = instruction_descriptors.get(instruction_id); + const left_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.left); + const right_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.right); + 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 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 = .{ + .register = right_register, + }, + .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); }, - .flags = .{}, - }; - - const right_operand = Operand{ - .id = right_operand_id, - .u = .{ - .memory = .{ .addressing_mode = right_operand_addressing_mode }, + }, + .immediate => switch (right_kind) { + .load => unreachable, + .immediate => switch (ir_binary_operation.id) { + else => |t| @panic(@tagName(t)), }, - .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); + .rest => unreachable, + }, } + // if (!is_left_load and is_right_load) { + // unreachable; + // } else if (is_left_load and !is_right_load) { + // 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, + // .shift_left => unreachable, + // .shift_right => unreachable, + // }; + // + // const instruction_descriptor = instruction_descriptors.get(instruction_id); + // // const left_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.left); + // const destination_operand_id = instruction_descriptor.operands[0].id; + // const left_operand_id = instruction_descriptor.operands[1].id; + // const right_operand_id = instruction_descriptor.operands[2].id; + // // const ir_load = mir.ir.loads.get(mir.ir.instructions.get(ir_binary_operation.right).u.load); + // // const right_operand_addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_load.instruction); + // const ir_load = mir.ir.loads.get(mir.ir.instructions.get(ir_binary_operation.left).u.load); + // + // const right_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.right); + // const right_operand = Operand{ + // .id = right_operand_id, + // .u = .{ + // .register = right_register, + // }, + // .flags = .{}, + // }; + // + // const left_operand_addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_load.instruction); + // const destination_operand = Operand{ + // .id = destination_operand_id, + // .u = .{ + // .memory = .{ .addressing_mode = left_operand_addressing_mode }, + // }, + // .flags = .{}, + // }; + // + // const left_operand = Operand{ + // .id = left_operand_id, + // .u = .{ + // .memory = .{ .addressing_mode = left_operand_addressing_mode }, + // }, + // .flags = .{}, + // }; + // + // const binary_op_instruction = try mir.buildInstruction(instruction_selection, instruction_id, &.{ + // destination_operand, + // left_operand, + // right_operand, + // }); + // + // try instruction_selection.instruction_cache.append(mir.allocator, binary_op_instruction); + // } else if (!is_left_load and !is_right_load) { + // } else { + // } }, } }, @@ -3798,6 +4104,7 @@ pub const MIR = struct { const virtual_register = live_register.virtual; const register_class = mir.virtual_registers.get(live_register.virtual).register_class; + logln(.codegen, .register_allocation_problematic_hint, "Hint 1: {?}", .{maybe_hint}); if (maybe_hint) |hint_register| { logln(.codegen, .register_allocation_problematic_hint, "Hint register 1: {s}", .{@tagName(hint_register.index.physical)}); if (hint_register.index == .physical) { @@ -3821,6 +4128,7 @@ pub const MIR = struct { logln(.codegen, .register_allocation_problematic_hint, "Tracing copies for VR{} in instruction #{}", .{ virtual_register.uniqueInteger(), instruction_index.uniqueInteger() }); const maybe_hint2 = register_allocator.traceCopies(mir, instruction_selection, virtual_register); + logln(.codegen, .register_allocation_problematic_hint, "Hint 2: {?}", .{maybe_hint2}); if (maybe_hint2) |hint| { // TODO const allocatable = true; @@ -3855,6 +4163,7 @@ pub const MIR = struct { for (register_class_members) |candidate_register| { if (!register_allocator.isRegisterUsedInInstruction(candidate_register, look_at_physical_register_uses)) { const spill_cost = register_allocator.computeSpillCost(candidate_register); + logln(.codegen, .register_allocation_problematic_hint, "Candidate: {s}. Spill cost: {}", .{ @tagName(candidate_register), spill_cost }); if (spill_cost == 0) { register_allocator.assignVirtualToPhysicalRegister(live_register, candidate_register); @@ -3891,7 +4200,10 @@ pub const MIR = struct { .free => 0, .preassigned => SpillCost.impossible, .virtual => |virtual_register_index| blk: { - const sure_spill = register_allocator.stack_slots.get(virtual_register_index) != null or register_allocator.live_virtual_registers.get(virtual_register_index).?.live_out; + const stack_slot = register_allocator.stack_slots.get(virtual_register_index) != null; + const live_out = register_allocator.live_virtual_registers.get(virtual_register_index).?.live_out; + log(.codegen, .register_allocation_problematic_hint, "Register {s} has stack slot: {}. Live out: {}", .{ @tagName(physical_register), stack_slot, live_out }); + const sure_spill = stack_slot or live_out; break :blk if (sure_spill) SpillCost.clean else SpillCost.dirty; }, .livein => unreachable, @@ -4056,10 +4368,12 @@ pub const MIR = struct { return switch (state.*) { .free => false, .preassigned => blk: { + logln(.codegen, .register_allocation_problematic_hint, "Freeing preassigned {s} at displacePhysicalRegister", .{@tagName(physical_register)}); state.* = .free; break :blk true; }, .virtual => |virtual_register| blk: { + logln(.codegen, .register_allocation_problematic_hint, "Freeing assigned {s} at displacePhysicalRegister", .{@tagName(physical_register)}); const live_reg = register_allocator.live_virtual_registers.getPtr(virtual_register).?; const before = mir.getNextInstructionIndex(instruction_index); try register_allocator.reload(mir, instruction_selection, before, virtual_register, physical_register); @@ -4099,7 +4413,10 @@ pub const MIR = struct { const state = register_allocator.register_states.getPtr(physical_register); switch (state.*) { .free => unreachable, - .preassigned => state.* = .free, + .preassigned => { + logln(.codegen, .register_allocation_problematic_hint, "Freeing preassigned {s} at freePhysicalRegister", .{@tagName(physical_register)}); + state.* = .free; + }, .virtual => |virtual_register_index| { const live_register = register_allocator.live_virtual_registers.getPtr(virtual_register_index).?; assert(live_register.physical == physical_register); @@ -4255,37 +4572,35 @@ pub const MIR = struct { } fn reloadAtBegin(register_allocator: *RegisterAllocator, mir: *MIR, instruction_selection: *InstructionSelection, basic_block: BasicBlock.Index) !void { - _ = instruction_selection; - _ = mir; - _ = register_allocator; _ = basic_block; - // if (register_allocator.live_virtual_registers.entries.len > 0) { - // // TODO: basic block liveins (regmasks?) - // - // const live_registers = register_allocator.live_virtual_registers.values(); - // print("Live register count: {}", .{live_registers.len}); - // - // for (live_registers) |live_register| { - // const physical_register = live_register.physical; - // if (physical_register == .no_register) { - // continue; - // } - // - // if (register_allocator.register_states.get(physical_register) == .livein) { - // unreachable; - // } - // - // // assert? - // - // const virtual_register = live_register.virtual; - // if (false) { - // unreachable; - // } else { - // try register_allocator.reload(mir, instruction_selection, 0, virtual_register, physical_register); - // } - // } - // unreachable; - // } + if (register_allocator.live_virtual_registers.entries.len > 0) { + // TODO: basic block liveins (regmasks?) + + const live_registers = register_allocator.live_virtual_registers.values(); + // print("Live register count: {}", .{live_registers.len}); + + for (live_registers) |live_register| { + const physical_register = live_register.physical; + if (physical_register == .no_register) { + continue; + } + + if (register_allocator.register_states.get(physical_register) == .livein) { + unreachable; + } + + // assert? + + const virtual_register = live_register.virtual; + if (false) { + unreachable; + } else { + try register_allocator.reload(mir, instruction_selection, 0, virtual_register, physical_register); + } + } + + register_allocator.live_virtual_registers.clearRetainingCapacity(); + } } }; @@ -4523,6 +4838,8 @@ pub const MIR = struct { } } + try register_allocator.reloadAtBegin(mir, instruction_selection, block_index); + for (register_allocator.coalesced.items) |coalesced| { for (block.instructions.items, 0..) |instruction_index, i| { if (coalesced.eq(instruction_index)) { @@ -4616,6 +4933,7 @@ pub const MIR = struct { for (stack_objects) |stack_object| { result += @intCast(stack_object.size); result = std.mem.alignForward(u32, result, stack_object.alignment); + // logln(.codegen, .register_allocation_problematic_hint, "Stack size: {} after Stack object: {}", .{ result, stack_object }); } return result; @@ -5170,6 +5488,68 @@ pub const MIR = struct { }, } }, + .shl32ri, + .shr32ri, + .shl32mi, + .shr32mi, + => { + 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); + + const left_operand_index = instruction.operands.items[1]; + const left_operand = mir.operands.get(left_operand_index); + + switch (destination_operand.u) { + .memory => switch (destination_operand.u.memory.addressing_mode.base) { + .register_base => unreachable, + .frame_index => |frame_index| { + assert(left_operand.u.memory.addressing_mode.base.frame_index == frame_index); + const modrm = ModRm{ + .rm = @intFromEnum(Encoding.GP64.bp), + .reg = switch (instruction.id) { + .shl32mi => 4, + .shr32mi => 5, + else => unreachable, + }, + .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); + }, + }, + .register => { + const register = getGP32Encoding(destination_operand.*); + // std.debug.print("Register: {}\n", .{register}); + const modrm = ModRm{ + .rm = @intCast(@intFromEnum(register)), + .reg = switch (instruction.id) { + .shl32ri => 4, + .shr32ri => 5, + else => unreachable, + }, + .mod = @as(u2, @intFromBool(true)) << 1 | @intFromBool(true), + }; + try image.section_manager.appendCodeByte(@bitCast(modrm)); + }, + else => unreachable, + } + + const source_operand_index = instruction.operands.items[2]; + const source_operand = mir.operands.get(source_operand_index); + assert(source_operand.id == .imm8); + const source_immediate: u8 = @intCast(source_operand.u.immediate); + + try image.section_manager.appendCodeByte(source_immediate); + }, else => |t| @panic(@tagName(t)), } diff --git a/src/frontend/semantic_analyzer.zig b/src/frontend/semantic_analyzer.zig index 6d4b3ef..27e2076 100644 --- a/src/frontend/semantic_analyzer.zig +++ b/src/frontend/semantic_analyzer.zig @@ -202,7 +202,7 @@ const Analyzer = struct { const declaration_index = try analyzer.symbolDeclaration(scope_index, statement_node_index, .local); const declaration = analyzer.module.declarations.get(declaration_index); const init_value = analyzer.module.values.get(declaration.init_value); - switch (init_value.isComptime() and declaration.mutability == .@"const") { + switch (init_value.isComptime(analyzer.module) and declaration.mutability == .@"const") { // Dont add comptime declaration statements true => continue, false => { @@ -461,7 +461,7 @@ const Analyzer = struct { fn processAssignment(analyzer: *Analyzer, scope_index: Scope.Index, node_index: Node.Index) !Value { const node = analyzer.getScopeNode(scope_index, node_index); assert(node.id == .assign); - const assignment = switch (node.left.invalid) { + switch (node.left.invalid) { // In an assignment, the node being invalid means a discarding underscore, like this: ```_ = result``` true => { var result = Value{ @@ -477,23 +477,25 @@ const Analyzer = struct { false => { // const id = analyzer.tokenIdentifier(.token); // logln("id: {s}\n", .{id}); - // const left = try analyzer.expression(scope_index, ExpectType.none, statement_node.left); + const left = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, node.left); + const right = try analyzer.unresolvedAllocate(scope_index, ExpectType{ + .type_index = left.ptr.getType(analyzer.module), + }, node.right); - // if (analyzer.module.values.get(left).isComptime() and analyzer.module.values.get(right).isComptime()) { - // unreachable; - // } else { - // const assignment_index = try analyzer.module.assignments.append(analyzer.allocator, .{ - // .store = result.left, - // .load = result.right, - // }); - // return assignment_index; - // } - unreachable; + if (left.ptr.isComptime(analyzer.module) and right.ptr.isComptime(analyzer.module)) { + unreachable; + } else { + const assignment = try analyzer.module.assignments.append(analyzer.allocator, .{ + .store = left.index, + .load = right.index, + }); + + return Value{ + .assign = assignment.index, + }; + } }, - }; - _ = assignment; - - unreachable; + } } fn processReturn(analyzer: *Analyzer, scope_index: Scope.Index, expect_type: ExpectType, node_index: Node.Index) !Value { @@ -524,29 +526,46 @@ 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 binary_operation_id: Compilation.BinaryOperation.Id = switch (node.id) { + .add => .add, + .sub => .sub, + .logical_and => .logical_and, + .logical_xor => .logical_xor, + .logical_or => .logical_or, + .multiply => .multiply, + .divide => .divide, + .shift_left => .shift_left, + .shift_right => .shift_right, + else => |t| @panic(@tagName(t)), + }; + + const right_expect_type: ExpectType = switch (binary_operation_id) { + .add, + .sub, + .logical_and, + .logical_xor, + .logical_or, + .multiply, + .divide, + => expect_type, + .shift_left, + .shift_right, + => ExpectType{ + .type_index = Type.u8, + }, + }; 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 right_allocation = try analyzer.unresolvedAllocate(scope_index, right_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; - } + _ = right_type; 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, - .logical_and => .logical_and, - .logical_xor => .logical_xor, - .logical_or => .logical_or, - .multiply => .multiply, - .divide => .divide, - else => |t| @panic(@tagName(t)), - }, + .id = binary_operation_id, }); return .{ @@ -606,11 +625,11 @@ const Analyzer = struct { } logln(.sema, .identifier, "Declaration resolved as: {}\n", .{init_value}); - logln(.sema, .identifier, "Declaration mutability: {s}. Is comptime: {}\n", .{ @tagName(declaration.mutability), init_value.isComptime() }); + logln(.sema, .identifier, "Declaration mutability: {s}. Is comptime: {}\n", .{ @tagName(declaration.mutability), init_value.isComptime(analyzer.module) }); const typecheck_result = try analyzer.typeCheck(expect_type, declaration.type); - if (init_value.isComptime() and declaration.mutability == .@"const") { + if (init_value.isComptime(analyzer.module) and declaration.mutability == .@"const") { assert(!declaration.init_value.invalid); assert(typecheck_result == .success); return declaration.init_value; @@ -1017,6 +1036,8 @@ const Analyzer = struct { .logical_or, .multiply, .divide, + .shift_left, + .shift_right, => 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)), diff --git a/src/frontend/syntactic_analyzer.zig b/src/frontend/syntactic_analyzer.zig index 6e8c0d8..43cbcfd 100644 --- a/src/frontend/syntactic_analyzer.zig +++ b/src/frontend/syntactic_analyzer.zig @@ -160,6 +160,8 @@ pub const Node = packed struct(u128) { logical_or = 66, multiply = 67, divide = 68, + shift_left = 69, + shift_right = 70, }; }; @@ -721,6 +723,8 @@ const Analyzer = struct { logical_or, multiply, divide, + shift_left, + shift_right, }; const operator_precedence = std.EnumArray(PrecedenceOperator, i32).init(.{ @@ -733,6 +737,8 @@ const Analyzer = struct { .logical_or = 40, .multiply = 70, .divide = 70, + .shift_left = 50, + .shift_right = 50, }); const operator_associativity = std.EnumArray(PrecedenceOperator, Associativity).init(.{ @@ -745,6 +751,8 @@ const Analyzer = struct { .logical_or = .left, .multiply = .left, .divide = .left, + .shift_left = .left, + .shift_right = .left, }); const operator_node_id = std.EnumArray(PrecedenceOperator, Node.Id).init(.{ @@ -757,6 +765,8 @@ const Analyzer = struct { .logical_or = .logical_or, .multiply = .multiply, .divide = .divide, + .shift_left = .shift_left, + .shift_right = .shift_right, }); fn expressionPrecedence(analyzer: *Analyzer, minimum_precedence: i32) !Node.Index { @@ -825,6 +835,14 @@ const Analyzer = struct { .equal => unreachable, else => .divide, }, + .less => switch (next_token_id) { + .less => .shift_left, + else => unreachable, + }, + .greater => switch (next_token_id) { + .greater => .shift_right, + else => unreachable, + }, else => |t| @panic(@tagName(t)), }; } else { @@ -845,7 +863,7 @@ const Analyzer = struct { } const operator_token = analyzer.token_i; - const extra_token = switch (operator) { + const extra_tokens: u32 = switch (operator) { .add, .sub, .logical_and, @@ -853,13 +871,15 @@ const Analyzer = struct { .logical_or, .multiply, .divide, - => false, + => 0, .compare_equal, .compare_not_equal, - => true, + .shift_right, + .shift_left, + => 1, // else => |t| @panic(@tagName(t)), }; - analyzer.token_i += @as(u32, 1) + @intFromBool(extra_token); + analyzer.token_i += @as(u32, 1) + extra_tokens; // TODO: fix this const right = try analyzer.expressionPrecedence(precedence + 1); diff --git a/test/imul/main.nat b/test/imul/main.nat index e28151e..f298a20 100644 --- a/test/imul/main.nat +++ b/test/imul/main.nat @@ -1,5 +1,5 @@ const main = fn () s32 { const a: s32 = 5; const b: s32 = 4; - return 5 * 4 - a * b; + return a * b - a * b; } diff --git a/test/shifts/main.nat b/test/shifts/main.nat new file mode 100644 index 0000000..83cf8e7 --- /dev/null +++ b/test/shifts/main.nat @@ -0,0 +1,7 @@ +const main = fn() s32 { + const x: u32 = 1; + x = x << 5; + x = x >> 5; + const b: u32 = 1; + return x - b; +}