This commit is contained in:
David Gonzalez Martin 2023-09-18 07:31:26 -06:00
parent 15a7df3f14
commit 48c3b5e224
8 changed files with 822 additions and 238 deletions

17
.vscode/launch.json vendored
View File

@ -5,13 +5,22 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"type": "cppvsdbg", "type": "lldb",
"request": "launch", "request": "launch",
"name": "Debug", "name": "Launch",
"program": "${workspaceFolder}/zig-out/bin/compiler.exe", "program": "${workspaceFolder}/zig-out/bin/compiler",
"args": [], "args": [],
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
"preLaunchTask": "zig build" "preLaunchTask": "zig build"
} },
// {
// "type": "cppvsdbg",
// "request": "launch",
// "name": "Debug",
// "program": "${workspaceFolder}/zig-out/bin/compiler.exe",
// "args": [],
// "cwd": "${workspaceFolder}",
// "preLaunchTask": "zig build"
// }
] ]
} }

View File

@ -21,6 +21,7 @@ const syntactic_analyzer = @import("frontend/syntactic_analyzer.zig");
const Node = syntactic_analyzer.Node; const Node = syntactic_analyzer.Node;
const semantic_analyzer = @import("frontend/semantic_analyzer.zig"); const semantic_analyzer = @import("frontend/semantic_analyzer.zig");
const intermediate_representation = @import("backend/intermediate_representation.zig"); const intermediate_representation = @import("backend/intermediate_representation.zig");
const emit = @import("backend/emit.zig");
test { test {
_ = lexical_analyzer; _ = lexical_analyzer;
@ -59,7 +60,8 @@ pub fn init(allocator: Allocator) !*Compilation {
pub const Struct = struct { pub const Struct = struct {
scope: Scope.Index, scope: Scope.Index,
initialization: Value.Index, fields: ArrayList(Field.Index) = .{},
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
}; };
@ -69,6 +71,7 @@ pub const Type = union(enum) {
noreturn, noreturn,
bool, bool,
integer: Integer, integer: Integer,
@"struct": Struct.Index,
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
}; };
@ -85,16 +88,29 @@ pub const Integer = struct {
/// A scope contains a bunch of declarations /// A scope contains a bunch of declarations
pub const Scope = struct { pub const Scope = struct {
parent: Scope.Index, parent: Scope.Index,
type: Type.Index, type: Type.Index = Type.Index.invalid,
declarations: AutoHashMap(u32, Declaration.Index) = .{}, declarations: AutoHashMap(u32, Declaration.Index) = .{},
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
}; };
pub const Declaration = union(enum) { pub const ScopeType = enum(u1) {
unresolved: Node.Index, local = 0,
struct_type: Struct, global = 1,
};
pub const Mutability = enum(u1) {
@"const",
@"var",
};
pub const Declaration = struct {
scope_type: ScopeType,
mutability: Mutability,
init_value: Value.Index,
name: []const u8,
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
}; };
@ -111,12 +127,17 @@ pub const Function = struct {
pub const Index = Prototype.List.Index; pub const Index = Prototype.List.Index;
}; };
pub fn getBodyBlock(function: Function, module: *Module) *Block {
return module.blocks.get(function.body);
}
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
}; };
pub const Block = struct { pub const Block = struct {
foo: u32 = 0, statements: ArrayList(Value.Index) = .{},
reaches_end: bool,
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
}; };
@ -129,27 +150,61 @@ pub const Field = struct {
}; };
pub const Loop = struct { pub const Loop = struct {
foo: u32 = 0, condition: Value.Index,
body: Value.Index,
breaks: bool,
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
}; };
pub const Value = struct { const Runtime = struct {
type: union(enum) { foo: u32 = 0,
declaration: Declaration.Index, };
bool_true,
bool_false, const Unresolved = struct {
loop: Loop.Index, node_index: Node.Index,
function: Function.Index, };
},
is_const: bool, pub const Assignment = struct {
is_comptime: bool, store: Value.Index,
load: Value.Index,
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
}; };
pub const Value = union(enum) {
unresolved: Unresolved,
declaration: Declaration.Index,
void,
bool: bool,
undefined,
loop: Loop.Index,
function: Function.Index,
block: Block.Index,
runtime: Runtime,
assign: Assignment.Index,
type: Type.Index,
pub const List = BlockList(@This());
pub const Index = List.Index;
pub fn isComptime(value: Value) bool {
return switch (value) {
.bool, .void, .undefined, .function => true,
else => false,
};
}
pub fn getType(value: *Value) !void {
switch (value.*) {
else => |t| @panic(@tagName(t)),
}
unreachable;
}
};
pub const Module = struct { pub const Module = struct {
main_package: *Package, main_package: *Package,
import_table: StringArrayHashMap(*File) = .{}, import_table: StringArrayHashMap(*File) = .{},
@ -165,6 +220,7 @@ pub const Module = struct {
types: BlockList(Type) = .{}, types: BlockList(Type) = .{},
blocks: BlockList(Block) = .{}, blocks: BlockList(Block) = .{},
loops: BlockList(Loop) = .{}, loops: BlockList(Loop) = .{},
assignments: BlockList(Assignment) = .{},
pub const Descriptor = struct { pub const Descriptor = struct {
main_package_path: []const u8, main_package_path: []const u8,
@ -354,7 +410,12 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) !
const main_declaration = try semantic_analyzer.initialize(compilation, module, packages[0]); const main_declaration = try semantic_analyzer.initialize(compilation, module, packages[0]);
try intermediate_representation.initialize(compilation, module, packages[0], main_declaration); var ir = try intermediate_representation.initialize(compilation, module, packages[0], main_declaration);
switch (@import("builtin").cpu.arch) {
.x86_64 => |arch| try emit.get(arch).initialize(compilation.base_allocator, &ir),
else => {},
}
} }
fn generateAST() !void {} fn generateAST() !void {}

View File

@ -6,7 +6,13 @@ const assert = std.debug.assert;
const expect = std.testing.expect; const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual; const expectEqual = std.testing.expectEqual;
const ir = @import("ir.zig"); const ir = @import("intermediate_representation.zig");
const data_structures = @import("../data_structures.zig");
const ArrayList = data_structures.ArrayList;
const AutoHashMap = data_structures.AutoHashMap;
const jit_callconv = .SysV;
const Section = struct { const Section = struct {
content: []align(page_size) u8, content: []align(page_size) u8,
@ -39,8 +45,13 @@ const Result = struct {
const windows = std.os.windows; const windows = std.os.windows;
break :blk @as([*]align(0x1000) u8, @ptrCast(@alignCast(try windows.VirtualAlloc(null, size, windows.MEM_COMMIT | windows.MEM_RESERVE, windows.PAGE_EXECUTE_READWRITE))))[0..size]; break :blk @as([*]align(0x1000) u8, @ptrCast(@alignCast(try windows.VirtualAlloc(null, size, windows.MEM_COMMIT | windows.MEM_RESERVE, windows.PAGE_EXECUTE_READWRITE))))[0..size];
}, },
.linux => blk: { .linux, .macos => |os_tag| blk: {
const protection_flags = std.os.PROT.READ | std.os.PROT.WRITE | if (flags.executable) std.os.PROT.EXEC else 0; const execute_flag: switch (os_tag) {
.linux => u32,
.macos => c_int,
else => unreachable,
} = if (flags.executable) std.os.PROT.EXEC else 0;
const protection_flags: u32 = @intCast(std.os.PROT.READ | std.os.PROT.WRITE | execute_flag);
const mmap_flags = std.os.MAP.ANONYMOUS | std.os.MAP.PRIVATE; const mmap_flags = std.os.MAP.ANONYMOUS | std.os.MAP.PRIVATE;
break :blk std.os.mmap(null, size, protection_flags, mmap_flags, -1, 0); break :blk std.os.mmap(null, size, protection_flags, mmap_flags, -1, 0);
@ -60,16 +71,163 @@ const Result = struct {
image.sections.text.index += 1; image.sections.text.index += 1;
} }
fn getEntryPoint(image: *const Result, comptime Function: type) *const Function { fn getEntryPoint(image: *const Result, comptime FunctionType: type) *const FunctionType {
comptime { comptime {
assert(@typeInfo(Function) == .Fn); assert(@typeInfo(FunctionType) == .Fn);
} }
assert(image.sections.text.content.len > 0); assert(image.sections.text.content.len > 0);
return @as(*const Function, @ptrCast(&image.sections.text.content[image.entry_point])); return @as(*const FunctionType, @ptrCast(&image.sections.text.content[image.entry_point]));
} }
}; };
const SimpleRelocation = struct {
source: u32,
write_offset: u8,
instruction_len: u8,
size: u8,
};
const RelocationManager = struct {
in_function_relocations: data_structures.AutoHashMap(ir.BasicBlock.Index, ArrayList(SimpleRelocation)) = .{},
};
pub fn get(comptime arch: std.Target.Cpu.Arch) type {
const backend = switch (arch) {
.x86_64 => @import("x86_64.zig"),
else => @compileError("Architecture not supported"),
};
_ = backend;
const Function = struct {
block_byte_counts: ArrayList(u16),
byte_count: u32 = 0,
relocations: ArrayList(Relocation) = .{},
block_map: AutoHashMap(ir.BasicBlock.Index, u32) = .{},
const Relocation = struct {
source: ir.BasicBlock.Index,
destination: ir.BasicBlock.Index,
offset_offset: u8,
preferred_instruction_len: u8,
};
};
const InstructionSelector = struct {
functions: ArrayList(Function),
};
_ = InstructionSelector;
return struct {
pub fn initialize(allocator: Allocator, intermediate: *ir.Result) !void {
_ = intermediate;
_ = allocator;
// var function_iterator = intermediate.functions.iterator();
// var instruction_selector = InstructionSelector{
// .functions = try ArrayList(Function).initCapacity(allocator, intermediate.functions.len),
// };
// while (function_iterator.next()) |ir_function| {
// const function = instruction_selector.functions.addOneAssumeCapacity();
// function.* = .{
// .block_byte_counts = try ArrayList(u16).initCapacity(allocator, ir_function.blocks.items.len),
// };
// try function.block_map.ensureTotalCapacity(allocator, @intCast(ir_function.blocks.items.len));
// for (ir_function.blocks.items, 0..) |block_index, index| {
// function.block_map.putAssumeCapacity(allocator, block_index, @intCast(index));
// }
//
// for (ir_function.blocks.items) |block_index| {
// const block = intermediate.blocks.get(block_index);
// var block_byte_count: u16 = 0;
// for (block.instructions.items) |instruction_index| {
// const instruction = intermediate.instructions.get(instruction_index).*;
// switch (instruction) {
// .phi => unreachable,
// .ret => unreachable,
// .jump => {
// block_byte_count += 2;
// },
// }
// }
// function.block_byte_counts.appendAssumeCapacity(block_byte_count);
// }
// }
// unreachable;
}
};
}
pub fn initialize(allocator: Allocator, intermediate: *ir.Result) !void {
_ = allocator;
var result = try Result.create();
_ = result;
var relocation_manager = RelocationManager{};
_ = relocation_manager;
var function_iterator = intermediate.functions.iterator();
_ = function_iterator;
// while (function_iterator.next()) |function| {
// defer relocation_manager.in_function_relocations.clearRetainingCapacity();
//
// for (function.blocks.items) |block_index| {
// if (relocation_manager.in_function_relocations.getPtr(block_index)) |relocations| {
// const current_offset: i64 = @intCast(result.sections.text.index);
// _ = current_offset;
// for (relocations.items) |relocation| switch (relocation.size) {
// inline @sizeOf(u8), @sizeOf(u32) => |relocation_size| {
// const Elem = switch (relocation_size) {
// @sizeOf(u8) => u8,
// @sizeOf(u32) => u32,
// else => unreachable,
// };
// const Ptr = *align(1) Elem;
// _ = Ptr;
// const relocation_slice = result.sections.text.content[relocation.source + relocation.write_offset ..][0..relocation_size];
// _ = relocation_slice;
// // std.math.cast(
// //
// unreachable;
// },
// else => unreachable,
// };
// // const ptr: *align(1) u32 = @ptrCast(&result.sections.text[relocation_source..][0..@sizeOf(u32)]);
// // ptr.* =
// // try relocations.append(allocator, @intCast(result.sections.text[));
// }
//
// const block = intermediate.blocks.get(block_index);
//
// for (block.instructions.items) |instruction_index| {
// const instruction = intermediate.instructions.get(instruction_index);
// switch (instruction.*) {
// .jump => |jump_index| {
// const jump = intermediate.jumps.get(jump_index);
// assert(@as(u32, @bitCast(jump.source)) == @as(u32, @bitCast(block_index)));
// const relocation_index = result.sections.text.index + 1;
// if (@as(u32, @bitCast(jump.destination)) <= @as(u32, @bitCast(jump.source))) {
// unreachable;
// } else {
// result.appendCode(&(.{jmp_rel_32} ++ .{0} ** @sizeOf(u32)));
// const lookup_result = try relocation_manager.in_function_relocations.getOrPut(allocator, jump.destination);
// if (!lookup_result.found_existing) {
// lookup_result.value_ptr.* = .{};
// }
//
// try lookup_result.value_ptr.append(allocator, .{
// .source = @intCast(relocation_index),
// .write_offset = @sizeOf(u8),
// .instruction_len = @sizeOf(u8) + @sizeOf(u32),
// .size = @sizeOf(u32),
// });
// }
// },
// else => |t| @panic(@tagName(t)),
// }
// }
// }
// unreachable;
// }
// unreachable;
}
const Rex = enum(u8) { const Rex = enum(u8) {
b = upper_4_bits | (1 << 0), b = upper_4_bits | (1 << 0),
x = upper_4_bits | (1 << 1), x = upper_4_bits | (1 << 1),
@ -115,6 +273,7 @@ const prefix_rep = 0xf3;
const prefix_rex_w = [1]u8{@intFromEnum(Rex.w)}; const prefix_rex_w = [1]u8{@intFromEnum(Rex.w)};
const prefix_16_bit_operand = [1]u8{0x66}; const prefix_16_bit_operand = [1]u8{0x66};
const jmp_rel_32 = 0xe9;
const ret = 0xc3; const ret = 0xc3;
const mov_a_imm = [1]u8{0xb8}; const mov_a_imm = [1]u8{0xb8};
const mov_reg_imm8: u8 = 0xb0; const mov_reg_imm8: u8 = 0xb0;
@ -142,7 +301,7 @@ test "ret void" {
var image = try Result.create(); var image = try Result.create();
image.appendCodeByte(ret); image.appendCodeByte(ret);
const function_pointer = image.getEntryPoint(fn () callconv(.C) void); const function_pointer = image.getEntryPoint(fn () callconv(jit_callconv) void);
function_pointer(); function_pointer();
} }
@ -167,7 +326,7 @@ test "ret integer" {
movAImm(&image, expected_number); movAImm(&image, expected_number);
image.appendCodeByte(ret); image.appendCodeByte(ret);
const function_pointer = image.getEntryPoint(fn () callconv(.C) Int); const function_pointer = image.getEntryPoint(fn () callconv(jit_callconv) Int);
const result = function_pointer(); const result = function_pointer();
try expect(result == expected_number); try expect(result == expected_number);
} }
@ -216,7 +375,7 @@ test "ret integer argument" {
movRmR(&image, Int, .a, .di); movRmR(&image, Int, .a, .di);
image.appendCodeByte(ret); image.appendCodeByte(ret);
const functionPointer = image.getEntryPoint(fn (Int) callconv(.C) Int); const functionPointer = image.getEntryPoint(fn (Int) callconv(jit_callconv) Int);
const result = functionPointer(number); const result = functionPointer(number);
try expectEqual(number, result); try expectEqual(number, result);
} }
@ -246,7 +405,7 @@ test "ret sub arguments" {
subRmR(&image, Int, .a, .si); subRmR(&image, Int, .a, .si);
image.appendCodeByte(ret); image.appendCodeByte(ret);
const functionPointer = image.getEntryPoint(fn (Int, Int) callconv(.C) Int); const functionPointer = image.getEntryPoint(fn (Int, Int) callconv(jit_callconv) Int);
const result = functionPointer(a, b); const result = functionPointer(a, b);
try expectEqual(a - b, result); try expectEqual(a - b, result);
} }
@ -328,7 +487,7 @@ fn TestIntegerBinaryOperation(comptime T: type) type {
dstRmSrcR(&image, T, test_case.opcode, .a, .si); dstRmSrcR(&image, T, test_case.opcode, .a, .si);
image.appendCodeByte(ret); image.appendCodeByte(ret);
const functionPointer = image.getEntryPoint(fn (T, T) callconv(.C) T); const functionPointer = image.getEntryPoint(fn (T, T) callconv(jit_callconv) T);
const expected = test_case.callback(a, b); const expected = test_case.callback(a, b);
const result = functionPointer(a, b); const result = functionPointer(a, b);
if (should_log) { if (should_log) {
@ -350,7 +509,7 @@ test "call after" {
@as(*align(1) u32, @ptrCast(&image.sections.text.content[jump_patch_offset])).* = @intCast(jump_target - jump_source); @as(*align(1) u32, @ptrCast(&image.sections.text.content[jump_patch_offset])).* = @intCast(jump_target - jump_source);
image.appendCodeByte(ret); image.appendCodeByte(ret);
const functionPointer = image.getEntryPoint(fn () callconv(.C) void); const functionPointer = image.getEntryPoint(fn () callconv(jit_callconv) void);
functionPointer(); functionPointer();
} }
@ -369,7 +528,7 @@ test "call before" {
image.appendCode(&second_call); image.appendCode(&second_call);
image.appendCodeByte(ret); image.appendCodeByte(ret);
const functionPointer = image.getEntryPoint(fn () callconv(.C) void); const functionPointer = image.getEntryPoint(fn () callconv(jit_callconv) void);
functionPointer(); functionPointer();
} }

View File

@ -1,9 +1,243 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const print = std.debug.print;
const Compilation = @import("../Compilation.zig"); const Compilation = @import("../Compilation.zig");
const Module = Compilation.Module; const Module = Compilation.Module;
const Package = Compilation.Package; const Package = Compilation.Package;
pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, main_declaration: Compilation.Declaration.Index) !void {
_ = main_declaration; const data_structures = @import("../data_structures.zig");
const ArrayList = data_structures.ArrayList;
const BlockList = data_structures.BlockList;
pub const Result = struct {
functions: BlockList(Function) = .{},
blocks: BlockList(BasicBlock) = .{},
instructions: BlockList(Instruction) = .{},
jumps: BlockList(Jump) = .{},
};
pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, main_file: Compilation.Type.Index) !Result {
_ = main_file;
_ = package; _ = package;
_ = module; print("\nFunction count: {}\n", .{module.functions.len});
_ = compilation;
var function_iterator = module.functions.iterator();
var builder = Builder{
.allocator = compilation.base_allocator,
.module = module,
};
while (function_iterator.next()) |sema_function| {
print("\nFunction: {}\n", .{sema_function});
try builder.function(sema_function);
}
return builder.ir;
} }
pub const BasicBlock = struct {
instructions: ArrayList(Instruction.Index) = .{},
incomplete_phis: ArrayList(Instruction.Index) = .{},
filled: bool = false,
sealed: bool = false,
pub const List = BlockList(@This());
pub const Index = List.Index;
fn seal(basic_block: *BasicBlock) void {
for (basic_block.incomplete_phis.items) |incomplete_phi| {
_ = incomplete_phi;
unreachable;
}
basic_block.sealed = true;
}
};
const Instruction = union(enum) {
jump: Jump.Index,
phi: Phi.Index,
ret: Ret,
pub const List = BlockList(@This());
pub const Index = List.Index;
};
const Phi = struct {
foo: u32 = 0,
pub const List = BlockList(@This());
pub const Index = List.Index;
};
const Ret = struct {
value: Instruction.Index,
};
pub const Jump = struct {
source: BasicBlock.Index,
destination: BasicBlock.Index,
pub const List = BlockList(@This());
pub const Index = List.Index;
};
const Function = struct {
blocks: ArrayList(BasicBlock.Index) = .{},
pub const List = BlockList(@This());
pub const Index = List.Index;
};
pub const Builder = struct {
allocator: Allocator,
ir: Result = .{},
module: *Module,
current_basic_block: BasicBlock.Index = BasicBlock.Index.invalid,
current_function_index: Function.Index = Function.Index.invalid,
fn function(builder: *Builder, sema_function: Compilation.Function) !void {
builder.current_function_index = try builder.ir.functions.append(builder.allocator, .{});
// TODO: arguments
builder.current_basic_block = try builder.newBlock();
const return_type = builder.module.types.get(builder.module.function_prototypes.get(sema_function.prototype).return_type);
const is_noreturn = return_type.* == .noreturn;
if (!is_noreturn) {
const exit_block = try builder.newBlock();
const phi = try builder.appendToBlock(exit_block, .{
.phi = Phi.Index.invalid,
});
const ret = try builder.appendToBlock(exit_block, .{
.ret = .{
.value = phi,
},
});
_ = ret;
}
const sema_block = sema_function.getBodyBlock(builder.module);
try builder.block(sema_block, .{ .emit_exit_block = !is_noreturn });
try builder.dumpFunction(std.io.getStdErr().writer(), builder.current_function_index);
}
fn dumpFunction(builder: *Builder, writer: anytype, index: Function.Index) !void {
const f = builder.ir.functions.get(index);
try writer.writeAll("Hello world!\n");
print("Function blocks: {}\n", .{f.blocks.items.len});
var function_instruction_index: usize = 0;
for (f.blocks.items, 0..) |block_index, function_block_index| {
print("#{}:\n", .{function_block_index});
const function_block = builder.ir.blocks.get(block_index);
for (function_block.instructions.items) |instruction_index| {
const instruction = builder.ir.instructions.get(instruction_index);
print("%{}: {}\n", .{ function_instruction_index, instruction });
function_instruction_index += 1;
}
print("\n", .{});
}
}
fn blockInsideBasicBlock(builder: *Builder, sema_block: *Compilation.Block, block_index: BasicBlock.Index) !BasicBlock.Index {
builder.current_basic_block = block_index;
try builder.block(sema_block, .{});
return builder.current_basic_block;
}
const BlockOptions = packed struct {
emit_exit_block: bool = true,
};
fn block(builder: *Builder, sema_block: *Compilation.Block, options: BlockOptions) error{OutOfMemory}!void {
for (sema_block.statements.items) |sema_statement_index| {
const sema_statement = builder.module.values.get(sema_statement_index);
switch (sema_statement.*) {
.loop => |loop_index| {
const sema_loop = builder.module.loops.get(loop_index);
const sema_loop_condition = builder.module.values.get(sema_loop.condition);
const sema_loop_body = builder.module.values.get(sema_loop.body);
const condition: Compilation.Value.Index = switch (sema_loop_condition.*) {
.bool => |bool_value| switch (bool_value) {
true => Compilation.Value.Index.invalid,
false => unreachable,
},
else => |t| @panic(@tagName(t)),
};
const original_block = builder.current_basic_block;
const jump_to_loop = try builder.append(.{
.jump = undefined,
});
const loop_body_block = try builder.newBlock();
const loop_prologue_block = if (options.emit_exit_block) try builder.newBlock() else BasicBlock.Index.invalid;
const loop_head_block = switch (condition.valid) {
false => loop_body_block,
true => unreachable,
};
builder.ir.instructions.get(jump_to_loop).jump = try builder.jump(.{
.source = original_block,
.destination = loop_head_block,
});
const sema_body_block = builder.module.blocks.get(sema_loop_body.block);
builder.current_basic_block = try builder.blockInsideBasicBlock(sema_body_block, loop_body_block);
if (loop_prologue_block.valid) {
builder.ir.blocks.get(loop_prologue_block).seal();
}
if (sema_body_block.reaches_end) {
_ = try builder.append(.{
.jump = try builder.jump(.{
.source = builder.current_basic_block,
.destination = loop_head_block,
}),
});
}
builder.ir.blocks.get(builder.current_basic_block).filled = true;
builder.ir.blocks.get(loop_body_block).seal();
if (!loop_head_block.eq(loop_body_block)) {
unreachable;
}
if (loop_prologue_block.valid) {
builder.current_basic_block = loop_prologue_block;
}
},
else => |t| @panic(@tagName(t)),
}
}
}
fn jump(builder: *Builder, jump_descriptor: Jump) !Jump.Index {
const destination_block = builder.ir.blocks.get(jump_descriptor.destination);
assert(!destination_block.sealed);
return try builder.ir.jumps.append(builder.allocator, jump_descriptor);
}
fn append(builder: *Builder, instruction: Instruction) !Instruction.Index {
assert(builder.current_basic_block.valid);
return builder.appendToBlock(builder.current_basic_block, instruction);
}
fn appendToBlock(builder: *Builder, block_index: BasicBlock.Index, instruction: Instruction) !Instruction.Index {
const instruction_index = try builder.ir.instructions.append(builder.allocator, instruction);
try builder.ir.blocks.get(block_index).instructions.append(builder.allocator, instruction_index);
return instruction_index;
}
fn newBlock(builder: *Builder) !BasicBlock.Index {
const new_block_index = try builder.ir.blocks.append(builder.allocator, .{});
const current_function = builder.ir.functions.get(builder.current_function_index);
const function_block_index = current_function.blocks.items.len;
try current_function.blocks.append(builder.allocator, new_block_index);
print("Adding block: {}\n", .{function_block_index});
return new_block_index;
}
};

0
src/backend/x86_64.zig Normal file
View File

View File

@ -36,17 +36,59 @@ pub fn BlockList(comptime T: type) type {
const List = @This(); const List = @This();
pub const Index = packed struct(u32) { pub const Index = packed struct(u32) {
valid: bool = true, block: u24,
index: u6, index: u6,
block: u25, _reserved: bool = false,
valid: bool = true,
pub const invalid = Index{ pub const invalid = Index{
.valid = false, .valid = false,
.index = 0, .index = 0,
.block = 0, .block = 0,
}; };
pub fn eq(index: Index, other: Index) bool {
return @as(u32, @bitCast(index)) == @as(u32, @bitCast(other));
}
}; };
pub const Iterator = struct {
block_index: u26,
element_index: u7,
list: *const List,
pub fn next(i: *Iterator) ?T {
return if (i.nextPointer()) |ptr| ptr.* else null;
}
pub fn nextPointer(i: *Iterator) ?*T {
if (i.element_index >= item_count) {
i.block_index += 1;
i.element_index = 0;
}
while (i.block_index < i.list.blocks.items.len) : (i.block_index += 1) {
while (i.element_index < item_count) : (i.element_index += 1) {
if (i.list.blocks.items[i.block_index].bitset.isSet(i.element_index)) {
const index = i.element_index;
i.element_index += 1;
return &i.list.blocks.items[i.block_index].items[index];
}
}
}
return null;
}
};
pub fn iterator(list: *const List) Iterator {
return .{
.block_index = 0,
.element_index = 0,
.list = list,
};
}
pub fn get(list: *List, index: Index) *T { pub fn get(list: *List, index: Index) *T {
assert(index.valid); assert(index.valid);
return &list.blocks.items[index.block].items[index.index]; return &list.blocks.items[index.block].items[index.index];
@ -59,6 +101,7 @@ pub fn BlockList(comptime T: type) type {
// Follow the guess // Follow the guess
if (list.blocks.items[list.first_block].allocateIndex()) |index| { if (list.blocks.items[list.first_block].allocateIndex()) |index| {
list.blocks.items[list.first_block].items[index] = element; list.blocks.items[list.first_block].items[index] = element;
list.len += 1;
return .{ return .{
.index = index, .index = index,
.block = @intCast(list.first_block), .block = @intCast(list.first_block),
@ -69,8 +112,10 @@ pub fn BlockList(comptime T: type) type {
} else { } else {
const block_index = list.blocks.items.len; const block_index = list.blocks.items.len;
const new_block = list.blocks.addOneAssumeCapacity(); const new_block = list.blocks.addOneAssumeCapacity();
new_block.* = .{};
const index = new_block.allocateIndex() catch unreachable; const index = new_block.allocateIndex() catch unreachable;
new_block.items[index] = element; new_block.items[index] = element;
list.len += 1;
return .{ return .{
.index = index, .index = index,
.block = @intCast(block_index), .block = @intCast(block_index),

View File

@ -7,10 +7,12 @@ const File = Compilation.File;
const Module = Compilation.Module; const Module = Compilation.Module;
const Package = Compilation.Package; const Package = Compilation.Package;
const Assignment = Compilation.Assignment;
const Block = Compilation.Block; const Block = Compilation.Block;
const Declaration = Compilation.Declaration; const Declaration = Compilation.Declaration;
const Field = Compilation.Field; const Field = Compilation.Field;
const Function = Compilation.Function; const Function = Compilation.Function;
const Loop = Compilation.Loop;
const Scope = Compilation.Scope; const Scope = Compilation.Scope;
const Struct = Compilation.Struct; const Struct = Compilation.Struct;
const Type = Compilation.Type; const Type = Compilation.Type;
@ -45,90 +47,157 @@ const Analyzer = struct {
fn comptimeBlock(analyzer: *Analyzer, scope: *Scope, node_index: Node.Index) !Value.Index { fn comptimeBlock(analyzer: *Analyzer, scope: *Scope, node_index: Node.Index) !Value.Index {
const comptime_node = analyzer.nodes[node_index.unwrap()]; const comptime_node = analyzer.nodes[node_index.unwrap()];
const comptime_block_node = analyzer.nodes[comptime_node.left.unwrap()]; const comptime_block = try analyzer.block(scope, .{ .none = {} }, comptime_node.left);
var statement_node_indices = ArrayList(Node.Index){}; return try analyzer.module.values.append(analyzer.allocator, .{
switch (comptime_block_node.id) { .block = comptime_block,
.block_one => { });
try statement_node_indices.append(analyzer.allocator, comptime_block_node.left);
},
else => |t| @panic(@tagName(t)),
} }
var statement_values = ArrayList(Value.Index){}; fn assign(analyzer: *Analyzer, scope: *Scope, node_index: Node.Index) !Assignment.Index {
print("Assign: #{}", .{node_index.value});
for (statement_node_indices.items) |statement_node_index| {
const statement_node = analyzer.nodes[statement_node_index.unwrap()];
switch (statement_node.id) {
.assign => {
const assign_expression = try analyzer.assign(scope, statement_node_index);
try statement_values.append(analyzer.allocator, assign_expression);
},
else => |t| @panic(@tagName(t)),
}
}
// TODO
return Value.Index.invalid;
}
fn assign(analyzer: *Analyzer, scope: *Scope, node_index: Node.Index) !Value.Index {
const node = analyzer.nodes[node_index.unwrap()]; const node = analyzer.nodes[node_index.unwrap()];
assert(node.id == .assign);
print("\nAssign. Left: {}. Right: {}\n", .{ node.left, node.right }); const Result = struct {
left: Value.Index,
right: Value.Index,
};
const result: Result = switch (node.left.valid) {
// In an assignment, the node being invalid means a discarding underscore, like this: ```_ = result``` // In an assignment, the node being invalid means a discarding underscore, like this: ```_ = result```
if (node.left.valid) { false => .{
@panic("Not discard"); .left = Value.Index.invalid,
.right = try analyzer.expression(scope, ExpectType.none, node.right),
},
true => {
const left_node = analyzer.nodes[node.left.unwrap()];
print("left node index: {}. Left node: {}", .{ node.left, left_node });
// const id = analyzer.tokenIdentifier(.token);
// print("id: {s}\n", .{id});
const left = try analyzer.expression(scope, ExpectType.none, node.left);
_ = left;
unreachable;
},
};
print("Assignment: L: {}. R: {}\n", .{ result.left, result.right });
if (result.left.valid and analyzer.module.values.get(result.left).isComptime() and analyzer.module.values.get(result.right).isComptime()) {
unreachable;
} else { } else {
return try analyzer.expression(scope, ExpectType{ .none = {} }, node.right); const assignment_index = try analyzer.module.assignments.append(analyzer.allocator, .{
.store = result.left,
.load = result.right,
});
return assignment_index;
} }
} }
fn block(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node_index: Node.Index) !Block.Index { fn block(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node_index: Node.Index) anyerror!Block.Index {
var reaches_end = true;
const block_node = analyzer.nodes[node_index.unwrap()]; const block_node = analyzer.nodes[node_index.unwrap()];
var statements = ArrayList(Node.Index){}; var statement_nodes = ArrayList(Node.Index){};
switch (block_node.id) { switch (block_node.id) {
.block_one => { .block_one, .comptime_block_one => {
try statements.append(analyzer.allocator, block_node.left); try statement_nodes.append(analyzer.allocator, block_node.left);
}, },
.block_zero => {}, .block_zero, .comptime_block_zero => {},
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
} }
for (statements.items) |statement_node_index| { const is_comptime = switch (block_node.id) {
_ = try analyzer.expression(scope, expect_type, statement_node_index); .comptime_block_zero, .comptime_block_one => true,
// const statement_node = analyzer.nodes[statement_node_index.unwrap()]; .block_zero, .block_one => false,
// else => |t| @panic(@tagName(t)),
// switch (statement_node.id) { };
// try .simple_while => { _ = is_comptime;
// const while_condition = try analyzer.expression(scope, ExpectType.boolean, statement_node.left);
// _ = while_condition; var statements = ArrayList(Value.Index){};
// const while_block = try analyzer.block(scope, expect_type, statement_node.right);
// _ = while_block; for (statement_nodes.items) |statement_node_index| {
// unreachable; if (!reaches_end) {
// }, unreachable;
// else => |t| @panic(@tagName(t)),
// }
} }
return try analyzer.module.blocks.append(analyzer.allocator, .{}); const statement_node = analyzer.nodes[statement_node_index.unwrap()];
const statement_value = switch (statement_node.id) {
inline .assign, .simple_while => |statement_id| blk: {
const specific_value_index = switch (statement_id) {
.assign => try analyzer.assign(scope, statement_node_index),
.simple_while => statement: {
const loop_index = try analyzer.module.loops.append(analyzer.allocator, .{
.condition = Value.Index.invalid,
.body = Value.Index.invalid,
.breaks = false,
});
const loop_structure = analyzer.module.loops.get(loop_index);
const while_condition = try analyzer.expression(scope, ExpectType.boolean, statement_node.left);
const while_body = try analyzer.expression(scope, expect_type, statement_node.right);
loop_structure.condition = while_condition;
loop_structure.body = while_body;
reaches_end = loop_structure.breaks or while_condition.valid;
break :statement loop_index;
},
else => unreachable,
};
const value = @unionInit(Value, switch (statement_id) {
.assign => "assign",
.simple_while => "loop",
else => unreachable,
}, specific_value_index);
const value_index = try analyzer.module.values.append(analyzer.allocator, value);
break :blk value_index;
},
else => |t| @panic(@tagName(t)),
};
try statements.append(analyzer.allocator, statement_value);
} }
fn expression(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node_index: Node.Index) error{OutOfMemory}!Value.Index { return try analyzer.module.blocks.append(analyzer.allocator, .{
const node = analyzer.nodes[node_index.unwrap()]; .statements = statements,
return switch (node.id) { .reaches_end = reaches_end,
.identifier => blk: { });
}
fn whileExpression(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node: Node) !Loop.Index {
_ = node;
_ = expect_type;
_ = scope;
_ = analyzer;
}
fn resolve(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, value: *Value) !void {
const node_index = switch (value.*) {
.unresolved => |unresolved| unresolved.node_index,
else => |t| @panic(@tagName(t)),
};
value.* = try analyzer.resolveNode(scope, expect_type, node_index);
}
fn doIdentifier(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node: Node) !Value.Index {
assert(node.id == .identifier);
const identifier_hash = try analyzer.identifierFromToken(node.token); const identifier_hash = try analyzer.identifierFromToken(node.token);
// TODO: search in upper scopes too // TODO: search in upper scopes too
const identifier_scope_lookup = try scope.declarations.getOrPut(analyzer.allocator, identifier_hash); const identifier_scope_lookup = try scope.declarations.getOrPut(analyzer.allocator, identifier_hash);
if (identifier_scope_lookup.found_existing) { if (identifier_scope_lookup.found_existing) {
const declaration_index = identifier_scope_lookup.value_ptr.*; const declaration_index = identifier_scope_lookup.value_ptr.*;
const declaration = analyzer.module.declarations.get(declaration_index); const declaration = analyzer.module.declarations.get(declaration_index);
break :blk try analyzer.analyzeDeclaration(scope, declaration); const init_value = analyzer.module.values.get(declaration.init_value);
try analyzer.resolve(scope, expect_type, init_value);
if (init_value.* != .runtime and declaration.mutability == .@"const") {
return declaration.init_value;
} else {
unreachable;
}
} else { } else {
@panic("TODO: not found"); @panic("TODO: not found");
} }
}, }
fn resolveNode(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node_index: Node.Index) anyerror!Value {
const node = analyzer.nodes[node_index.unwrap()];
return switch (node.id) {
.identifier => unreachable,
.compiler_intrinsic_one => blk: { .compiler_intrinsic_one => blk: {
const intrinsic_name = analyzer.tokenIdentifier(node.token + 1); const intrinsic_name = analyzer.tokenIdentifier(node.token + 1);
const intrinsic = data_structures.enumFromString(Intrinsic, intrinsic_name) orelse unreachable; const intrinsic = data_structures.enumFromString(Intrinsic, intrinsic_name) orelse unreachable;
@ -148,14 +217,9 @@ const Analyzer = struct {
unreachable; unreachable;
} }
const file_struct_declaration_index = try analyzeFile(analyzer.allocator, analyzer.module, imported_file.file); break :blk .{
break :blk try analyzer.module.values.append(analyzer.allocator, .{ .type = try analyzeFile(analyzer.allocator, analyzer.module, imported_file.file),
.type = .{ };
.declaration = file_struct_declaration_index,
},
.is_const = true,
.is_comptime = true,
});
}, },
else => unreachable, else => unreachable,
} }
@ -174,15 +238,27 @@ const Analyzer = struct {
.prototype = function_prototype_index, .prototype = function_prototype_index,
.body = function_body, .body = function_body,
}); });
const value_index = try analyzer.module.values.append(analyzer.allocator, .{ break :blk .{
.type = .{
.function = function_index, .function = function_index,
};
}, },
.is_const = true, .keyword_true => unreachable,
.is_comptime = true, .simple_while => unreachable,
}); // .assign => try analyzer.assign(scope, node_index),
break :blk value_index; .block_zero, .block_one => blk: {
const block_index = try analyzer.block(scope, expect_type, node_index);
break :blk .{
.block = block_index,
};
}, },
else => |t| @panic(@tagName(t)),
};
}
fn expression(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node_index: Node.Index) !Value.Index {
const node = analyzer.nodes[node_index.unwrap()];
return switch (node.id) {
.identifier => analyzer.doIdentifier(scope, expect_type, node),
.keyword_true => blk: { .keyword_true => blk: {
switch (expect_type) { switch (expect_type) {
.none => {}, .none => {},
@ -195,22 +271,9 @@ const Analyzer = struct {
break :blk bool_true; break :blk bool_true;
}, },
.simple_while => blk: { .block_zero => try analyzer.module.values.append(analyzer.allocator, .{
const while_condition = try analyzer.expression(scope, ExpectType.boolean, node.left); .block = try analyzer.block(scope, expect_type, node_index),
_ = while_condition; }),
const while_body = try analyzer.block(scope, expect_type, node.right);
_ = while_body;
const loop_index = try analyzer.module.loops.append(analyzer.allocator, .{});
const value_index = try analyzer.module.values.append(analyzer.allocator, .{
.type = .{
.loop = loop_index,
},
// TODO:
.is_const = false,
.is_comptime = false,
});
break :blk value_index;
},
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
} }
@ -249,44 +312,36 @@ const Analyzer = struct {
} }
fn analyzeDeclaration(analyzer: *Analyzer, scope: *Scope, declaration: *Declaration) !Value.Index { fn analyzeDeclaration(analyzer: *Analyzer, scope: *Scope, declaration: *Declaration) !Value.Index {
switch (declaration.*) { _ = declaration;
.unresolved => |node_index| { _ = scope;
const declaration_node = analyzer.nodes[node_index.unwrap()]; _ = analyzer;
return switch (declaration_node.id) { // switch (declaration.*) {
.simple_variable_declaration => blk: { // .unresolved => |node_index| {
const expect_type = switch (declaration_node.left.valid) { // const declaration_node = analyzer.nodes[node_index.unwrap()];
true => unreachable, // return switch (declaration_node.id) {
false => @unionInit(ExpectType, "none", {}), // .simple_variable_declaration => blk: {
}; // const expect_type = switch (declaration_node.left.valid) {
// true => unreachable,
const initialization_expression = try analyzer.expression(scope, expect_type, declaration_node.right); // false => @unionInit(ExpectType, "none", {}),
const value = analyzer.module.values.get(initialization_expression); // };
if (value.is_comptime and value.is_const) { //
break :blk initialization_expression; // const initialization_expression = try analyzer.expression(scope, expect_type, declaration_node.right);
} // const value = analyzer.module.values.get(initialization_expression);
// if (value.is_comptime and value.is_const) {
unreachable; // break :blk initialization_expression;
}, // }
else => |t| @panic(@tagName(t)), //
}; // unreachable;
}, // },
.struct_type => unreachable, // else => |t| @panic(@tagName(t)),
} // };
// },
// .struct_type => unreachable,
// }
@panic("TODO: analyzeDeclaration"); @panic("TODO: analyzeDeclaration");
} }
fn containerMember(analyzer: *Analyzer, scope: *Scope, node_index: Node.Index) !void {
const node = analyzer.nodes[node_index.unwrap()];
switch (node.id) {
.simple_variable_declaration => {},
.@"comptime" => {
_ = try analyzer.comptimeBlock(scope, node_index);
},
else => std.debug.panic("Tag: {}", .{node.id}),
}
}
fn globalSymbolDeclaration(analyzer: *Analyzer, symbol_declaration: SymbolDeclaration) !void { fn globalSymbolDeclaration(analyzer: *Analyzer, symbol_declaration: SymbolDeclaration) !void {
if (symbol_declaration.type_node.get()) |type_node_index| { if (symbol_declaration.type_node.get()) |type_node_index| {
_ = type_node_index; _ = type_node_index;
@ -339,21 +394,24 @@ const Analyzer = struct {
}; };
} }
fn structDeclaration(analyzer: *Analyzer, parent_scope: Scope.Index, container_declaration: syntactic_analyzer.ContainerDeclaration, index: Node.Index) !Declaration.Index { fn structType(analyzer: *Analyzer, parent_scope: Scope.Index, container_declaration: syntactic_analyzer.ContainerDeclaration, index: Node.Index) !Type.Index {
_ = index; _ = index;
const new_scope = try analyzer.allocateScope(parent_scope, Type.Index.invalid); const new_scope = try analyzer.allocateScope(.{ .parent = parent_scope });
const scope = new_scope.ptr; const scope = new_scope.ptr;
const is_file = !parent_scope.valid; const is_file = !parent_scope.valid;
assert(is_file); assert(is_file);
// TODO: do it properly
const declaration_index = try analyzer.module.declarations.append(analyzer.allocator, .{ const struct_index = try analyzer.module.structs.append(analyzer.allocator, .{
.struct_type = .{
.scope = new_scope.index, .scope = new_scope.index,
.initialization = if (is_file) Value.Index.invalid else unreachable,
},
}); });
// TODO: const struct_type = analyzer.module.structs.get(struct_index);
const type_index = try analyzer.module.types.append(analyzer.allocator, .{
.@"struct" = struct_index,
});
scope.type = type_index;
_ = struct_type;
assert(container_declaration.members.len > 0); assert(container_declaration.members.len > 0);
const count = blk: { const count = blk: {
@ -391,6 +449,11 @@ const Analyzer = struct {
switch (declaration_node.id) { switch (declaration_node.id) {
.@"comptime" => {}, .@"comptime" => {},
.simple_variable_declaration => { .simple_variable_declaration => {
const mutability: Compilation.Mutability = switch (analyzer.tokens[declaration_node.token].id) {
.fixed_keyword_const => .@"const",
.fixed_keyword_var => .@"var",
else => |t| @panic(@tagName(t)),
};
const expected_identifier_token_index = declaration_node.token + 1; const expected_identifier_token_index = declaration_node.token + 1;
const expected_identifier_token = analyzer.tokens[expected_identifier_token_index]; const expected_identifier_token = analyzer.tokens[expected_identifier_token_index];
if (expected_identifier_token.id != .identifier) { if (expected_identifier_token.id != .identifier) {
@ -416,7 +479,14 @@ const Analyzer = struct {
} }
const container_declaration_index = try analyzer.module.declarations.append(analyzer.allocator, .{ const container_declaration_index = try analyzer.module.declarations.append(analyzer.allocator, .{
.unresolved = declaration_node_index, .name = declaration_name,
.scope_type = .global,
.mutability = mutability,
.init_value = try analyzer.module.values.append(analyzer.allocator, .{
.unresolved = .{
.node_index = declaration_node.right,
},
}),
}); });
scope_lookup.value_ptr.* = container_declaration_index; scope_lookup.value_ptr.* = container_declaration_index;
@ -429,8 +499,9 @@ const Analyzer = struct {
for (declaration_nodes.items) |declaration_node_index| { for (declaration_nodes.items) |declaration_node_index| {
const declaration_node = analyzer.nodes[declaration_node_index.unwrap()]; const declaration_node = analyzer.nodes[declaration_node_index.unwrap()];
switch (declaration_node.id) { switch (declaration_node.id) {
.@"comptime", .simple_variable_declaration => try analyzer.containerMember(scope, declaration_node_index), .@"comptime" => _ = try analyzer.comptimeBlock(scope, declaration_node_index),
else => unreachable, .simple_variable_declaration => {},
else => |t| @panic(@tagName(t)),
} }
} }
@ -441,7 +512,7 @@ const Analyzer = struct {
@panic("TODO: fields"); @panic("TODO: fields");
} }
return declaration_index; return type_index;
} }
const MemberType = enum { const MemberType = enum {
@ -494,11 +565,8 @@ const Analyzer = struct {
index: Scope.Index, index: Scope.Index,
}; };
fn allocateScope(analyzer: *Analyzer, parent_scope: Scope.Index, scope_type: Type.Index) !ScopeAllocation { fn allocateScope(analyzer: *Analyzer, scope_value: Scope) !ScopeAllocation {
const scope_index = try analyzer.module.scopes.append(analyzer.allocator, .{ const scope_index = try analyzer.module.scopes.append(analyzer.allocator, scope_value);
.parent = parent_scope,
.type = scope_type,
});
const scope = analyzer.module.scopes.get(scope_index); const scope = analyzer.module.scopes.get(scope_index);
return .{ return .{
@ -512,6 +580,9 @@ const ExpectType = union(enum) {
none, none,
type_index: Type.Index, type_index: Type.Index,
pub const none = ExpectType{
.none = {},
};
pub const boolean = ExpectType{ pub const boolean = ExpectType{
.type_index = type_boolean, .type_index = type_boolean,
}; };
@ -562,7 +633,7 @@ const HardwareSignedIntegerType = enum {
const offset = HardwareUnsignedIntegerType.offset + @typeInfo(HardwareUnsignedIntegerType).Enum.fields.len; const offset = HardwareUnsignedIntegerType.offset + @typeInfo(HardwareUnsignedIntegerType).Enum.fields.len;
}; };
pub fn initialize(compilation: *Compilation, module: *Module, package: *Package) !Declaration.Index { pub fn initialize(compilation: *Compilation, module: *Module, package: *Package) !Type.Index {
inline for (@typeInfo(FixedTypeKeyword).Enum.fields) |enum_field| { inline for (@typeInfo(FixedTypeKeyword).Enum.fields) |enum_field| {
_ = try module.types.append(compilation.base_allocator, @unionInit(Type, enum_field.name, {})); _ = try module.types.append(compilation.base_allocator, @unionInit(Type, enum_field.name, {}));
} }
@ -596,25 +667,17 @@ pub fn initialize(compilation: *Compilation, module: *Module, package: *Package)
} }
_ = try module.values.append(compilation.base_allocator, .{ _ = try module.values.append(compilation.base_allocator, .{
.type = .{ .bool = false,
.bool_false = {},
},
.is_const = true,
.is_comptime = true,
}); });
_ = try module.values.append(compilation.base_allocator, .{ _ = try module.values.append(compilation.base_allocator, .{
.type = .{ .bool = true,
.bool_true = {},
},
.is_const = true,
.is_comptime = true,
}); });
return analyzeExistingPackage(compilation, module, package); return analyzeExistingPackage(compilation, module, package);
} }
pub fn analyzeExistingPackage(compilation: *Compilation, module: *Module, package: *Package) !Declaration.Index { pub fn analyzeExistingPackage(compilation: *Compilation, module: *Module, package: *Package) !Type.Index {
const package_import = try module.importPackage(compilation.base_allocator, package); const package_import = try module.importPackage(compilation.base_allocator, package);
assert(!package_import.is_new); assert(!package_import.is_new);
const package_file = package_import.file; const package_file = package_import.file;
@ -622,7 +685,7 @@ pub fn analyzeExistingPackage(compilation: *Compilation, module: *Module, packag
return try analyzeFile(compilation.base_allocator, module, package_file); return try analyzeFile(compilation.base_allocator, module, package_file);
} }
pub fn analyzeFile(allocator: Allocator, module: *Module, file: *File) !Declaration.Index { pub fn analyzeFile(allocator: Allocator, module: *Module, file: *File) !Type.Index {
assert(file.status == .parsed); assert(file.status == .parsed);
var analyzer = Analyzer{ var analyzer = Analyzer{
@ -634,7 +697,7 @@ pub fn analyzeFile(allocator: Allocator, module: *Module, file: *File) !Declarat
.module = module, .module = module,
}; };
const result = try analyzer.structDeclaration(Scope.Index.invalid, try mainNodeToContainerDeclaration(allocator, file), .{ .value = 0 }); const result = try analyzer.structType(Scope.Index.invalid, try mainNodeToContainerDeclaration(allocator, file), .{ .value = 0 });
return result; return result;
} }

View File

@ -17,6 +17,10 @@ pub const Result = struct {
time: u64, time: u64,
}; };
pub const Options = packed struct {
is_comptime: bool,
};
// TODO: pack it to be more efficient // TODO: pack it to be more efficient
pub const Node = packed struct(u128) { pub const Node = packed struct(u128) {
token: u32, token: u32,
@ -68,6 +72,8 @@ pub const Node = packed struct(u128) {
function_definition = 16, function_definition = 16,
keyword_noreturn = 17, keyword_noreturn = 17,
keyword_true = 18, keyword_true = 18,
comptime_block_zero = 19,
comptime_block_one = 20,
}; };
}; };
@ -111,7 +117,7 @@ const Analyzer = struct {
.fixed_keyword_comptime => switch (analyzer.tokens[analyzer.token_i + 1].id) { .fixed_keyword_comptime => switch (analyzer.tokens[analyzer.token_i + 1].id) {
.left_brace => blk: { .left_brace => blk: {
analyzer.token_i += 1; analyzer.token_i += 1;
const comptime_block = try analyzer.block(); const comptime_block = try analyzer.block(.{ .is_comptime = true });
break :blk .{ break :blk .{
.id = .@"comptime", .id = .@"comptime",
@ -181,7 +187,9 @@ const Analyzer = struct {
fn function(analyzer: *Analyzer) !Node.Index { fn function(analyzer: *Analyzer) !Node.Index {
const token = analyzer.token_i; const token = analyzer.token_i;
const function_prototype = try analyzer.functionPrototype(); const function_prototype = try analyzer.functionPrototype();
const function_body = try analyzer.block(); const is_comptime = false;
_ = is_comptime;
const function_body = try analyzer.block(.{ .is_comptime = false });
return analyzer.addNode(.{ return analyzer.addNode(.{
.id = .function_definition, .id = .function_definition,
.token = token, .token = token,
@ -223,40 +231,14 @@ const Analyzer = struct {
} }
} }
fn block(analyzer: *Analyzer) !Node.Index { fn block(analyzer: *Analyzer, options: Options) !Node.Index {
const left_brace = try analyzer.expectToken(.left_brace); const left_brace = try analyzer.expectToken(.left_brace);
const node_heap_top = analyzer.temporal_node_heap.items.len; const node_heap_top = analyzer.temporal_node_heap.items.len;
defer analyzer.temporal_node_heap.shrinkRetainingCapacity(node_heap_top); defer analyzer.temporal_node_heap.shrinkRetainingCapacity(node_heap_top);
while (analyzer.tokens[analyzer.token_i].id != .right_brace) { while (analyzer.tokens[analyzer.token_i].id != .right_brace) {
const statement_index = try analyzer.statement();
try analyzer.temporal_node_heap.append(analyzer.allocator, statement_index);
}
_ = try analyzer.expectToken(.right_brace);
const statement_array = analyzer.temporal_node_heap.items[node_heap_top..];
const node: Node = switch (statement_array.len) {
0 => .{
.id = .block_zero,
.token = left_brace,
.left = Node.Index.invalid,
.right = Node.Index.invalid,
},
1 => .{
.id = .block_one,
.token = left_brace,
.left = statement_array[0],
.right = Node.Index.invalid,
},
else => |len| std.debug.panic("len: {}", .{len}),
};
return analyzer.addNode(node);
}
fn statement(analyzer: *Analyzer) !Node.Index {
// TODO: more stuff before
const first_statement_token = analyzer.tokens[analyzer.token_i]; const first_statement_token = analyzer.tokens[analyzer.token_i];
return switch (first_statement_token.id) { const statement_index = switch (first_statement_token.id) {
.identifier => switch (analyzer.tokens[analyzer.token_i + 1].id) { .identifier => switch (analyzer.tokens[analyzer.token_i + 1].id) {
.colon => { .colon => {
unreachable; unreachable;
@ -269,12 +251,40 @@ const Analyzer = struct {
break :blk result; break :blk result;
}, },
}, },
.fixed_keyword_while => try analyzer.whileStatement(), .fixed_keyword_while => try analyzer.whileStatement(options),
else => unreachable, else => unreachable,
}; };
try analyzer.temporal_node_heap.append(analyzer.allocator, statement_index);
} }
fn whileStatement(analyzer: *Analyzer) error{ OutOfMemory, unexpected_token, not_implemented }!Node.Index { _ = try analyzer.expectToken(.right_brace);
const statement_array = analyzer.temporal_node_heap.items[node_heap_top..];
const node: Node = switch (statement_array.len) {
0 => .{
.id = switch (options.is_comptime) {
true => .comptime_block_zero,
false => .block_zero,
},
.token = left_brace,
.left = Node.Index.invalid,
.right = Node.Index.invalid,
},
1 => .{
.id = switch (options.is_comptime) {
true => .comptime_block_one,
false => .block_one,
},
.token = left_brace,
.left = statement_array[0],
.right = Node.Index.invalid,
},
else => |len| std.debug.panic("len: {}", .{len}),
};
return analyzer.addNode(node);
}
fn whileStatement(analyzer: *Analyzer, options: Options) error{ OutOfMemory, unexpected_token, not_implemented }!Node.Index {
const while_identifier_index = try analyzer.expectToken(.fixed_keyword_while); const while_identifier_index = try analyzer.expectToken(.fixed_keyword_while);
_ = try analyzer.expectToken(.left_parenthesis); _ = try analyzer.expectToken(.left_parenthesis);
@ -282,7 +292,7 @@ const Analyzer = struct {
const while_condition = try analyzer.expression(); const while_condition = try analyzer.expression();
_ = try analyzer.expectToken(.right_parenthesis); _ = try analyzer.expectToken(.right_parenthesis);
const while_block = try analyzer.block(); const while_block = try analyzer.block(options);
return analyzer.addNode(.{ return analyzer.addNode(.{
.id = .simple_while, .id = .simple_while,
@ -300,7 +310,7 @@ const Analyzer = struct {
else => unreachable, else => unreachable,
}; };
return analyzer.addNode(.{ const node = Node{
.id = expression_id, .id = expression_id,
.token = blk: { .token = blk: {
const token_i = analyzer.token_i; const token_i = analyzer.token_i;
@ -309,7 +319,9 @@ const Analyzer = struct {
}, },
.left = expr, .left = expr,
.right = try analyzer.expression(), .right = try analyzer.expression(),
}); };
std.debug.print("assign:\nleft: {}.\nright: {}\n", .{ node.left, node.right });
return analyzer.addNode(node);
} }
fn compilerIntrinsic(analyzer: *Analyzer) !Node.Index { fn compilerIntrinsic(analyzer: *Analyzer) !Node.Index {
@ -405,7 +417,8 @@ const Analyzer = struct {
else => try analyzer.curlySuffixExpression(), else => try analyzer.curlySuffixExpression(),
}, },
.string_literal, .fixed_keyword_true, .fixed_keyword_false => try analyzer.curlySuffixExpression(), .string_literal, .fixed_keyword_true, .fixed_keyword_false => try analyzer.curlySuffixExpression(),
.left_brace => try analyzer.block(), // todo:?
// .left_brace => try analyzer.block(),
else => |id| { else => |id| {
log.warn("By default, calling curlySuffixExpression with {s}", .{@tagName(id)}); log.warn("By default, calling curlySuffixExpression with {s}", .{@tagName(id)});
unreachable; unreachable;
@ -519,6 +532,7 @@ const Analyzer = struct {
.colon => unreachable, .colon => unreachable,
else => blk: { else => blk: {
const identifier = analyzer.getIdentifier(token); const identifier = analyzer.getIdentifier(token);
std.debug.print("identifier: {s}\n", .{identifier});
analyzer.token_i += 1; analyzer.token_i += 1;
if (equal(u8, identifier, "_")) { if (equal(u8, identifier, "_")) {
break :blk Node.Index.invalid; break :blk Node.Index.invalid;
@ -549,10 +563,9 @@ const Analyzer = struct {
} }
fn addNode(analyzer: *Analyzer, node: Node) !Node.Index { fn addNode(analyzer: *Analyzer, node: Node) !Node.Index {
std.debug.print("Adding node {s}\n", .{@tagName(node.id)});
const index = analyzer.nodes.items.len; const index = analyzer.nodes.items.len;
try analyzer.nodes.append(analyzer.allocator, node); try analyzer.nodes.append(analyzer.allocator, node);
std.debug.print("Adding node #{} {s}\n", .{ index, @tagName(node.id) });
return Node.Index{ return Node.Index{
.value = @intCast(index), .value = @intCast(index),
}; };