ir for main function

This commit is contained in:
David Gonzalez Martin 2023-09-30 12:51:58 -06:00
parent 611e611cab
commit bca2f024cd
5 changed files with 209 additions and 65 deletions

View File

@ -76,6 +76,20 @@ pub const Type = union(enum) {
pub const List = BlockList(@This());
pub const Index = List.Index;
pub const Allocation = List.Allocation;
pub fn getSize(type_info: Type) u64 {
return switch (type_info) {
.integer => |integer| integer.getSize(),
else => |t| @panic(@tagName(t)),
};
}
pub fn getAlignment(type_info: Type) u64 {
return switch (type_info) {
.integer => |integer| @min(16, integer.getSize()),
else => |t| @panic(@tagName(t)),
};
}
};
pub const Integer = struct {
@ -85,6 +99,10 @@ pub const Integer = struct {
unsigned = 0,
signed = 1,
};
pub fn getSize(integer: Integer) u64 {
return integer.bit_count / @bitSizeOf(u8) + @intFromBool(integer.bit_count % @bitSizeOf(u8) != 0);
}
};
/// A scope contains a bunch of declarations
@ -201,6 +219,7 @@ pub const Syscall = struct {
pub const Call = struct {
value: Value.Index,
arguments: ArgumentList.Index,
type: Type.Index,
pub const List = BlockList(@This());
pub const Index = List.Index;
pub const Allocation = List.Allocation;
@ -251,11 +270,11 @@ pub const Value = union(enum) {
};
}
pub fn getType(value: *Value) !void {
switch (value.*) {
pub fn getType(value: *Value, module: *Module) Type.Index {
return switch (value.*) {
.call => |call_index| module.calls.get(call_index).type,
else => |t| @panic(@tagName(t)),
}
unreachable;
};
}
};
@ -490,10 +509,7 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) !
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 => {},
}
try emit.get(.x86_64).initialize(compilation.base_allocator, &ir);
}
fn generateAST() !void {}

View File

@ -46,13 +46,18 @@ pub const Result = struct {
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, .macos => |os_tag| blk: {
const jit = switch (os_tag) {
.macos => 0x800,
.linux => 0,
else => unreachable,
};
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 | jit;
break :blk std.os.mmap(null, size, protection_flags, mmap_flags, -1, 0);
},

View File

@ -10,16 +10,20 @@ const Package = Compilation.Package;
const data_structures = @import("../data_structures.zig");
const ArrayList = data_structures.ArrayList;
const BlockList = data_structures.BlockList;
const AutoHashMap = data_structures.AutoHashMap;
pub const Result = struct {
functions: BlockList(Function) = .{},
blocks: BlockList(BasicBlock) = .{},
calls: BlockList(Call) = .{},
functions: BlockList(Function) = .{},
instructions: BlockList(Instruction) = .{},
jumps: BlockList(Jump) = .{},
values: BlockList(Value) = .{},
syscalls: BlockList(Syscall) = .{},
loads: BlockList(Load) = .{},
phis: BlockList(Phi) = .{},
stores: BlockList(Store) = .{},
syscalls: BlockList(Syscall) = .{},
values: BlockList(Value) = .{},
stack_references: BlockList(StackReference) = .{},
};
pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, main_file: Compilation.Type.Index) !Result {
@ -62,10 +66,12 @@ pub const BasicBlock = struct {
};
pub const Instruction = union(enum) {
call: Call.Index,
jump: Jump.Index,
load: Load.Index,
phi: Phi.Index,
ret: Ret,
store: Store.Index,
syscall: Syscall.Index,
@"unreachable",
@ -106,9 +112,34 @@ const Load = struct {
pub const Index = List.Index;
};
const Store = struct {
source: Value.Index,
destination: StackReference.Index,
pub const List = BlockList(@This());
pub const Index = List.Index;
};
const StackReference = struct {
size: u64,
alignment: u64,
offset: u64,
pub const List = BlockList(@This());
pub const Index = List.Index;
};
const Call = struct {
function: Function.Index,
pub const List = BlockList(@This());
pub const Index = List.Index;
pub const Allocation = List.Allocation;
};
pub const Value = union(enum) {
integer: Integer,
load: Load.Index,
call: Call.Index,
stack_reference: StackReference.Index,
pub const List = BlockList(@This());
pub const Index = List.Index;
@ -116,6 +147,8 @@ pub const Value = union(enum) {
return switch (value) {
.integer => false,
.load => true,
.call => true,
.stack_reference => true,
};
}
};
@ -138,11 +171,15 @@ pub const Builder = struct {
current_basic_block: BasicBlock.Index = BasicBlock.Index.invalid,
current_function_index: Function.Index = Function.Index.invalid,
return_phi_node: Instruction.Index = Instruction.Index.invalid,
current_stack_offset: usize = 0,
stack_map: AutoHashMap(Compilation.Declaration.Index, StackReference.Index) = .{},
fn function(builder: *Builder, sema_function: Compilation.Function) !void {
builder.current_function_index = (try builder.ir.functions.append(builder.allocator, .{})).index;
// TODO: arguments
builder.current_basic_block = try builder.newBlock();
builder.current_stack_offset = 0;
builder.stack_map = .{};
const return_type = builder.module.types.get(builder.module.function_prototypes.get(sema_function.prototype).return_type);
const is_noreturn = return_type.* == .noreturn;
@ -301,28 +338,77 @@ pub const Builder = struct {
});
},
.declaration => |sema_declaration_index| {
_ = sema_declaration_index;
unreachable;
const sema_declaration = builder.module.declarations.get(sema_declaration_index);
assert(sema_declaration.scope_type == .local);
const sema_init_value = builder.module.values.get(sema_declaration.init_value);
const declaration_type = builder.module.types.get(sema_init_value.getType(builder.module));
const size = declaration_type.getSize();
const alignment = declaration_type.getAlignment();
const stack_offset = switch (size > 0) {
true => builder.allocateStack(size, alignment),
false => 0,
};
var value_index = try builder.emitValue(sema_declaration.init_value);
const value = builder.ir.values.get(value_index);
print("Value: {}\n", .{value.*});
value_index = switch (value.isInMemory()) {
false => try builder.load(value_index),
true => value_index,
};
if (stack_offset > 0) {
_ = try builder.store(.{
.source = value_index,
.destination = try builder.stackReference(stack_offset, declaration_type.*, sema_declaration_index),
});
}
},
else => |t| @panic(@tagName(t)),
}
}
}
fn stackReference(builder: *Builder, stack_offset: u64, t: Compilation.Type, value: Compilation.Declaration.Index) !StackReference.Index {
const stack_reference_allocation = try builder.ir.stack_references.append(builder.allocator, .{
.offset = stack_offset,
.size = t.getSize(),
.alignment = t.getAlignment(),
});
const index = stack_reference_allocation.index;
try builder.stack_map.put(builder.allocator, value, index);
return index;
}
fn store(builder: *Builder, descriptor: Store) !void {
const store_allocation = try builder.ir.stores.append(builder.allocator, descriptor);
_ = try builder.append(.{
.store = store_allocation.index,
});
}
fn allocateStack(builder: *Builder, size: u64, alignment: u64) u64 {
builder.current_stack_offset = std.mem.alignForward(u64, builder.current_stack_offset, alignment);
builder.current_stack_offset += size;
return builder.current_stack_offset;
}
fn load(builder: *Builder, value_index: Value.Index) !Value.Index {
print("Doing load!\n", .{});
const load_index = try builder.ir.loads.append(builder.allocator, .{
const load_allocation = try builder.ir.loads.append(builder.allocator, .{
.value = value_index,
});
const instruction_index = try builder.append(.{
.load = load_index,
.load = load_allocation.index,
});
_ = instruction_index;
const result = try builder.ir.values.append(builder.allocator, .{
.load = load_index,
.load = load_allocation.index,
});
return result;
return result.index;
}
fn emitValue(builder: *Builder, sema_value_index: Compilation.Value.Index) !Value.Index {
@ -335,14 +421,65 @@ pub const Builder = struct {
.sign = false,
},
})).index,
.call => |sema_call_index| {
const sema_call = builder.module.calls.get(sema_call_index);
const argument_list_index = sema_call.arguments;
if (argument_list_index.valid) {
unreachable;
}
const call_index = try builder.call(.{
.function = switch (builder.module.values.get(sema_call.value).*) {
.function => |function_index| .{
.index = function_index.index,
.block = function_index.block,
},
else => |t| @panic(@tagName(t)),
},
});
_ = try builder.append(.{
.call = call_index,
});
const value_allocation = try builder.ir.values.append(builder.allocator, .{
.call = call_index,
});
return value_allocation.index;
},
.declaration_reference => |sema_declaration_index| {
const sema_declaration = builder.module.declarations.get(sema_declaration_index);
const sema_init_value = builder.module.values.get(sema_declaration.init_value);
const init_type = sema_init_value.getType(builder.module);
_ = init_type;
switch (sema_declaration.scope_type) {
.local => {
const stack_reference = builder.stack_map.get(sema_declaration_index).?;
const value = try builder.ir.values.append(builder.allocator, .{
.stack_reference = stack_reference,
});
return value.index;
},
.global => unreachable,
}
// switch (sema_declaration.*) {
// else => |t| @panic(@tagName(t)),
// }
},
else => |t| @panic(@tagName(t)),
};
}
fn jump(builder: *Builder, jump_descriptor: Jump) !Jump.Index {
const destination_block = builder.ir.blocks.get(jump_descriptor.destination);
fn call(builder: *Builder, descriptor: Call) !Call.Index {
const call_allocation = try builder.ir.calls.append(builder.allocator, descriptor);
return call_allocation.index;
}
fn jump(builder: *Builder, descriptor: Jump) !Jump.Index {
const destination_block = builder.ir.blocks.get(descriptor.destination);
assert(!destination_block.sealed);
const jump_allocation = try builder.ir.jumps.append(builder.allocator, jump_descriptor);
const jump_allocation = try builder.ir.jumps.append(builder.allocator, descriptor);
return jump_allocation.index;
}

View File

@ -54,23 +54,6 @@ pub fn selectInstruction(instruction_selector: *InstructionSelector, function: *
});
// TODO
} else unreachable;
// if (integer.value == 0) {
// try function.instructions.append(instruction_selector.allocator, .{
// .xor_reg32_reg32 = .{
// .destination = syscall_register,
// .source = syscall_register,
// },
// });
// } else if (integer.value < std.math.maxInt(u32)) {
// try function.instructions.append(instruction_selector.allocator, .{
// .mov_reg_imm32 = .{
// .destination = syscall_register,
// .source = @intCast(integer.value),
// },
// });
// } else {
// unreachable;
// }
},
else => |t| @panic(@tagName(t)),
}
@ -83,14 +66,14 @@ pub fn selectInstruction(instruction_selector: *InstructionSelector, function: *
.phi => unreachable,
.ret => unreachable,
.jump => |jump_index| {
_ = jump_index;
// const jump = intermediate.jumps.get(jump_index);
// const relocation = LocalRelative{
// .instruction = .jmp_rel_8,
// .source = @intCast(function.block_map.get(jump.source) orelse unreachable),
// .destination = @intCast(function.block_map.get(jump.destination) orelse unreachable),
// .offset_in_block = function.block_byte_count,
// };
const jump = intermediate.jumps.get(jump_index);
const relocation = Displacement{
.size = .one,
.source = @intCast(function.block_map.get(jump.source) orelse unreachable),
.destination = @intCast(function.block_map.get(jump.destination) orelse unreachable),
.offset_in_block = function.block_byte_count,
};
_ = relocation;
// const index = function.instructions.items.len;
// try function.relocations.append(instruction_selector.allocator, @intCast(index));
// try function.instructions.append(instruction_selector.allocator, .{
@ -98,6 +81,8 @@ pub fn selectInstruction(instruction_selector: *InstructionSelector, function: *
// });
unreachable;
},
.call => unreachable,
.store => unreachable,
}
}
@ -115,6 +100,13 @@ const RegisterMemoryRegister = struct {
direct: bool,
};
const Displacement = struct {
size: Size,
source: u16,
destination: u16,
offset_in_block: u16,
};
const RmResult = struct {
rex: Rex,
mod_rm: ModRm,
@ -215,24 +207,7 @@ pub fn emitInstruction(result: *emit.Result, instruction: Instruction, intermedi
result.appendCodeByte(opcode_byte);
emitImmediate(result, intermediate, register_immediate.immediate, register_immediate.immediate_size);
},
// .jmp_rel_8 => unreachable, //result.appendOnlyOpcodeSkipInstructionBytes(instruction),
// inline .mov_reg_imm32 => |content, tag| {
// _ = tag;
// _ = content;
// // const descriptor = instruction_descriptors.get(tag);
// // result.writeOpcode(descriptor.opcode);
// // result.appendCodeByte(descriptor.getOpcode()[0] | @intFromEnum(content.destination));
// // result.appendCode(std.mem.asBytes(&content.source));
// unreachable;
// },
// inline .xor_reg32_reg32 => |content, tag| {
// _ = tag;
// _ = content;
// // const descriptor = instruction_descriptors.get(tag);
// // result.appendCodeByte(descriptor.getOpcode()[0]);
// // result.appendCodeByte(0xc0 | @as(u8, @intFromEnum(content.source)) << 4 | @intFromEnum(content.destination));
// unreachable;
// },
.jmp_rel => unreachable,
inline .syscall, .ud2 => |_, tag| {
const opcode = tag.getOpcode(&.{});
result.appendCode(opcode);
@ -244,6 +219,7 @@ pub fn emitInstruction(result: *emit.Result, instruction: Instruction, intermedi
pub const Instruction = union(Id) {
xor_rm_r: RegisterMemoryRegister,
mov_r_imm: RegisterImmediate,
jmp_rel: Displacement,
// jmp_rel_8: LocalRelative,
// mov_reg_imm32: struct {
// destination: GPRegister,
@ -259,7 +235,7 @@ pub const Instruction = union(Id) {
const Id = enum {
xor_rm_r,
mov_r_imm,
// jmp_rel_8,
jmp_rel,
// mov_reg_imm32,
// xor_reg32_reg32,
syscall,
@ -277,6 +253,11 @@ pub const Instruction = union(Id) {
.one => &.{0x30},
.two, .four, .eight => &.{0x31},
},
.jmp_rel => switch (operands[0].displacement.size) {
.one => unreachable,
.four => unreachable,
else => unreachable,
},
};
}
};

View File

@ -396,10 +396,15 @@ const Analyzer = struct {
.call_one => blk: {
const this_value_node_index = node.left;
const this_value_allocation = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, this_value_node_index);
const value_type = switch (this_value_allocation.ptr.*) {
.function => |function_index| analyzer.module.function_prototypes.get(analyzer.module.functions.get(function_index).prototype).return_type,
else => |t| @panic(@tagName(t)),
};
const call_allocation = try analyzer.module.calls.append(analyzer.allocator, .{
.value = this_value_allocation.index,
.arguments = ArgumentList.Index.invalid,
.type = value_type,
});
break :blk .{
.call = call_allocation.index,