commit
f2064c93ff
@ -2,6 +2,7 @@ comptime {
|
|||||||
_ = _start;
|
_ = _start;
|
||||||
}
|
}
|
||||||
|
|
||||||
const _start = () noreturn {
|
const _start = fn () noreturn {
|
||||||
while (true) {}
|
_ = #syscall(231, 0);
|
||||||
|
unreachable;
|
||||||
};
|
};
|
||||||
|
@ -174,18 +174,34 @@ pub const Assignment = struct {
|
|||||||
pub const Index = List.Index;
|
pub const Index = List.Index;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Syscall = struct {
|
||||||
|
number: Value.Index,
|
||||||
|
arguments: [6]Value.Index,
|
||||||
|
argument_count: u8,
|
||||||
|
|
||||||
|
pub fn getArguments(syscall: Syscall) []const Value.Index {
|
||||||
|
return syscall.arguments[0..syscall.argument_count];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const List = BlockList(@This());
|
||||||
|
pub const Index = List.Index;
|
||||||
|
};
|
||||||
|
|
||||||
pub const Value = union(enum) {
|
pub const Value = union(enum) {
|
||||||
unresolved: Unresolved,
|
unresolved: Unresolved,
|
||||||
declaration: Declaration.Index,
|
declaration: Declaration.Index,
|
||||||
void,
|
void,
|
||||||
bool: bool,
|
bool: bool,
|
||||||
undefined,
|
undefined,
|
||||||
|
@"unreachable",
|
||||||
loop: Loop.Index,
|
loop: Loop.Index,
|
||||||
function: Function.Index,
|
function: Function.Index,
|
||||||
block: Block.Index,
|
block: Block.Index,
|
||||||
runtime: Runtime,
|
runtime: Runtime,
|
||||||
assign: Assignment.Index,
|
assign: Assignment.Index,
|
||||||
type: Type.Index,
|
type: Type.Index,
|
||||||
|
integer: u64,
|
||||||
|
syscall: Syscall.Index,
|
||||||
|
|
||||||
pub const List = BlockList(@This());
|
pub const List = BlockList(@This());
|
||||||
pub const Index = List.Index;
|
pub const Index = List.Index;
|
||||||
@ -221,6 +237,7 @@ pub const Module = struct {
|
|||||||
blocks: BlockList(Block) = .{},
|
blocks: BlockList(Block) = .{},
|
||||||
loops: BlockList(Loop) = .{},
|
loops: BlockList(Loop) = .{},
|
||||||
assignments: BlockList(Assignment) = .{},
|
assignments: BlockList(Assignment) = .{},
|
||||||
|
syscalls: BlockList(Syscall) = .{},
|
||||||
|
|
||||||
pub const Descriptor = struct {
|
pub const Descriptor = struct {
|
||||||
main_package_path: []const u8,
|
main_package_path: []const u8,
|
||||||
@ -238,6 +255,7 @@ pub const Module = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub fn importFile(module: *Module, allocator: Allocator, current_file: *File, import_name: []const u8) !ImportPackageResult {
|
pub fn importFile(module: *Module, allocator: Allocator, current_file: *File, import_name: []const u8) !ImportPackageResult {
|
||||||
|
print("import: '{s}'\n", .{import_name});
|
||||||
if (equal(u8, import_name, "std")) {
|
if (equal(u8, import_name, "std")) {
|
||||||
return module.importPackage(allocator, module.main_package.dependencies.get("std").?);
|
return module.importPackage(allocator, module.main_package.dependencies.get("std").?);
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ const Section = struct {
|
|||||||
index: usize = 0,
|
index: usize = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Result = struct {
|
pub const Result = struct {
|
||||||
sections: struct {
|
sections: struct {
|
||||||
text: Section,
|
text: Section,
|
||||||
rodata: Section,
|
rodata: Section,
|
||||||
@ -60,24 +60,30 @@ const Result = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn appendCode(image: *Result, code: []const u8) void {
|
pub fn appendCode(image: *Result, code: []const u8) void {
|
||||||
|
std.debug.print("New code: ", .{});
|
||||||
|
for (code) |byte| {
|
||||||
|
std.debug.print("0x{x} ", .{byte});
|
||||||
|
}
|
||||||
|
std.debug.print("\n", .{});
|
||||||
const destination = image.sections.text.content[image.sections.text.index..][0..code.len];
|
const destination = image.sections.text.content[image.sections.text.index..][0..code.len];
|
||||||
@memcpy(destination, code);
|
@memcpy(destination, code);
|
||||||
image.sections.text.index += code.len;
|
image.sections.text.index += code.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn appendCodeByte(image: *Result, code_byte: u8) void {
|
pub fn appendCodeByte(image: *Result, code_byte: u8) void {
|
||||||
|
std.debug.print("New code: 0x{x}\n", .{code_byte});
|
||||||
image.sections.text.content[image.sections.text.index] = code_byte;
|
image.sections.text.content[image.sections.text.index] = code_byte;
|
||||||
image.sections.text.index += 1;
|
image.sections.text.index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn appendOnlyOpcodeSkipInstructionBytes(image: *Result, instruction: Instruction) void {
|
// fn appendOnlyOpcodeSkipInstructionBytes(image: *Result, instruction: Instruction) void {
|
||||||
const instruction_descriptor = instruction_descriptors.get(instruction);
|
// const instruction_descriptor = instruction_descriptors.get(instruction);
|
||||||
assert(instruction_descriptor.opcode_byte_count == instruction_descriptor.operand_offset);
|
// assert(instruction_descriptor.opcode_byte_count == instruction_descriptor.operand_offset);
|
||||||
image.appendCode(instruction_descriptor.getOpcode());
|
// image.appendCode(instruction_descriptor.getOpcode());
|
||||||
|
//
|
||||||
image.sections.text.index += instruction_descriptor.size - instruction_descriptor.opcode_byte_count;
|
// image.sections.text.index += instruction_descriptor.size - instruction_descriptor.opcode_byte_count;
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn getEntryPoint(image: *const Result, comptime FunctionType: type) *const FunctionType {
|
fn getEntryPoint(image: *const Result, comptime FunctionType: type) *const FunctionType {
|
||||||
comptime {
|
comptime {
|
||||||
@ -89,101 +95,45 @@ const Result = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const Instruction = enum {
|
pub fn InstructionSelector(comptime Instruction: type) type {
|
||||||
jmp_rel_8,
|
return struct {
|
||||||
|
|
||||||
const Descriptor = struct {
|
|
||||||
operands: [4]Operand,
|
|
||||||
operand_count: u3,
|
|
||||||
operand_offset: u5,
|
|
||||||
size: u8,
|
|
||||||
opcode: [2]u8,
|
|
||||||
opcode_byte_count: u8,
|
|
||||||
|
|
||||||
fn getOperands(descriptor: Descriptor) []const Operand {
|
|
||||||
return descriptor.operands[0..descriptor.operand_count];
|
|
||||||
}
|
|
||||||
|
|
||||||
fn getOpcode(descriptor: Descriptor) []const u8 {
|
|
||||||
return descriptor.opcode[0..descriptor.opcode_byte_count];
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new(opcode_bytes: []const u8, operands: []const Operand) Descriptor {
|
|
||||||
// TODO: prefixes
|
|
||||||
var result = Descriptor{
|
|
||||||
.operands = undefined,
|
|
||||||
.operand_count = @intCast(operands.len),
|
|
||||||
.operand_offset = opcode_bytes.len,
|
|
||||||
.size = opcode_bytes.len,
|
|
||||||
.opcode = undefined,
|
|
||||||
.opcode_byte_count = opcode_bytes.len,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (opcode_bytes, result.opcode[0..opcode_bytes.len]) |opcode_byte, *out_opcode| {
|
|
||||||
out_opcode.* = opcode_byte;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (operands, result.operands[0..operands.len]) |operand, *out_operand| {
|
|
||||||
out_operand.* = operand;
|
|
||||||
result.size += operand.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const Operand = struct {
|
|
||||||
type: Type,
|
|
||||||
size: u8,
|
|
||||||
|
|
||||||
const Type = enum {
|
|
||||||
rel,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const rel8 = Instruction.Operand{
|
|
||||||
.type = .rel,
|
|
||||||
.size = @sizeOf(u8),
|
|
||||||
};
|
|
||||||
|
|
||||||
const instruction_descriptors = blk: {
|
|
||||||
var result = std.EnumArray(Instruction, Instruction.Descriptor).initUndefined();
|
|
||||||
result.getPtr(.jmp_rel_8).* = Instruction.Descriptor.new(&.{0xeb}, &[_]Instruction.Operand{rel8});
|
|
||||||
break :blk result;
|
|
||||||
};
|
|
||||||
|
|
||||||
const InstructionSelector = struct {
|
|
||||||
functions: ArrayList(Function),
|
functions: ArrayList(Function),
|
||||||
const Function = struct {
|
allocator: Allocator,
|
||||||
|
|
||||||
|
pub const Function = struct {
|
||||||
instructions: ArrayList(Instruction) = .{},
|
instructions: ArrayList(Instruction) = .{},
|
||||||
block_byte_counts: ArrayList(u16),
|
block_byte_counts: ArrayList(u16),
|
||||||
block_offsets: ArrayList(u32),
|
block_offsets: ArrayList(u32),
|
||||||
byte_count: u32 = 0,
|
relocations: ArrayList(u32) = .{},
|
||||||
relocations: ArrayList(Relocation) = .{},
|
|
||||||
block_map: AutoHashMap(ir.BasicBlock.Index, u32) = .{},
|
block_map: AutoHashMap(ir.BasicBlock.Index, u32) = .{},
|
||||||
const Relocation = struct {
|
byte_count: u32 = 0,
|
||||||
instruction: Instruction,
|
block_byte_count: u16 = 0,
|
||||||
source: u16,
|
|
||||||
destination: u16,
|
pub fn selectInstruction(function: *Function, allocator: Allocator, instruction: Instruction) !void {
|
||||||
block_offset: u16,
|
try function.instructions.append(allocator, instruction);
|
||||||
};
|
function.block_byte_count += Instruction.descriptors.get(instruction).size;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Selector = @This();
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get(comptime arch: std.Target.Cpu.Arch) type {
|
pub fn get(comptime arch: std.Target.Cpu.Arch) type {
|
||||||
const backend = switch (arch) {
|
const backend = switch (arch) {
|
||||||
.x86_64 => @import("x86_64.zig"),
|
.x86_64 => @import("x86_64.zig"),
|
||||||
else => @compileError("Architecture not supported"),
|
else => @compileError("Architecture not supported"),
|
||||||
};
|
};
|
||||||
_ = backend;
|
const Instruction = backend.Instruction;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
pub fn initialize(allocator: Allocator, intermediate: *ir.Result) !void {
|
pub fn initialize(allocator: Allocator, intermediate: *ir.Result) !void {
|
||||||
var result = try Result.create();
|
var result = try Result.create();
|
||||||
var function_iterator = intermediate.functions.iterator();
|
var function_iterator = intermediate.functions.iterator();
|
||||||
var instruction_selector = InstructionSelector{
|
const IS = InstructionSelector(Instruction);
|
||||||
.functions = try ArrayList(InstructionSelector.Function).initCapacity(allocator, intermediate.functions.len),
|
var instruction_selector = IS{
|
||||||
|
.functions = try ArrayList(IS.Function).initCapacity(allocator, intermediate.functions.len),
|
||||||
|
.allocator = allocator,
|
||||||
};
|
};
|
||||||
|
|
||||||
while (function_iterator.next()) |ir_function| {
|
while (function_iterator.next()) |ir_function| {
|
||||||
@ -200,72 +150,55 @@ pub fn get(comptime arch: std.Target.Cpu.Arch) type {
|
|||||||
for (ir_function.blocks.items) |block_index| {
|
for (ir_function.blocks.items) |block_index| {
|
||||||
const block = intermediate.blocks.get(block_index);
|
const block = intermediate.blocks.get(block_index);
|
||||||
function.block_offsets.appendAssumeCapacity(function.byte_count);
|
function.block_offsets.appendAssumeCapacity(function.byte_count);
|
||||||
var block_byte_count: u16 = 0;
|
function.block_byte_count = 0;
|
||||||
for (block.instructions.items) |instruction_index| {
|
for (block.instructions.items) |instruction_index| {
|
||||||
const instruction = intermediate.instructions.get(instruction_index).*;
|
const instruction = intermediate.instructions.get(instruction_index).*;
|
||||||
switch (instruction) {
|
try backend.selectInstruction(&instruction_selector, function, intermediate, instruction);
|
||||||
.phi => unreachable,
|
|
||||||
.ret => unreachable,
|
|
||||||
.jump => |jump_index| {
|
|
||||||
const jump = intermediate.jumps.get(jump_index);
|
|
||||||
const relocation = InstructionSelector.Function.Relocation{
|
|
||||||
.instruction = .jmp_rel_8,
|
|
||||||
.source = @intCast(function.block_map.get(jump.source) orelse unreachable),
|
|
||||||
.destination = @intCast(function.block_map.get(jump.destination) orelse unreachable),
|
|
||||||
.block_offset = block_byte_count,
|
|
||||||
};
|
|
||||||
try function.relocations.append(allocator, relocation);
|
|
||||||
block_byte_count += instruction_descriptors.get(.jmp_rel_8).size;
|
|
||||||
try function.instructions.append(allocator, .jmp_rel_8);
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
function.block_byte_counts.appendAssumeCapacity(block_byte_count);
|
function.block_byte_counts.appendAssumeCapacity(function.block_byte_count);
|
||||||
function.byte_count += block_byte_count;
|
function.byte_count += function.block_byte_count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (instruction_selector.functions.items) |function| {
|
for (instruction_selector.functions.items) |function| {
|
||||||
for (function.instructions.items) |instruction| switch (instruction) {
|
for (function.instructions.items) |instruction| backend.emitInstruction(&result, instruction, intermediate);
|
||||||
.jmp_rel_8 => result.appendOnlyOpcodeSkipInstructionBytes(instruction),
|
}
|
||||||
|
|
||||||
|
// for (instruction_selector.functions.items) |function| {
|
||||||
|
// var fix_size: bool = false;
|
||||||
|
// _ = fix_size;
|
||||||
|
// for (function.relocations.items) |instruction_index| {
|
||||||
|
// const instruction = function.instructions.items[instruction_index];
|
||||||
|
// const relative = instruction.jmp_rel_8;
|
||||||
|
// const source_block = relative.source;
|
||||||
|
// const destination_block = relative.destination;
|
||||||
|
// const source_offset = function.block_offsets.items[source_block];
|
||||||
|
// const destination_offset = function.block_offsets.items[destination_block];
|
||||||
|
// std.debug.print("Source offset: {}. Destination: {}\n", .{ source_offset, destination_offset });
|
||||||
|
// const instruction_descriptor = instruction_descriptors.get(relative.instruction);
|
||||||
|
// const instruction_offset = source_offset + relative.block_offset;
|
||||||
|
// const really_source_offset = instruction_offset + instruction_descriptor.size;
|
||||||
|
// const displacement = @as(i64, destination_offset) - @as(i64, really_source_offset);
|
||||||
|
//
|
||||||
|
// const operands = instruction_descriptor.getOperands();
|
||||||
|
// switch (operands.len) {
|
||||||
|
// 1 => switch (operands[0].size) {
|
||||||
|
// @sizeOf(u8) => {
|
||||||
|
// if (displacement >= std.math.minInt(i8) and displacement <= std.math.maxInt(i8)) {
|
||||||
|
// const writer_index = instruction_offset + instruction_descriptor.operand_offset;
|
||||||
|
// std.debug.print("Instruction offset: {}. Operand offset: {}. Writer index: {}. displacement: {}\n", .{ instruction_offset, instruction_descriptor.operand_offset, writer_index, displacement });
|
||||||
|
// result.sections.text.content[writer_index] = @bitCast(@as(i8, @intCast(displacement)));
|
||||||
|
// } else {
|
||||||
|
// unreachable;
|
||||||
|
// }
|
||||||
|
// },
|
||||||
// else => unreachable,
|
// else => unreachable,
|
||||||
};
|
// },
|
||||||
}
|
// else => unreachable,
|
||||||
|
// }
|
||||||
for (instruction_selector.functions.items) |function| {
|
// }
|
||||||
var fix_size: bool = false;
|
// }
|
||||||
_ = fix_size;
|
|
||||||
for (function.relocations.items) |relocation| {
|
|
||||||
std.debug.print("RELOC: {}\n", .{relocation});
|
|
||||||
const source_block = relocation.source;
|
|
||||||
const destination_block = relocation.destination;
|
|
||||||
const source_offset = function.block_offsets.items[source_block];
|
|
||||||
const destination_offset = function.block_offsets.items[destination_block];
|
|
||||||
std.debug.print("Source offset: {}. Destination: {}\n", .{ source_offset, destination_offset });
|
|
||||||
const instruction_descriptor = instruction_descriptors.get(relocation.instruction);
|
|
||||||
const instruction_offset = source_offset + relocation.block_offset;
|
|
||||||
const really_source_offset = instruction_offset + instruction_descriptor.size;
|
|
||||||
const displacement = @as(i64, destination_offset) - @as(i64, really_source_offset);
|
|
||||||
|
|
||||||
const operands = instruction_descriptor.getOperands();
|
|
||||||
switch (operands.len) {
|
|
||||||
1 => switch (operands[0].size) {
|
|
||||||
@sizeOf(u8) => {
|
|
||||||
if (displacement >= std.math.minInt(i8) and displacement <= std.math.maxInt(i8)) {
|
|
||||||
const writer_index = instruction_offset + instruction_descriptor.operand_offset;
|
|
||||||
std.debug.print("Instruction offset: {}. Operand offset: {}. Writer index: {}. displacement: {}\n", .{ instruction_offset, instruction_descriptor.operand_offset, writer_index, displacement });
|
|
||||||
result.sections.text.content[writer_index] = @bitCast(@as(i8, @intCast(displacement)));
|
|
||||||
} else {
|
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
else => unreachable,
|
|
||||||
},
|
|
||||||
else => unreachable,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const text_section = result.sections.text.content[0..result.sections.text.index];
|
const text_section = result.sections.text.content[0..result.sections.text.index];
|
||||||
for (text_section) |byte| {
|
for (text_section) |byte| {
|
||||||
@ -274,328 +207,3 @@ pub fn get(comptime arch: std.Target.Cpu.Arch) type {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const Rex = enum(u8) {
|
|
||||||
b = upper_4_bits | (1 << 0),
|
|
||||||
x = upper_4_bits | (1 << 1),
|
|
||||||
r = upper_4_bits | (1 << 2),
|
|
||||||
w = upper_4_bits | (1 << 3),
|
|
||||||
|
|
||||||
const upper_4_bits = 0b100_0000;
|
|
||||||
};
|
|
||||||
|
|
||||||
const GPRegister = enum(u4) {
|
|
||||||
a = 0,
|
|
||||||
c = 1,
|
|
||||||
d = 2,
|
|
||||||
b = 3,
|
|
||||||
sp = 4,
|
|
||||||
bp = 5,
|
|
||||||
si = 6,
|
|
||||||
di = 7,
|
|
||||||
r8 = 8,
|
|
||||||
r9 = 9,
|
|
||||||
r10 = 10,
|
|
||||||
r11 = 11,
|
|
||||||
r12 = 12,
|
|
||||||
r13 = 13,
|
|
||||||
r14 = 14,
|
|
||||||
r15 = 15,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const BasicGPRegister = enum(u3) {
|
|
||||||
a = 0,
|
|
||||||
c = 1,
|
|
||||||
d = 2,
|
|
||||||
b = 3,
|
|
||||||
sp = 4,
|
|
||||||
bp = 5,
|
|
||||||
si = 6,
|
|
||||||
di = 7,
|
|
||||||
};
|
|
||||||
|
|
||||||
const prefix_lock = 0xf0;
|
|
||||||
const prefix_repne_nz = 0xf2;
|
|
||||||
const prefix_rep = 0xf3;
|
|
||||||
const prefix_rex_w = [1]u8{@intFromEnum(Rex.w)};
|
|
||||||
const prefix_16_bit_operand = [1]u8{0x66};
|
|
||||||
|
|
||||||
const jmp_rel_32 = 0xe9;
|
|
||||||
const ret = 0xc3;
|
|
||||||
const mov_a_imm = [1]u8{0xb8};
|
|
||||||
const mov_reg_imm8: u8 = 0xb0;
|
|
||||||
|
|
||||||
fn intToArrayOfBytes(integer: anytype) [@sizeOf(@TypeOf(integer))]u8 {
|
|
||||||
comptime {
|
|
||||||
assert(@typeInfo(@TypeOf(integer)) == .Int);
|
|
||||||
}
|
|
||||||
|
|
||||||
return @as([@sizeOf(@TypeOf(integer))]u8, @bitCast(integer));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn movAImm(image: *Result, integer: anytype) void {
|
|
||||||
const T = @TypeOf(integer);
|
|
||||||
image.appendCode(&(switch (T) {
|
|
||||||
u8, i8 => .{mov_reg_imm8 | @intFromEnum(GPRegister.a)},
|
|
||||||
u16, i16 => prefix_16_bit_operand ++ mov_a_imm,
|
|
||||||
u32, i32 => mov_a_imm,
|
|
||||||
u64, i64 => prefix_rex_w ++ mov_a_imm,
|
|
||||||
else => @compileError("Unsupported"),
|
|
||||||
} ++ intToArrayOfBytes(integer)));
|
|
||||||
}
|
|
||||||
|
|
||||||
test "ret void" {
|
|
||||||
var image = try Result.create();
|
|
||||||
image.appendCodeByte(ret);
|
|
||||||
|
|
||||||
const function_pointer = image.getEntryPoint(fn () callconv(jit_callconv) void);
|
|
||||||
function_pointer();
|
|
||||||
}
|
|
||||||
|
|
||||||
const integer_types_to_test = [_]type{ u8, u16, u32, u64, i8, i16, i32, i64 };
|
|
||||||
|
|
||||||
fn getMaxInteger(comptime T: type) T {
|
|
||||||
comptime {
|
|
||||||
assert(@typeInfo(T) == .Int);
|
|
||||||
}
|
|
||||||
|
|
||||||
return switch (@typeInfo(T).Int.signedness) {
|
|
||||||
.unsigned => std.math.maxInt(T),
|
|
||||||
.signed => std.math.minInt(T),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
test "ret integer" {
|
|
||||||
inline for (integer_types_to_test) |Int| {
|
|
||||||
var image = try Result.create();
|
|
||||||
const expected_number = getMaxInteger(Int);
|
|
||||||
|
|
||||||
movAImm(&image, expected_number);
|
|
||||||
image.appendCodeByte(ret);
|
|
||||||
|
|
||||||
const function_pointer = image.getEntryPoint(fn () callconv(jit_callconv) Int);
|
|
||||||
const result = function_pointer();
|
|
||||||
try expect(result == expected_number);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const LastByte = packed struct(u8) {
|
|
||||||
dst: BasicGPRegister,
|
|
||||||
src: BasicGPRegister,
|
|
||||||
always_on: u2 = 0b11,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn movRmR(image: *Result, comptime T: type, dst: BasicGPRegister, src: BasicGPRegister) void {
|
|
||||||
dstRmSrcR(image, T, .mov, dst, src);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dstRmSrcR(image: *Result, comptime T: type, opcode: OpcodeRmR, dst: BasicGPRegister, src: BasicGPRegister) void {
|
|
||||||
const last_byte: u8 = @bitCast(LastByte{
|
|
||||||
.dst = dst,
|
|
||||||
.src = src,
|
|
||||||
});
|
|
||||||
const opcode_byte = @intFromEnum(opcode);
|
|
||||||
|
|
||||||
const bytes = switch (T) {
|
|
||||||
u8, i8 => blk: {
|
|
||||||
const base = [_]u8{ opcode_byte - 1, last_byte };
|
|
||||||
if (@intFromEnum(dst) >= @intFromEnum(BasicGPRegister.sp) or @intFromEnum(src) >= @intFromEnum(BasicGPRegister.sp)) {
|
|
||||||
image.appendCodeByte(0x40);
|
|
||||||
}
|
|
||||||
|
|
||||||
break :blk base;
|
|
||||||
},
|
|
||||||
u16, i16 => prefix_16_bit_operand ++ .{ opcode_byte, last_byte },
|
|
||||||
u32, i32 => .{ opcode_byte, last_byte },
|
|
||||||
u64, i64 => prefix_rex_w ++ .{ opcode_byte, last_byte },
|
|
||||||
else => @compileError("Not supported"),
|
|
||||||
};
|
|
||||||
|
|
||||||
image.appendCode(&bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "ret integer argument" {
|
|
||||||
inline for (integer_types_to_test) |Int| {
|
|
||||||
var image = try Result.create();
|
|
||||||
const number = getMaxInteger(Int);
|
|
||||||
|
|
||||||
movRmR(&image, Int, .a, .di);
|
|
||||||
image.appendCodeByte(ret);
|
|
||||||
|
|
||||||
const functionPointer = image.getEntryPoint(fn (Int) callconv(jit_callconv) Int);
|
|
||||||
const result = functionPointer(number);
|
|
||||||
try expectEqual(number, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var r = std.rand.Pcg.init(0xffffffffffffffff);
|
|
||||||
|
|
||||||
fn getRandomNumberRange(comptime T: type, min: T, max: T) T {
|
|
||||||
const random = r.random();
|
|
||||||
return switch (@typeInfo(T).Int.signedness) {
|
|
||||||
.signed => random.intRangeAtMost(T, min, max),
|
|
||||||
.unsigned => random.uintAtMost(T, max),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn subRmR(image: *Result, comptime T: type, dst: BasicGPRegister, src: BasicGPRegister) void {
|
|
||||||
dstRmSrcR(image, T, .sub, dst, src);
|
|
||||||
}
|
|
||||||
|
|
||||||
test "ret sub arguments" {
|
|
||||||
inline for (integer_types_to_test) |Int| {
|
|
||||||
var image = try Result.create();
|
|
||||||
const a = getRandomNumberRange(Int, std.math.minInt(Int) / 2, std.math.maxInt(Int) / 2);
|
|
||||||
const b = getRandomNumberRange(Int, std.math.minInt(Int) / 2, a);
|
|
||||||
|
|
||||||
movRmR(&image, Int, .a, .di);
|
|
||||||
subRmR(&image, Int, .a, .si);
|
|
||||||
image.appendCodeByte(ret);
|
|
||||||
|
|
||||||
const functionPointer = image.getEntryPoint(fn (Int, Int) callconv(jit_callconv) Int);
|
|
||||||
const result = functionPointer(a, b);
|
|
||||||
try expectEqual(a - b, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const OpcodeRmR = enum(u8) {
|
|
||||||
add = 0x01,
|
|
||||||
@"or" = 0x09,
|
|
||||||
@"and" = 0x21,
|
|
||||||
sub = 0x29,
|
|
||||||
xor = 0x31,
|
|
||||||
@"test" = 0x85,
|
|
||||||
mov = 0x89,
|
|
||||||
};
|
|
||||||
|
|
||||||
test "test binary operations" {
|
|
||||||
inline for (integer_types_to_test) |T| {
|
|
||||||
const test_cases = [_]TestIntegerBinaryOperation(T){
|
|
||||||
.{
|
|
||||||
.opcode = .add,
|
|
||||||
.callback = struct {
|
|
||||||
fn callback(a: T, b: T) T {
|
|
||||||
return @addWithOverflow(a, b)[0];
|
|
||||||
}
|
|
||||||
}.callback,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
.opcode = .sub,
|
|
||||||
.callback = struct {
|
|
||||||
fn callback(a: T, b: T) T {
|
|
||||||
return @subWithOverflow(a, b)[0];
|
|
||||||
}
|
|
||||||
}.callback,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
.opcode = .@"or",
|
|
||||||
.callback = struct {
|
|
||||||
fn callback(a: T, b: T) T {
|
|
||||||
return a | b;
|
|
||||||
}
|
|
||||||
}.callback,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
.opcode = .@"and",
|
|
||||||
.callback = struct {
|
|
||||||
fn callback(a: T, b: T) T {
|
|
||||||
return a & b;
|
|
||||||
}
|
|
||||||
}.callback,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
.opcode = .xor,
|
|
||||||
.callback = struct {
|
|
||||||
fn callback(a: T, b: T) T {
|
|
||||||
return a ^ b;
|
|
||||||
}
|
|
||||||
}.callback,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
for (test_cases) |test_case| {
|
|
||||||
try test_case.runTest();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn TestIntegerBinaryOperation(comptime T: type) type {
|
|
||||||
const should_log = false;
|
|
||||||
return struct {
|
|
||||||
callback: *const fn (a: T, b: T) T,
|
|
||||||
opcode: OpcodeRmR,
|
|
||||||
|
|
||||||
pub fn runTest(test_case: @This()) !void {
|
|
||||||
for (0..10) |_| {
|
|
||||||
var image = try Result.create();
|
|
||||||
const a = getRandomNumberRange(T, std.math.minInt(T) / 2, std.math.maxInt(T) / 2);
|
|
||||||
const b = getRandomNumberRange(T, std.math.minInt(T) / 2, a);
|
|
||||||
movRmR(&image, T, .a, .di);
|
|
||||||
dstRmSrcR(&image, T, test_case.opcode, .a, .si);
|
|
||||||
image.appendCodeByte(ret);
|
|
||||||
|
|
||||||
const functionPointer = image.getEntryPoint(fn (T, T) callconv(jit_callconv) T);
|
|
||||||
const expected = test_case.callback(a, b);
|
|
||||||
const result = functionPointer(a, b);
|
|
||||||
if (should_log) {
|
|
||||||
log.err("{s} {}, {} ({})", .{ @tagName(test_case.opcode), a, b, T });
|
|
||||||
}
|
|
||||||
try expectEqual(expected, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
test "call after" {
|
|
||||||
var image = try Result.create();
|
|
||||||
const jump_patch_offset = image.sections.text.index + 1;
|
|
||||||
image.appendCode(&.{ 0xe8, 0x00, 0x00, 0x00, 0x00 });
|
|
||||||
const jump_source = image.sections.text.index;
|
|
||||||
image.appendCodeByte(ret);
|
|
||||||
const jump_target = image.sections.text.index;
|
|
||||||
@as(*align(1) u32, @ptrCast(&image.sections.text.content[jump_patch_offset])).* = @intCast(jump_target - jump_source);
|
|
||||||
image.appendCodeByte(ret);
|
|
||||||
|
|
||||||
const functionPointer = image.getEntryPoint(fn () callconv(jit_callconv) void);
|
|
||||||
functionPointer();
|
|
||||||
}
|
|
||||||
|
|
||||||
test "call before" {
|
|
||||||
var image = try Result.create();
|
|
||||||
const first_jump_patch_offset = image.sections.text.index + 1;
|
|
||||||
const first_call = .{0xe8} ++ .{ 0x00, 0x00, 0x00, 0x00 };
|
|
||||||
image.appendCode(&first_call);
|
|
||||||
const first_jump_source = image.sections.text.index;
|
|
||||||
image.appendCodeByte(ret);
|
|
||||||
const second_jump_target = image.sections.text.index;
|
|
||||||
image.appendCodeByte(ret);
|
|
||||||
const first_jump_target = image.sections.text.index;
|
|
||||||
@as(*align(1) i32, @ptrCast(&image.sections.text.content[first_jump_patch_offset])).* = @intCast(first_jump_target - first_jump_source);
|
|
||||||
const second_call = .{0xe8} ++ @as([4]u8, @bitCast(@as(i32, @intCast(@as(i64, @intCast(second_jump_target)) - @as(i64, @intCast(image.sections.text.index + 5))))));
|
|
||||||
image.appendCode(&second_call);
|
|
||||||
image.appendCodeByte(ret);
|
|
||||||
|
|
||||||
const functionPointer = image.getEntryPoint(fn () callconv(jit_callconv) void);
|
|
||||||
functionPointer();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn runTest(allocator: Allocator, ir_result: *const ir.Result) !Result {
|
|
||||||
_ = allocator;
|
|
||||||
|
|
||||||
var image = try Result.create();
|
|
||||||
|
|
||||||
var entry_point: u32 = 0;
|
|
||||||
_ = entry_point;
|
|
||||||
|
|
||||||
for (ir_result.functions.items) |*function| {
|
|
||||||
for (function.instructions.items) |instruction| {
|
|
||||||
switch (instruction.id) {
|
|
||||||
.ret_void => {
|
|
||||||
image.appendCodeByte(ret);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
@ -16,6 +16,9 @@ pub const Result = struct {
|
|||||||
blocks: BlockList(BasicBlock) = .{},
|
blocks: BlockList(BasicBlock) = .{},
|
||||||
instructions: BlockList(Instruction) = .{},
|
instructions: BlockList(Instruction) = .{},
|
||||||
jumps: BlockList(Jump) = .{},
|
jumps: BlockList(Jump) = .{},
|
||||||
|
values: BlockList(Value) = .{},
|
||||||
|
syscalls: BlockList(Syscall) = .{},
|
||||||
|
loads: BlockList(Load) = .{},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, main_file: Compilation.Type.Index) !Result {
|
pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, main_file: Compilation.Type.Index) !Result {
|
||||||
@ -57,10 +60,13 @@ pub const BasicBlock = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const Instruction = union(enum) {
|
pub const Instruction = union(enum) {
|
||||||
jump: Jump.Index,
|
jump: Jump.Index,
|
||||||
|
load: Load.Index,
|
||||||
phi: Phi.Index,
|
phi: Phi.Index,
|
||||||
ret: Ret,
|
ret: Ret,
|
||||||
|
syscall: Syscall.Index,
|
||||||
|
@"unreachable",
|
||||||
|
|
||||||
pub const List = BlockList(@This());
|
pub const List = BlockList(@This());
|
||||||
pub const Index = List.Index;
|
pub const Index = List.Index;
|
||||||
@ -83,6 +89,38 @@ pub const Jump = struct {
|
|||||||
pub const Index = List.Index;
|
pub const Index = List.Index;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Syscall = struct {
|
||||||
|
arguments: ArrayList(Value.Index),
|
||||||
|
pub const List = BlockList(@This());
|
||||||
|
pub const Index = List.Index;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Load = struct {
|
||||||
|
value: Value.Index,
|
||||||
|
|
||||||
|
pub const List = BlockList(@This());
|
||||||
|
pub const Index = List.Index;
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Value = union(enum) {
|
||||||
|
integer: Integer,
|
||||||
|
load: Load.Index,
|
||||||
|
pub const List = BlockList(@This());
|
||||||
|
pub const Index = List.Index;
|
||||||
|
|
||||||
|
pub fn isInMemory(value: Value) bool {
|
||||||
|
return switch (value) {
|
||||||
|
.integer => false,
|
||||||
|
.load => true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Integer = struct {
|
||||||
|
value: u64,
|
||||||
|
sign: bool,
|
||||||
|
};
|
||||||
|
|
||||||
const Function = struct {
|
const Function = struct {
|
||||||
blocks: ArrayList(BasicBlock.Index) = .{},
|
blocks: ArrayList(BasicBlock.Index) = .{},
|
||||||
pub const List = BlockList(@This());
|
pub const List = BlockList(@This());
|
||||||
@ -207,11 +245,66 @@ pub const Builder = struct {
|
|||||||
builder.current_basic_block = loop_prologue_block;
|
builder.current_basic_block = loop_prologue_block;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
.syscall => |syscall_index| {
|
||||||
|
const sema_syscall = builder.module.syscalls.get(syscall_index);
|
||||||
|
var arguments = try ArrayList(Value.Index).initCapacity(builder.allocator, sema_syscall.argument_count + 1);
|
||||||
|
|
||||||
|
const sema_syscall_number = sema_syscall.number;
|
||||||
|
assert(sema_syscall_number.valid);
|
||||||
|
const number_value_index = try builder.emitValue(sema_syscall_number);
|
||||||
|
|
||||||
|
arguments.appendAssumeCapacity(number_value_index);
|
||||||
|
|
||||||
|
for (sema_syscall.getArguments()) |sema_syscall_argument| {
|
||||||
|
assert(sema_syscall_argument.valid);
|
||||||
|
const argument_value_index = try builder.emitValue(sema_syscall_argument);
|
||||||
|
arguments.appendAssumeCapacity(argument_value_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = try builder.append(.{
|
||||||
|
.syscall = try builder.ir.syscalls.append(builder.allocator, .{
|
||||||
|
.arguments = arguments,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
.@"unreachable" => _ = try builder.append(.{
|
||||||
|
.@"unreachable" = {},
|
||||||
|
}),
|
||||||
else => |t| @panic(@tagName(t)),
|
else => |t| @panic(@tagName(t)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load(builder: *Builder, value_index: Value.Index) !Value.Index {
|
||||||
|
print("Doing load!\n", .{});
|
||||||
|
|
||||||
|
const load_index = try builder.ir.loads.append(builder.allocator, .{
|
||||||
|
.value = value_index,
|
||||||
|
});
|
||||||
|
const instruction_index = try builder.append(.{
|
||||||
|
.load = load_index,
|
||||||
|
});
|
||||||
|
_ = instruction_index;
|
||||||
|
const result = try builder.ir.values.append(builder.allocator, .{
|
||||||
|
.load = load_index,
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emitValue(builder: *Builder, sema_value_index: Compilation.Value.Index) !Value.Index {
|
||||||
|
const sema_value = builder.module.values.get(sema_value_index).*;
|
||||||
|
return switch (sema_value) {
|
||||||
|
// TODO
|
||||||
|
.integer => |integer| try builder.ir.values.append(builder.allocator, .{
|
||||||
|
.integer = .{
|
||||||
|
.value = integer,
|
||||||
|
.sign = false,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
else => |t| @panic(@tagName(t)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn jump(builder: *Builder, jump_descriptor: Jump) !Jump.Index {
|
fn jump(builder: *Builder, jump_descriptor: Jump) !Jump.Index {
|
||||||
const destination_block = builder.ir.blocks.get(jump_descriptor.destination);
|
const destination_block = builder.ir.blocks.get(jump_descriptor.destination);
|
||||||
assert(!destination_block.sealed);
|
assert(!destination_block.sealed);
|
||||||
|
@ -0,0 +1,414 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const print = std.debug.print;
|
||||||
|
const emit = @import("emit.zig");
|
||||||
|
const ir = @import("./intermediate_representation.zig");
|
||||||
|
|
||||||
|
const InstructionSelector = emit.InstructionSelector(Instruction);
|
||||||
|
|
||||||
|
const Size = enum(u2) {
|
||||||
|
one = 0,
|
||||||
|
two = 1,
|
||||||
|
four = 2,
|
||||||
|
eight = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn selectInstruction(instruction_selector: *InstructionSelector, function: *InstructionSelector.Function, intermediate: *ir.Result, instruction: ir.Instruction) !void {
|
||||||
|
switch (instruction) {
|
||||||
|
.@"unreachable" => try function.instructions.append(instruction_selector.allocator, .{ .ud2 = {} }),
|
||||||
|
.load => |load_index| {
|
||||||
|
const load = intermediate.loads.get(load_index).*;
|
||||||
|
const load_value = intermediate.values.get(load.value).*;
|
||||||
|
switch (load_value) {
|
||||||
|
.integer => |integer| {
|
||||||
|
_ = integer;
|
||||||
|
unreachable;
|
||||||
|
},
|
||||||
|
else => |t| @panic(@tagName(t)),
|
||||||
|
}
|
||||||
|
unreachable;
|
||||||
|
},
|
||||||
|
.syscall => |syscall_index| {
|
||||||
|
const syscall = intermediate.syscalls.get(syscall_index);
|
||||||
|
for (syscall.arguments.items, syscall_registers[0..syscall.arguments.items.len]) |argument_index, syscall_register| {
|
||||||
|
const argument = intermediate.values.get(argument_index).*;
|
||||||
|
switch (argument) {
|
||||||
|
.integer => |integer| {
|
||||||
|
if (integer.value == 0) {
|
||||||
|
try function.instructions.append(instruction_selector.allocator, .{
|
||||||
|
.xor_rm_r = .{
|
||||||
|
.destination = @enumFromInt(@intFromEnum(syscall_register)),
|
||||||
|
.source = @enumFromInt(@intFromEnum(syscall_register)),
|
||||||
|
.size = .four,
|
||||||
|
.direct = true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else if (integer.value <= std.math.maxInt(u32)) {
|
||||||
|
try function.instructions.append(instruction_selector.allocator, .{
|
||||||
|
.mov_r_imm = .{
|
||||||
|
.register_size = .four,
|
||||||
|
.register = @enumFromInt(@intFromEnum(syscall_register)),
|
||||||
|
.immediate = argument_index,
|
||||||
|
.immediate_size = .four,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// 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)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try function.instructions.append(instruction_selector.allocator, .{
|
||||||
|
.syscall = {},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
.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 index = function.instructions.items.len;
|
||||||
|
// try function.relocations.append(instruction_selector.allocator, @intCast(index));
|
||||||
|
// try function.instructions.append(instruction_selector.allocator, .{
|
||||||
|
// .jmp_rel_8 = relocation,
|
||||||
|
// });
|
||||||
|
unreachable;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const RegisterImmediate = struct {
|
||||||
|
immediate: ir.Value.Index,
|
||||||
|
register: GPRegister,
|
||||||
|
register_size: Size,
|
||||||
|
immediate_size: Size,
|
||||||
|
};
|
||||||
|
|
||||||
|
const RegisterMemoryRegister = struct {
|
||||||
|
destination: GPRegister,
|
||||||
|
source: GPRegister,
|
||||||
|
size: Size,
|
||||||
|
direct: bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
const RmResult = struct {
|
||||||
|
rex: Rex,
|
||||||
|
mod_rm: ModRm,
|
||||||
|
};
|
||||||
|
|
||||||
|
const RmAndRexArguments = packed struct {
|
||||||
|
rm: GPRegister,
|
||||||
|
reg: GPRegister,
|
||||||
|
direct: bool,
|
||||||
|
bit64: bool,
|
||||||
|
sib: bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
// fn computeRmAndRex(args: RmAndRexArguments) RmResult {
|
||||||
|
// _ = register_memory_register;
|
||||||
|
// const rex_byte = Rex{
|
||||||
|
// .b = @intFromEnum(args.rm) > std.math.maxInt(u3),
|
||||||
|
// .x = args.sib,
|
||||||
|
// .r = @intFromEnum(args.reg) > std.math.maxInt(u3),
|
||||||
|
// .w = args.bit64,
|
||||||
|
// };
|
||||||
|
// var rex_byte = std.mem.zeroes(Rex);
|
||||||
|
// if (@intFromEnum(rm) > std.math.maxInt(u3))
|
||||||
|
// }
|
||||||
|
fn emitImmediate(result: *emit.Result, intermediate: *ir.Result, value_index: ir.Value.Index, size: Size) void {
|
||||||
|
const value = intermediate.values.get(value_index);
|
||||||
|
const integer = value.integer.value;
|
||||||
|
const integer_bytes = switch (size) {
|
||||||
|
.one => std.mem.asBytes(&@as(u8, @intCast(integer))),
|
||||||
|
.two => std.mem.asBytes(&@as(u16, @intCast(integer))),
|
||||||
|
.four => std.mem.asBytes(&@as(u32, @intCast(integer))),
|
||||||
|
.eight => std.mem.asBytes(&@as(u64, @intCast(integer))),
|
||||||
|
};
|
||||||
|
result.appendCode(integer_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ModRm = packed struct(u8) {
|
||||||
|
rm: u3,
|
||||||
|
reg: u3,
|
||||||
|
mod: u2,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn emitInstruction(result: *emit.Result, instruction: Instruction, intermediate: *ir.Result) void {
|
||||||
|
switch (instruction) {
|
||||||
|
inline .xor_rm_r => |register_memory_register, tag| {
|
||||||
|
const rm = register_memory_register.destination;
|
||||||
|
const reg = register_memory_register.source;
|
||||||
|
const rex_byte = Rex{
|
||||||
|
.b = @intFromEnum(rm) > std.math.maxInt(u3),
|
||||||
|
.x = false, //args.sib,
|
||||||
|
.r = @intFromEnum(reg) > std.math.maxInt(u3),
|
||||||
|
.w = register_memory_register.size == .eight,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (@as(u4, @truncate(@as(u8, @bitCast(rex_byte)))) != 0) {
|
||||||
|
result.appendCodeByte(@bitCast(rex_byte));
|
||||||
|
}
|
||||||
|
|
||||||
|
const modrm = ModRm{
|
||||||
|
.rm = @truncate(@intFromEnum(rm)),
|
||||||
|
.reg = @truncate(@intFromEnum(reg)),
|
||||||
|
.mod = @as(u2, @intFromBool(register_memory_register.direct)) << 1 | @intFromBool(register_memory_register.direct),
|
||||||
|
};
|
||||||
|
// _ = modrm;
|
||||||
|
const opcode = tag.getOpcode(&.{
|
||||||
|
.{
|
||||||
|
.register_memory = .{
|
||||||
|
.value = register_memory_register.destination,
|
||||||
|
.size = register_memory_register.size,
|
||||||
|
.direct = register_memory_register.direct,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.register = .{
|
||||||
|
.value = register_memory_register.source,
|
||||||
|
.size = register_memory_register.size,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
result.appendCode(opcode);
|
||||||
|
result.appendCodeByte(@bitCast(modrm));
|
||||||
|
},
|
||||||
|
inline .mov_r_imm => |register_immediate, tag| {
|
||||||
|
const opcode = tag.getOpcode(&.{
|
||||||
|
.{
|
||||||
|
.register = .{
|
||||||
|
.value = register_immediate.register,
|
||||||
|
.size = register_immediate.register_size,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.immediate = register_immediate.immediate_size,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
assert(opcode.len == 1);
|
||||||
|
const opcode_byte = opcode[0] | @intFromEnum(register_immediate.register);
|
||||||
|
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;
|
||||||
|
// },
|
||||||
|
inline .syscall, .ud2 => |_, tag| {
|
||||||
|
const opcode = tag.getOpcode(&.{});
|
||||||
|
result.appendCode(opcode);
|
||||||
|
},
|
||||||
|
// else => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Instruction = union(Id) {
|
||||||
|
xor_rm_r: RegisterMemoryRegister,
|
||||||
|
mov_r_imm: RegisterImmediate,
|
||||||
|
// jmp_rel_8: LocalRelative,
|
||||||
|
// mov_reg_imm32: struct {
|
||||||
|
// destination: GPRegister,
|
||||||
|
// source: u32,
|
||||||
|
// },
|
||||||
|
// xor_reg32_reg32: struct {
|
||||||
|
// destination: GPRegister,
|
||||||
|
// source: GPRegister,
|
||||||
|
// },
|
||||||
|
syscall,
|
||||||
|
ud2,
|
||||||
|
|
||||||
|
const Id = enum {
|
||||||
|
xor_rm_r,
|
||||||
|
mov_r_imm,
|
||||||
|
// jmp_rel_8,
|
||||||
|
// mov_reg_imm32,
|
||||||
|
// xor_reg32_reg32,
|
||||||
|
syscall,
|
||||||
|
ud2,
|
||||||
|
|
||||||
|
fn getOpcode(comptime instruction: Instruction.Id, operands: []const Operand) []const u8 {
|
||||||
|
return switch (instruction) {
|
||||||
|
.mov_r_imm => switch (operands[0].register.size) {
|
||||||
|
.one => &.{0xb0},
|
||||||
|
.two, .four, .eight => &.{0xb8},
|
||||||
|
},
|
||||||
|
.syscall => &.{ 0x0f, 0x05 },
|
||||||
|
.ud2 => &.{ 0x0f, 0x0b },
|
||||||
|
.xor_rm_r => switch (operands[0].register_memory.size) {
|
||||||
|
.one => &.{0x30},
|
||||||
|
.two, .four, .eight => &.{0x31},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Operand = union(enum) {
|
||||||
|
displacement,
|
||||||
|
register: struct {
|
||||||
|
value: GPRegister,
|
||||||
|
size: Size,
|
||||||
|
},
|
||||||
|
// TODO
|
||||||
|
register_memory: struct {
|
||||||
|
value: GPRegister,
|
||||||
|
size: Size,
|
||||||
|
direct: bool,
|
||||||
|
},
|
||||||
|
immediate: Size,
|
||||||
|
|
||||||
|
const Id = enum {
|
||||||
|
displacement,
|
||||||
|
register,
|
||||||
|
register_memory,
|
||||||
|
immediate,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const descriptors = blk: {
|
||||||
|
var result = std.EnumArray(Instruction.Id, Instruction.Descriptor).initUndefined();
|
||||||
|
result.getPtr(.jmp_rel_8).* = Instruction.Descriptor.new(&.{0xeb}, &[_]Instruction.Operand{rel8});
|
||||||
|
result.getPtr(.mov_reg_imm32).* = Instruction.Descriptor.new(&.{0xb8}, &[_]Instruction.Operand{ reg32, imm32 });
|
||||||
|
result.getPtr(.xor_reg_reg).* = Instruction.Descriptor.new(&.{0x31}, &[_]Instruction.Operand{ reg32, reg32 });
|
||||||
|
result.getPtr(.syscall).* = Instruction.Descriptor.new(&.{ 0x0f, 0x05 }, &.{});
|
||||||
|
result.getPtr(.ud2).* = Instruction.Descriptor.new(&.{ 0x0f, 0x0b }, &.{});
|
||||||
|
break :blk result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Descriptor = struct {
|
||||||
|
operands: [4]Operand,
|
||||||
|
operand_count: u3,
|
||||||
|
operand_offset: u5,
|
||||||
|
size: u8,
|
||||||
|
opcode: [3]u8,
|
||||||
|
opcode_byte_count: u8,
|
||||||
|
|
||||||
|
fn getOperands(descriptor: Descriptor) []const Operand {
|
||||||
|
return descriptor.operands[0..descriptor.operand_count];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(opcode_bytes: []const u8, operands: []const Operand) Descriptor {
|
||||||
|
// TODO: prefixes
|
||||||
|
var result = Descriptor{
|
||||||
|
.operands = undefined,
|
||||||
|
.operand_count = @intCast(operands.len),
|
||||||
|
.operand_offset = opcode_bytes.len,
|
||||||
|
.size = opcode_bytes.len,
|
||||||
|
.opcode = .{ 0, 0 },
|
||||||
|
.opcode_byte_count = opcode_bytes.len,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (opcode_bytes.len == 1) {
|
||||||
|
result.opcode[1] = opcode_bytes[0];
|
||||||
|
} else for (opcode_bytes, result.opcode[0..opcode_bytes.len]) |opcode_byte, *out_opcode| {
|
||||||
|
out_opcode.* = opcode_byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (operands, result.operands[0..operands.len]) |operand, *out_operand| {
|
||||||
|
out_operand.* = operand;
|
||||||
|
result.size += operand.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const LocalRelative = struct {
|
||||||
|
instruction: Instruction.Id,
|
||||||
|
source: u16,
|
||||||
|
destination: u16,
|
||||||
|
offset_in_block: u16,
|
||||||
|
};
|
||||||
|
|
||||||
|
const rel8 = Instruction.Operand{
|
||||||
|
.type = .relative,
|
||||||
|
.size = @sizeOf(u8),
|
||||||
|
};
|
||||||
|
|
||||||
|
const reg32 = Instruction.Operand{
|
||||||
|
.type = .register,
|
||||||
|
.size = @sizeOf(u32),
|
||||||
|
};
|
||||||
|
|
||||||
|
const imm32 = Instruction.Operand{
|
||||||
|
.type = .immediate,
|
||||||
|
.size = @sizeOf(u32),
|
||||||
|
};
|
||||||
|
|
||||||
|
const Rex = packed struct(u8) {
|
||||||
|
b: bool,
|
||||||
|
x: bool,
|
||||||
|
r: bool,
|
||||||
|
w: bool,
|
||||||
|
fixed: u4 = 0b0100,
|
||||||
|
};
|
||||||
|
|
||||||
|
const GPRegister = enum(u4) {
|
||||||
|
a = 0,
|
||||||
|
c = 1,
|
||||||
|
d = 2,
|
||||||
|
b = 3,
|
||||||
|
sp = 4,
|
||||||
|
bp = 5,
|
||||||
|
si = 6,
|
||||||
|
di = 7,
|
||||||
|
r8 = 8,
|
||||||
|
r9 = 9,
|
||||||
|
r10 = 10,
|
||||||
|
r11 = 11,
|
||||||
|
r12 = 12,
|
||||||
|
r13 = 13,
|
||||||
|
r14 = 14,
|
||||||
|
r15 = 15,
|
||||||
|
};
|
||||||
|
|
||||||
|
// pub const BasicGPRegister = enum(u3) {
|
||||||
|
// a = 0,
|
||||||
|
// c = 1,
|
||||||
|
// d = 2,
|
||||||
|
// b = 3,
|
||||||
|
// sp = 4,
|
||||||
|
// bp = 5,
|
||||||
|
// si = 6,
|
||||||
|
// di = 7,
|
||||||
|
// };
|
||||||
|
|
||||||
|
const syscall_registers = [7]GPRegister{ .a, .di, .si, .d, .r10, .r8, .r9 };
|
@ -20,7 +20,7 @@ pub const Token = packed struct(u64) {
|
|||||||
pub const Id = enum(u8) {
|
pub const Id = enum(u8) {
|
||||||
eof = 0x00,
|
eof = 0x00,
|
||||||
identifier = 0x01,
|
identifier = 0x01,
|
||||||
number = 0x02,
|
number_literal = 0x02,
|
||||||
string_literal = 0x03,
|
string_literal = 0x03,
|
||||||
fixed_keyword_function = 0x04,
|
fixed_keyword_function = 0x04,
|
||||||
fixed_keyword_const = 0x05,
|
fixed_keyword_const = 0x05,
|
||||||
@ -32,6 +32,8 @@ pub const Token = packed struct(u64) {
|
|||||||
fixed_keyword_bool = 0x0b,
|
fixed_keyword_bool = 0x0b,
|
||||||
fixed_keyword_true = 0x0c,
|
fixed_keyword_true = 0x0c,
|
||||||
fixed_keyword_false = 0x0d,
|
fixed_keyword_false = 0x0d,
|
||||||
|
fixed_keyword_fn = 0x0e,
|
||||||
|
fixed_keyword_unreachable = 0x0f,
|
||||||
bang = '!', // 0x21
|
bang = '!', // 0x21
|
||||||
hash = '#', // 0x23
|
hash = '#', // 0x23
|
||||||
dollar_sign = '$', // 0x24
|
dollar_sign = '$', // 0x24
|
||||||
@ -78,6 +80,8 @@ pub const FixedKeyword = enum {
|
|||||||
bool,
|
bool,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
@"fn",
|
||||||
|
@"unreachable",
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Result = struct {
|
pub const Result = struct {
|
||||||
@ -123,7 +127,7 @@ pub fn analyze(allocator: Allocator, text: []const u8) !Result {
|
|||||||
inline else => |comptime_fixed_keyword| @field(Token.Id, "fixed_keyword_" ++ @tagName(comptime_fixed_keyword)),
|
inline else => |comptime_fixed_keyword| @field(Token.Id, "fixed_keyword_" ++ @tagName(comptime_fixed_keyword)),
|
||||||
} else .identifier;
|
} else .identifier;
|
||||||
},
|
},
|
||||||
'(', ')', '{', '}', '-', '=', ';', '#' => |operator| blk: {
|
'(', ')', '{', '}', '-', '=', ';', '#', '@', ',' => |operator| blk: {
|
||||||
index += 1;
|
index += 1;
|
||||||
break :blk @enumFromInt(operator);
|
break :blk @enumFromInt(operator);
|
||||||
},
|
},
|
||||||
@ -132,7 +136,7 @@ pub fn analyze(allocator: Allocator, text: []const u8) !Result {
|
|||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
break :blk .number;
|
break :blk .number_literal;
|
||||||
},
|
},
|
||||||
'\'' => {
|
'\'' => {
|
||||||
unreachable;
|
unreachable;
|
||||||
|
@ -54,41 +54,9 @@ const Analyzer = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn assign(analyzer: *Analyzer, scope: *Scope, node_index: Node.Index) !Assignment.Index {
|
fn assign(analyzer: *Analyzer, scope: *Scope, node_index: Node.Index) !Assignment.Index {
|
||||||
print("Assign: #{}", .{node_index.value});
|
_ = node_index;
|
||||||
const node = analyzer.nodes[node_index.unwrap()];
|
_ = scope;
|
||||||
assert(node.id == .assign);
|
_ = analyzer;
|
||||||
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```
|
|
||||||
false => .{
|
|
||||||
.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 {
|
|
||||||
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) anyerror!Block.Index {
|
fn block(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node_index: Node.Index) anyerror!Block.Index {
|
||||||
@ -100,15 +68,19 @@ const Analyzer = struct {
|
|||||||
try statement_nodes.append(analyzer.allocator, block_node.left);
|
try statement_nodes.append(analyzer.allocator, block_node.left);
|
||||||
},
|
},
|
||||||
.block_zero, .comptime_block_zero => {},
|
.block_zero, .comptime_block_zero => {},
|
||||||
|
.block_two, .comptime_block_two => {
|
||||||
|
try statement_nodes.append(analyzer.allocator, block_node.left);
|
||||||
|
try statement_nodes.append(analyzer.allocator, block_node.right);
|
||||||
|
},
|
||||||
else => |t| @panic(@tagName(t)),
|
else => |t| @panic(@tagName(t)),
|
||||||
}
|
}
|
||||||
|
|
||||||
const is_comptime = switch (block_node.id) {
|
const is_comptime = switch (block_node.id) {
|
||||||
.comptime_block_zero, .comptime_block_one => true,
|
.comptime_block_zero, .comptime_block_one, .comptime_block_two => true,
|
||||||
.block_zero, .block_one => false,
|
.block_zero, .block_one, .block_two => false,
|
||||||
else => |t| @panic(@tagName(t)),
|
else => |t| @panic(@tagName(t)),
|
||||||
};
|
};
|
||||||
_ = is_comptime;
|
print("Is comptime: {}\n", .{is_comptime});
|
||||||
|
|
||||||
var statements = ArrayList(Value.Index){};
|
var statements = ArrayList(Value.Index){};
|
||||||
|
|
||||||
@ -121,7 +93,37 @@ const Analyzer = struct {
|
|||||||
const statement_value = switch (statement_node.id) {
|
const statement_value = switch (statement_node.id) {
|
||||||
inline .assign, .simple_while => |statement_id| blk: {
|
inline .assign, .simple_while => |statement_id| blk: {
|
||||||
const specific_value_index = switch (statement_id) {
|
const specific_value_index = switch (statement_id) {
|
||||||
.assign => try analyzer.assign(scope, statement_node_index),
|
.assign => {
|
||||||
|
print("Assign: #{}\n", .{node_index.value});
|
||||||
|
assert(statement_node.id == .assign);
|
||||||
|
switch (statement_node.left.valid) {
|
||||||
|
// In an assignment, the node being invalid means a discarding underscore, like this: ```_ = result```
|
||||||
|
false => {
|
||||||
|
const right = try analyzer.expression(scope, ExpectType.none, statement_node.right);
|
||||||
|
try statements.append(analyzer.allocator, right);
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
true => {
|
||||||
|
const left_node = analyzer.nodes[statement_node.left.unwrap()];
|
||||||
|
print("left node index: {}. Left node: {}\n", .{ statement_node.left, left_node });
|
||||||
|
// const id = analyzer.tokenIdentifier(.token);
|
||||||
|
// print("id: {s}\n", .{id});
|
||||||
|
const left = try analyzer.expression(scope, ExpectType.none, statement_node.left);
|
||||||
|
_ = left;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
.simple_while => statement: {
|
.simple_while => statement: {
|
||||||
const loop_index = try analyzer.module.loops.append(analyzer.allocator, .{
|
const loop_index = try analyzer.module.loops.append(analyzer.allocator, .{
|
||||||
.condition = Value.Index.invalid,
|
.condition = Value.Index.invalid,
|
||||||
@ -148,6 +150,10 @@ const Analyzer = struct {
|
|||||||
const value_index = try analyzer.module.values.append(analyzer.allocator, value);
|
const value_index = try analyzer.module.values.append(analyzer.allocator, value);
|
||||||
break :blk value_index;
|
break :blk value_index;
|
||||||
},
|
},
|
||||||
|
.@"unreachable" => blk: {
|
||||||
|
reaches_end = false;
|
||||||
|
break :blk Values.@"unreachable".getIndex();
|
||||||
|
},
|
||||||
else => |t| @panic(@tagName(t)),
|
else => |t| @panic(@tagName(t)),
|
||||||
};
|
};
|
||||||
try statements.append(analyzer.allocator, statement_value);
|
try statements.append(analyzer.allocator, statement_value);
|
||||||
@ -194,16 +200,31 @@ const Analyzer = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn getArguments(analyzer: *Analyzer, node_index: Node.Index) !ArrayList(Node.Index) {
|
||||||
|
var arguments = ArrayList(Node.Index){};
|
||||||
|
const node = analyzer.nodes[node_index.unwrap()];
|
||||||
|
switch (node.id) {
|
||||||
|
.compiler_intrinsic_two => {
|
||||||
|
try arguments.append(analyzer.allocator, node.left);
|
||||||
|
try arguments.append(analyzer.allocator, node.right);
|
||||||
|
},
|
||||||
|
else => |t| @panic(@tagName(t)),
|
||||||
|
}
|
||||||
|
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
|
|
||||||
fn resolveNode(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node_index: Node.Index) anyerror!Value {
|
fn resolveNode(analyzer: *Analyzer, scope: *Scope, expect_type: ExpectType, node_index: Node.Index) anyerror!Value {
|
||||||
const node = analyzer.nodes[node_index.unwrap()];
|
const node = analyzer.nodes[node_index.unwrap()];
|
||||||
return switch (node.id) {
|
return switch (node.id) {
|
||||||
.identifier => unreachable,
|
.identifier => unreachable,
|
||||||
.compiler_intrinsic_one => blk: {
|
.compiler_intrinsic_one, .compiler_intrinsic_two => 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;
|
||||||
print("Intrinsic: {s}", .{@tagName(intrinsic)});
|
print("Intrinsic: {s}\n", .{@tagName(intrinsic)});
|
||||||
switch (intrinsic) {
|
switch (intrinsic) {
|
||||||
.import => {
|
.import => {
|
||||||
|
assert(node.id == .compiler_intrinsic_one);
|
||||||
const import_argument = analyzer.nodes[node.left.unwrap()];
|
const import_argument = analyzer.nodes[node.left.unwrap()];
|
||||||
switch (import_argument.id) {
|
switch (import_argument.id) {
|
||||||
.string_literal => {
|
.string_literal => {
|
||||||
@ -212,7 +233,7 @@ const Analyzer = struct {
|
|||||||
|
|
||||||
if (imported_file.is_new) {
|
if (imported_file.is_new) {
|
||||||
// TODO: fix error
|
// TODO: fix error
|
||||||
analyzer.module.generateAbstractSyntaxTreeForFile(analyzer.allocator, imported_file.file) catch return error.OutOfMemory;
|
try analyzer.module.generateAbstractSyntaxTreeForFile(analyzer.allocator, imported_file.file);
|
||||||
} else {
|
} else {
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
@ -224,6 +245,35 @@ const Analyzer = struct {
|
|||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
.syscall => {
|
||||||
|
var argument_nodes = try analyzer.getArguments(node_index);
|
||||||
|
print("Argument count: {}\n", .{argument_nodes.items.len});
|
||||||
|
if (argument_nodes.items.len > 0 and argument_nodes.items.len <= 6 + 1) {
|
||||||
|
const number = try analyzer.expression(scope, ExpectType.none, argument_nodes.items[0]);
|
||||||
|
assert(number.valid);
|
||||||
|
var arguments = std.mem.zeroes([6]Value.Index);
|
||||||
|
for (argument_nodes.items[1..], 0..) |argument_node_index, argument_index| {
|
||||||
|
const argument = try analyzer.expression(scope, ExpectType.none, argument_node_index);
|
||||||
|
print("Index: {}. Argument: {}\n", .{ argument_index, argument });
|
||||||
|
arguments[argument_index] = argument;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: typecheck for usize
|
||||||
|
for (arguments[0..argument_nodes.items.len]) |argument| {
|
||||||
|
_ = argument;
|
||||||
|
}
|
||||||
|
|
||||||
|
break :blk .{
|
||||||
|
.syscall = try analyzer.module.syscalls.append(analyzer.allocator, .{
|
||||||
|
.number = number,
|
||||||
|
.arguments = arguments,
|
||||||
|
.argument_count = @intCast(argument_nodes.items.len - 1),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
unreachable;
|
unreachable;
|
||||||
},
|
},
|
||||||
@ -244,13 +294,18 @@ const Analyzer = struct {
|
|||||||
},
|
},
|
||||||
.keyword_true => unreachable,
|
.keyword_true => unreachable,
|
||||||
.simple_while => unreachable,
|
.simple_while => unreachable,
|
||||||
// .assign => try analyzer.assign(scope, node_index),
|
|
||||||
.block_zero, .block_one => blk: {
|
.block_zero, .block_one => blk: {
|
||||||
const block_index = try analyzer.block(scope, expect_type, node_index);
|
const block_index = try analyzer.block(scope, expect_type, node_index);
|
||||||
break :blk .{
|
break :blk .{
|
||||||
.block = block_index,
|
.block = block_index,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
.number_literal => switch (std.zig.parseNumberLiteral(analyzer.tokenBytes(analyzer.tokens[node.token]))) {
|
||||||
|
.int => |integer| .{
|
||||||
|
.integer = integer,
|
||||||
|
},
|
||||||
|
else => |t| @panic(@tagName(t)),
|
||||||
|
},
|
||||||
else => |t| @panic(@tagName(t)),
|
else => |t| @panic(@tagName(t)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -269,12 +324,9 @@ const Analyzer = struct {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
break :blk bool_true;
|
break :blk Values.getIndex(.bool_true);
|
||||||
},
|
},
|
||||||
.block_zero => try analyzer.module.values.append(analyzer.allocator, .{
|
else => try analyzer.module.values.append(analyzer.allocator, try analyzer.resolveNode(scope, expect_type, node_index)),
|
||||||
.block = try analyzer.block(scope, expect_type, node_index),
|
|
||||||
}),
|
|
||||||
else => |t| @panic(@tagName(t)),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,58 +394,6 @@ const Analyzer = struct {
|
|||||||
@panic("TODO: analyzeDeclaration");
|
@panic("TODO: analyzeDeclaration");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn globalSymbolDeclaration(analyzer: *Analyzer, symbol_declaration: SymbolDeclaration) !void {
|
|
||||||
if (symbol_declaration.type_node.get()) |type_node_index| {
|
|
||||||
_ = type_node_index;
|
|
||||||
@panic("TODO: type node");
|
|
||||||
}
|
|
||||||
const initialization_node = analyzer.nodes[symbol_declaration.initialization_node.unwrap()];
|
|
||||||
switch (initialization_node.id) {
|
|
||||||
.compiler_intrinsic_one => {
|
|
||||||
const intrinsic_name = analyzer.tokenIdentifier(initialization_node.token + 1);
|
|
||||||
const intrinsic = inline for (@typeInfo(Intrinsic).Enum.fields) |intrinsic_enum_field| {
|
|
||||||
if (equal(u8, intrinsic_name, intrinsic_enum_field.name)) {
|
|
||||||
break @field(Intrinsic, intrinsic_enum_field.name);
|
|
||||||
}
|
|
||||||
} else unreachable;
|
|
||||||
print("Intrinsic: {s}", .{@tagName(intrinsic)});
|
|
||||||
switch (intrinsic) {
|
|
||||||
.import => {
|
|
||||||
const import_argument = analyzer.nodes[initialization_node.left.get()];
|
|
||||||
switch (import_argument.id) {
|
|
||||||
.string_literal => unreachable,
|
|
||||||
else => unreachable,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
// const intrinsic_node_index = initialization_node.left.unwrap();
|
|
||||||
// const intrinsic_node = analyzer.nodes[intrinsic_node_index];
|
|
||||||
//
|
|
||||||
// switch (intrinsic_node.id) {
|
|
||||||
// .string_literal =>
|
|
||||||
// }
|
|
||||||
// print("intrinsic: {}", .{intrinsic_node.id});
|
|
||||||
|
|
||||||
// _ = a;
|
|
||||||
},
|
|
||||||
else => unreachable,
|
|
||||||
}
|
|
||||||
print("Init node: {}\n", .{initialization_node});
|
|
||||||
@panic("TODO");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn symbolDeclaration(analyzer: *Analyzer, node_index: Node.Index) SymbolDeclaration {
|
|
||||||
const node = analyzer.nodes[node_index.unwrap()];
|
|
||||||
return switch (node.id) {
|
|
||||||
.simple_variable_declaration => .{
|
|
||||||
.type_node = node.left,
|
|
||||||
.initialization_node = node.right,
|
|
||||||
.mutability_token = node.token,
|
|
||||||
},
|
|
||||||
else => unreachable,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn structType(analyzer: *Analyzer, parent_scope: Scope.Index, container_declaration: syntactic_analyzer.ContainerDeclaration, index: Node.Index) !Type.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 = parent_scope });
|
const new_scope = try analyzer.allocateScope(.{ .parent = parent_scope });
|
||||||
@ -544,18 +544,20 @@ const Analyzer = struct {
|
|||||||
fn tokenIdentifier(analyzer: *Analyzer, token_index: Token.Index) []const u8 {
|
fn tokenIdentifier(analyzer: *Analyzer, token_index: Token.Index) []const u8 {
|
||||||
const token = analyzer.tokens[token_index];
|
const token = analyzer.tokens[token_index];
|
||||||
assert(token.id == .identifier);
|
assert(token.id == .identifier);
|
||||||
const identifier = analyzer.source_code[token.start..][0..token.len];
|
const identifier = analyzer.tokenBytes(token);
|
||||||
|
|
||||||
return identifier;
|
return identifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tokenBytes(analyzer: *Analyzer, token: Token) []const u8 {
|
||||||
|
return analyzer.source_code[token.start..][0..token.len];
|
||||||
|
}
|
||||||
|
|
||||||
fn tokenStringLiteral(analyzer: *Analyzer, token_index: Token.Index) []const u8 {
|
fn tokenStringLiteral(analyzer: *Analyzer, token_index: Token.Index) []const u8 {
|
||||||
const token = analyzer.tokens[token_index];
|
const token = analyzer.tokens[token_index];
|
||||||
assert(token.id == .string_literal);
|
assert(token.id == .string_literal);
|
||||||
// Eat double quotes
|
// Eat double quotes
|
||||||
const start = token.start + 1;
|
const string_literal = analyzer.tokenBytes(token)[1..][0 .. token.len - 2];
|
||||||
const len = token.len - 2;
|
|
||||||
const string_literal = analyzer.source_code[start..][0..len];
|
|
||||||
|
|
||||||
return string_literal;
|
return string_literal;
|
||||||
}
|
}
|
||||||
@ -593,18 +595,30 @@ const type_boolean = Type.Index{
|
|||||||
.index = FixedTypeKeyword.offset + @intFromEnum(FixedTypeKeyword.bool),
|
.index = FixedTypeKeyword.offset + @intFromEnum(FixedTypeKeyword.bool),
|
||||||
};
|
};
|
||||||
|
|
||||||
const bool_false = Value.Index{
|
// Each time an enum is added here, a corresponding insertion in the initialization must be made
|
||||||
.block = 0,
|
const Values = enum {
|
||||||
.index = 1,
|
bool_false,
|
||||||
};
|
bool_true,
|
||||||
|
@"unreachable",
|
||||||
|
|
||||||
const bool_true = Value.Index{
|
fn getIndex(value: Values) Value.Index {
|
||||||
.block = 0,
|
const absolute: u32 = @intFromEnum(value);
|
||||||
.index = 1,
|
const foo = @as(Value.Index, undefined);
|
||||||
|
const ElementT = @TypeOf(@field(foo, "index"));
|
||||||
|
const BlockT = @TypeOf(@field(foo, "block"));
|
||||||
|
const divider = std.math.maxInt(ElementT);
|
||||||
|
const element_index: ElementT = @intCast(absolute % divider);
|
||||||
|
const block_index: BlockT = @intCast(absolute / divider);
|
||||||
|
return .{
|
||||||
|
.index = element_index,
|
||||||
|
.block = block_index,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const Intrinsic = enum {
|
const Intrinsic = enum {
|
||||||
import,
|
import,
|
||||||
|
syscall,
|
||||||
};
|
};
|
||||||
|
|
||||||
const FixedTypeKeyword = enum {
|
const FixedTypeKeyword = enum {
|
||||||
@ -674,6 +688,10 @@ pub fn initialize(compilation: *Compilation, module: *Module, package: *Package)
|
|||||||
.bool = true,
|
.bool = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_ = try module.values.append(compilation.base_allocator, .{
|
||||||
|
.@"unreachable" = {},
|
||||||
|
});
|
||||||
|
|
||||||
return analyzeExistingPackage(compilation, module, package);
|
return analyzeExistingPackage(compilation, module, package);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@ pub const Node = packed struct(u128) {
|
|||||||
left: Node.Index,
|
left: Node.Index,
|
||||||
right: Node.Index,
|
right: Node.Index,
|
||||||
|
|
||||||
|
pub const List = ArrayList(Node.Index);
|
||||||
|
|
||||||
pub const Index = packed struct(u32) {
|
pub const Index = packed struct(u32) {
|
||||||
value: u31,
|
value: u31,
|
||||||
valid: bool = true,
|
valid: bool = true,
|
||||||
@ -74,6 +76,11 @@ pub const Node = packed struct(u128) {
|
|||||||
keyword_true = 18,
|
keyword_true = 18,
|
||||||
comptime_block_zero = 19,
|
comptime_block_zero = 19,
|
||||||
comptime_block_one = 20,
|
comptime_block_one = 20,
|
||||||
|
number_literal = 21,
|
||||||
|
compiler_intrinsic_two = 22,
|
||||||
|
comptime_block_two = 23,
|
||||||
|
block_two = 24,
|
||||||
|
@"unreachable" = 25,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -90,6 +97,7 @@ const Analyzer = struct {
|
|||||||
file: []const u8,
|
file: []const u8,
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
temporal_node_heap: ArrayList(Node.Index) = .{},
|
temporal_node_heap: ArrayList(Node.Index) = .{},
|
||||||
|
node_lists: ArrayList(Node.List) = .{},
|
||||||
|
|
||||||
fn expectToken(analyzer: *Analyzer, token_id: Token.Id) !u32 {
|
fn expectToken(analyzer: *Analyzer, token_id: Token.Id) !u32 {
|
||||||
if (analyzer.tokens[analyzer.token_i].id == token_id) {
|
if (analyzer.tokens[analyzer.token_i].id == token_id) {
|
||||||
@ -136,12 +144,13 @@ const Analyzer = struct {
|
|||||||
_ = try analyzer.expectToken(.equal);
|
_ = try analyzer.expectToken(.equal);
|
||||||
|
|
||||||
// TODO: do this in a function
|
// TODO: do this in a function
|
||||||
const init_node = switch (analyzer.tokens[analyzer.token_i].id) {
|
const init_node = try analyzer.expression();
|
||||||
.identifier => unreachable,
|
// const init_node = switch (analyzer.tokens[analyzer.token_i].id) {
|
||||||
.hash => try analyzer.compilerIntrinsic(),
|
// .identifier => unreachable,
|
||||||
.left_parenthesis => try analyzer.function(),
|
// .hash => try analyzer.compilerIntrinsic(),
|
||||||
else => |t| std.debug.panic("NI: {s}", .{@tagName(t)}),
|
// .left_parenthesis => try analyzer.function(),
|
||||||
};
|
// else => |t| std.debug.panic("NI: {s}", .{@tagName(t)}),
|
||||||
|
// };
|
||||||
|
|
||||||
_ = try analyzer.expectToken(.semicolon);
|
_ = try analyzer.expectToken(.semicolon);
|
||||||
|
|
||||||
@ -186,6 +195,8 @@ 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;
|
||||||
|
assert(analyzer.tokens[token].id == .fixed_keyword_fn);
|
||||||
|
analyzer.token_i += 1;
|
||||||
const function_prototype = try analyzer.functionPrototype();
|
const function_prototype = try analyzer.functionPrototype();
|
||||||
const is_comptime = false;
|
const is_comptime = false;
|
||||||
_ = is_comptime;
|
_ = is_comptime;
|
||||||
@ -200,6 +211,7 @@ const Analyzer = struct {
|
|||||||
|
|
||||||
fn functionPrototype(analyzer: *Analyzer) !Node.Index {
|
fn functionPrototype(analyzer: *Analyzer) !Node.Index {
|
||||||
const token = analyzer.token_i;
|
const token = analyzer.token_i;
|
||||||
|
assert(analyzer.tokens[token].id == .left_parenthesis);
|
||||||
const arguments = try analyzer.argumentList(.left_parenthesis, .right_parenthesis);
|
const arguments = try analyzer.argumentList(.left_parenthesis, .right_parenthesis);
|
||||||
const return_type = try analyzer.typeExpression();
|
const return_type = try analyzer.typeExpression();
|
||||||
|
|
||||||
@ -231,6 +243,12 @@ const Analyzer = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn assignExpressionStatement(analyzer: *Analyzer) !Node.Index {
|
||||||
|
const result = try analyzer.assignExpression();
|
||||||
|
_ = try analyzer.expectToken(.semicolon);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
fn block(analyzer: *Analyzer, options: Options) !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;
|
||||||
@ -243,14 +261,9 @@ const Analyzer = struct {
|
|||||||
.colon => {
|
.colon => {
|
||||||
unreachable;
|
unreachable;
|
||||||
},
|
},
|
||||||
else => blk: {
|
else => try analyzer.assignExpressionStatement(),
|
||||||
const identifier = analyzer.getIdentifier(first_statement_token);
|
|
||||||
std.debug.print("Starting statement with identifier: {s}\n", .{identifier});
|
|
||||||
const result = try analyzer.assignExpression();
|
|
||||||
_ = try analyzer.expectToken(.semicolon);
|
|
||||||
break :blk result;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
.fixed_keyword_unreachable => try analyzer.assignExpressionStatement(),
|
||||||
.fixed_keyword_while => try analyzer.whileStatement(options),
|
.fixed_keyword_while => try analyzer.whileStatement(options),
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
@ -279,6 +292,15 @@ const Analyzer = struct {
|
|||||||
.left = statement_array[0],
|
.left = statement_array[0],
|
||||||
.right = Node.Index.invalid,
|
.right = Node.Index.invalid,
|
||||||
},
|
},
|
||||||
|
2 => .{
|
||||||
|
.id = switch (options.is_comptime) {
|
||||||
|
true => .comptime_block_two,
|
||||||
|
false => .block_two,
|
||||||
|
},
|
||||||
|
.token = left_brace,
|
||||||
|
.left = statement_array[0],
|
||||||
|
.right = statement_array[1],
|
||||||
|
},
|
||||||
else => |len| std.debug.panic("len: {}", .{len}),
|
else => |len| std.debug.panic("len: {}", .{len}),
|
||||||
};
|
};
|
||||||
return analyzer.addNode(node);
|
return analyzer.addNode(node);
|
||||||
@ -355,6 +377,12 @@ const Analyzer = struct {
|
|||||||
.left = parameters[0],
|
.left = parameters[0],
|
||||||
.right = Node.Index.invalid,
|
.right = Node.Index.invalid,
|
||||||
}),
|
}),
|
||||||
|
2 => analyzer.addNode(.{
|
||||||
|
.id = .compiler_intrinsic_two,
|
||||||
|
.token = hash,
|
||||||
|
.left = parameters[0],
|
||||||
|
.right = parameters[1],
|
||||||
|
}),
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -370,7 +398,7 @@ const Analyzer = struct {
|
|||||||
|
|
||||||
while (analyzer.token_i < analyzer.tokens.len) {
|
while (analyzer.token_i < analyzer.tokens.len) {
|
||||||
const precedence: i32 = switch (analyzer.tokens[analyzer.token_i].id) {
|
const precedence: i32 = switch (analyzer.tokens[analyzer.token_i].id) {
|
||||||
.equal, .semicolon, .right_parenthesis, .right_brace => -1,
|
.equal, .semicolon, .right_parenthesis, .right_brace, .comma => -1,
|
||||||
else => |foo| std.debug.panic("Foo: ({s}) {}", .{ @tagName(foo), foo }),
|
else => |foo| std.debug.panic("Foo: ({s}) {}", .{ @tagName(foo), foo }),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -416,7 +444,8 @@ const Analyzer = struct {
|
|||||||
.colon => unreachable,
|
.colon => unreachable,
|
||||||
else => try analyzer.curlySuffixExpression(),
|
else => try analyzer.curlySuffixExpression(),
|
||||||
},
|
},
|
||||||
.string_literal, .fixed_keyword_true, .fixed_keyword_false => try analyzer.curlySuffixExpression(),
|
.string_literal, .number_literal, .fixed_keyword_true, .fixed_keyword_false, .hash, .fixed_keyword_unreachable => try analyzer.curlySuffixExpression(),
|
||||||
|
.fixed_keyword_fn => analyzer.function(),
|
||||||
// todo:?
|
// todo:?
|
||||||
// .left_brace => try analyzer.block(),
|
// .left_brace => try analyzer.block(),
|
||||||
else => |id| {
|
else => |id| {
|
||||||
@ -463,7 +492,7 @@ const Analyzer = struct {
|
|||||||
|
|
||||||
fn typeExpression(analyzer: *Analyzer) !Node.Index {
|
fn typeExpression(analyzer: *Analyzer) !Node.Index {
|
||||||
return switch (analyzer.tokens[analyzer.token_i].id) {
|
return switch (analyzer.tokens[analyzer.token_i].id) {
|
||||||
.identifier, .fixed_keyword_noreturn, .fixed_keyword_true, .fixed_keyword_false => try analyzer.errorUnionExpression(),
|
.identifier, .fixed_keyword_noreturn, .fixed_keyword_true, .fixed_keyword_false, .hash => try analyzer.errorUnionExpression(),
|
||||||
else => |id| blk: {
|
else => |id| blk: {
|
||||||
log.warn("By default, calling errorUnionExpression with {s}", .{@tagName(id)});
|
log.warn("By default, calling errorUnionExpression with {s}", .{@tagName(id)});
|
||||||
|
|
||||||
@ -528,6 +557,15 @@ const Analyzer = struct {
|
|||||||
.right = Node.Index.invalid,
|
.right = Node.Index.invalid,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
.number_literal => blk: {
|
||||||
|
analyzer.token_i += 1;
|
||||||
|
break :blk analyzer.addNode(.{
|
||||||
|
.id = .number_literal,
|
||||||
|
.token = token_i,
|
||||||
|
.left = Node.Index.invalid,
|
||||||
|
.right = Node.Index.invalid,
|
||||||
|
});
|
||||||
|
},
|
||||||
.identifier => switch (analyzer.tokens[token_i + 1].id) {
|
.identifier => switch (analyzer.tokens[token_i + 1].id) {
|
||||||
.colon => unreachable,
|
.colon => unreachable,
|
||||||
else => blk: {
|
else => blk: {
|
||||||
@ -546,6 +584,16 @@ const Analyzer = struct {
|
|||||||
},
|
},
|
||||||
.fixed_keyword_noreturn => try analyzer.noReturn(),
|
.fixed_keyword_noreturn => try analyzer.noReturn(),
|
||||||
.fixed_keyword_true => try analyzer.boolTrue(),
|
.fixed_keyword_true => try analyzer.boolTrue(),
|
||||||
|
.fixed_keyword_unreachable => try analyzer.addNode(.{
|
||||||
|
.id = .@"unreachable",
|
||||||
|
.token = blk: {
|
||||||
|
analyzer.token_i += 1;
|
||||||
|
break :blk token_i;
|
||||||
|
},
|
||||||
|
.left = Node.Index.invalid,
|
||||||
|
.right = Node.Index.invalid,
|
||||||
|
}),
|
||||||
|
.hash => analyzer.compilerIntrinsic(),
|
||||||
else => |foo| {
|
else => |foo| {
|
||||||
switch (foo) {
|
switch (foo) {
|
||||||
.identifier => std.debug.panic("{s}: {s}", .{ @tagName(foo), analyzer.getIdentifier(analyzer.tokens[token_i]) }),
|
.identifier => std.debug.panic("{s}: {s}", .{ @tagName(foo), analyzer.getIdentifier(analyzer.tokens[token_i]) }),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user