Implement 'add' and 'sub'

This commit is contained in:
David Gonzalez Martin 2024-05-23 21:29:35 -06:00
parent 1d52b9abdc
commit 69e6fba3fe
2 changed files with 317 additions and 114 deletions

View File

@ -282,55 +282,93 @@ const Parser = struct{
} }
} }
fn parse_typed_expression(parser: *Parser, analyzer: *Analyzer, thread: *Thread, file: *File, ty: *Type, side: Side) *Value { fn parse_constant_integer(parser: *Parser, thread: *Thread, file: *File, ty: *Type) *ConstantInt {
_ = side; // autofix
_ = &analyzer;
const src = file.source_code; const src = file.source_code;
assert(ty.sema.id != .unresolved); const starting_index = parser.i;
const starting_ch = src[parser.i]; const starting_ch = src[starting_index];
if (starting_ch == '0') {
const follow_up_character = src[parser.i + 1];
const is_hex_start = follow_up_character == 'x';
const is_octal_start = follow_up_character == 'o';
const is_bin_start = follow_up_character == 'b';
const is_prefixed_start = is_hex_start or is_octal_start or is_bin_start;
const follow_up_alpha = is_alphabetic(follow_up_character);
const follow_up_digit = is_decimal_digit(follow_up_character);
const is_valid_after_zero = is_space(follow_up_character) or (!follow_up_digit and !follow_up_alpha);
if (is_prefixed_start) {
exit(1);
} else if (is_valid_after_zero) {
parser.i += 1;
const constant_int = thread.constant_ints.append(.{
.value = .{
.sema = .{
.thread = thread.get_index(),
.resolved = true,
.id = .constant_int,
},
},
.n = 0,
.type = ty,
});
return constant_int;
} else {
exit(1);
}
}
while (is_decimal_digit(src[parser.i])) {
parser.i += 1;
}
const character_count = parser.i - starting_index;
const slice = src[starting_index..][0..character_count];
var i = character_count;
var integer: u64 = 0;
var factor: u64 = 1;
while (i > 0) {
i -= 1;
const ch = slice[i];
const int = ch - '0';
const extra = int * factor;
integer += extra;
factor *= 10;
}
const constant_int = thread.constant_ints.append(.{
.value = .{
.sema = .{
.thread = thread.get_index(),
.resolved = true,
.id = .constant_int,
},
},
.n = integer,
.type = ty,
});
return constant_int;
}
fn parse_single_expression(parser: *Parser, analyzer: *Analyzer, thread: *Thread, file: *File, maybe_type: ?*Type, side: Side) *Value {
_ = side; // autofix
const src = file.source_code;
const starting_index = parser.i;
const starting_ch = src[starting_index];
const is_digit_start = is_decimal_digit(starting_ch); const is_digit_start = is_decimal_digit(starting_ch);
const is_alpha_start = is_alphabetic(starting_ch); const is_alpha_start = is_alphabetic(starting_ch);
if (is_digit_start) { if (is_digit_start) {
const ty = maybe_type orelse exit(1);
switch (ty.sema.id) { switch (ty.sema.id) {
.integer => { .integer => {
if (starting_ch == '0') { const constant_int = parser.parse_constant_integer(thread, file, ty);
const follow_up_character = src[parser.i + 1]; return &constant_int.value;
const is_hex_start = follow_up_character == 'x';
const is_octal_start = follow_up_character == 'o';
const is_bin_start = follow_up_character == 'b';
const is_prefixed_start = is_hex_start or is_octal_start or is_bin_start;
const follow_up_alpha = is_alphabetic(follow_up_character);
const follow_up_digit = is_decimal_digit(follow_up_character);
const is_valid_after_zero = is_space(follow_up_character) or (!follow_up_digit and !follow_up_alpha);
if (is_prefixed_start) {
exit(1);
} else if (is_valid_after_zero) {
parser.i += 1;
const constant_int = thread.constant_ints.append(.{
.value = .{
.sema = .{
.thread = thread.get_index(),
.resolved = true,
.id = .constant_int,
},
},
.n = 0,
.type = ty,
});
return &constant_int.value;
} else {
exit(1);
}
}
exit(0);
}, },
else => unreachable, else => unreachable,
} }
} else if (is_alpha_start) { } else if (is_alpha_start) {
var resolved = true; var resolved = true;
_ = &resolved; // autofix
const identifier = parser.parse_identifier(thread, src); const identifier = parser.parse_identifier(thread, src);
if (analyzer.current_scope.get_declaration(identifier)) |lookup_result| { if (analyzer.current_scope.get_declaration(identifier)) |lookup_result| {
@ -375,7 +413,7 @@ const Parser = struct{
.resolved = false, .resolved = false,
.id = .instruction, .id = .instruction,
}, },
}, },
.id = .call, .id = .call,
}, },
.callable = &lazy_expression.value, .callable = &lazy_expression.value,
@ -393,36 +431,37 @@ const Parser = struct{
}, },
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
} }
exit(1);
}, },
';' => { ' ', ';' => {
switch (lookup_result.declaration.*.id) { switch (lookup_result.declaration.*.id) {
.local => { .local => {
const local_declaration = lookup_result.declaration.*.get_payload(.local); const local_declaration = lookup_result.declaration.*.get_payload(.local);
const local_symbol = local_declaration.to_symbol(); const local_symbol = local_declaration.to_symbol();
switch (typecheck(ty, local_symbol.type)) { if (maybe_type) |ty| {
.success => { switch (typecheck(ty, local_symbol.type)) {
const load = thread.loads.append(.{ .success => {},
.instruction = .{ }
.value = .{
.sema = .{
.thread = thread.get_index(),
.resolved = true,
.id = .instruction,
},
},
.id = .load,
},
.value = &local_symbol.instruction.value,
.type = local_symbol.type,
.alignment = local_symbol.type.alignment,
.is_volatile = false,
});
_ = analyzer.current_basic_block.instructions.append(&load.instruction);
return &load.instruction.value;
},
} }
const load = thread.loads.append(.{
.instruction = .{
.value = .{
.sema = .{
.thread = thread.get_index(),
.resolved = true,
.id = .instruction,
},
},
.id = .load,
},
.value = &local_symbol.instruction.value,
.type = local_symbol.type,
.alignment = local_symbol.type.alignment,
.is_volatile = false,
});
_ = analyzer.current_basic_block.instructions.append(&load.instruction);
return &load.instruction.value;
}, },
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
} }
@ -434,6 +473,88 @@ const Parser = struct{
exit(1); exit(1);
} }
} }
const CurrentOperation = enum{
none,
add,
add_assign,
sub,
sub_assign,
};
fn parse_expression(parser: *Parser, analyzer: *Analyzer, thread: *Thread, file: *File, ty: ?*Type, side: Side) *Value {
const src = file.source_code;
var current_operation = CurrentOperation.none;
var previous_value: *Value = undefined;
while (true) {
const current_value = parser.parse_single_expression(analyzer, thread, file, ty, side);
parser.skip_space(src);
switch (current_operation) {
.none => {
previous_value = current_value;
},
.add, .sub => {
const add = thread.integer_binary_operations.append(.{
.instruction = .{
.value = .{
.sema = .{
.thread = thread.get_index(),
.resolved = true,
.id = .instruction,
},
},
.id = .integer_binary_operation,
},
.left = previous_value,
.right = current_value,
.id = switch (current_operation) {
.none, .add_assign, .sub_assign => unreachable,
inline else => |co| @field(IntegerBinaryOperation.Id, @tagName(co)),
},
.type = if (ty) |t| t else current_value.get_type(),
});
_ = analyzer.current_basic_block.instructions.append(&add.instruction);
previous_value = &add.instruction.value;
},
.add_assign, .sub_assign => unreachable,
}
switch (src[parser.i]) {
';' => return previous_value,
'+' => {
current_operation = .add;
parser.i += 1;
switch (src[parser.i]) {
'=' => {
current_operation = .add_assign;
parser.i += 1;
},
else => {},
}
parser.skip_space(src);
},
'-' => {
current_operation = .sub;
parser.i += 1;
switch (src[parser.i]) {
'=' => {
current_operation = .sub_assign;
parser.i += 1;
},
else => {},
}
parser.skip_space(src);
},
else => @panic((src.ptr + parser.i)[0..1]),
}
}
}
}; };
const LazyExpression = struct { const LazyExpression = struct {
@ -494,20 +615,6 @@ const LazyExpression = struct {
} }
}; };
// fn Descriptor(comptime Id: type, comptime Integer: type) type {
// return packed struct(Integer) {
// index: @Type(.{
// .Int = .{
// .signedness = .unsigned,
// .bits = @typeInfo(Integer).Int.bits - @typeInfo(@typeInfo(Id).Enum.tag_type).Int.bits,
// },
// }),
// id: Id,
//
// pub const Index = PinnedArray(@This()).Index;
// };
// }
const Value = struct { const Value = struct {
llvm: ?*LLVM.Value = null, llvm: ?*LLVM.Value = null,
sema: packed struct(u32) { sema: packed struct(u32) {
@ -535,6 +642,26 @@ const Value = struct {
assert(value.sema.id == id); assert(value.sema.id == id);
return @fieldParentPtr("value", value); return @fieldParentPtr("value", value);
} }
fn get_type(value: *Value) *Type {
return switch (value.sema.id) {
.instruction => blk: {
const instruction = value.get_payload(.instruction);
break :blk switch (instruction.id) {
.integer_binary_operation => block: {
const bin_op = instruction.get_payload(.integer_binary_operation);
break :block bin_op.type;
},
.load => block: {
const load = instruction.get_payload(.load);
break :block load.type;
},
else => |t| @panic(@tagName(t)),
};
},
else => |t| @panic(@tagName(t)),
};
}
}; };
const Type = struct { const Type = struct {
@ -747,11 +874,18 @@ const Function = struct{
}; };
}; };
const ConstantInt = struct{
value: Value,
n: u64,
type: *Type,
};
const Instruction = struct{ const Instruction = struct{
value: Value, value: Value,
id: Id, id: Id,
const Id = enum{ const Id = enum{
integer_binary_operation,
call, call,
load, load,
local_symbol, local_symbol,
@ -762,6 +896,7 @@ const Instruction = struct{
const id_to_instruction_map = std.EnumArray(Id, type).init(.{ const id_to_instruction_map = std.EnumArray(Id, type).init(.{
.call = Call, .call = Call,
.integer_binary_operation = IntegerBinaryOperation,
.local_symbol = LocalSymbol, .local_symbol = LocalSymbol,
.load = Load, .load = Load,
.ret = Return, .ret = Return,
@ -775,10 +910,17 @@ const Instruction = struct{
} }
}; };
const ConstantInt = struct{ const IntegerBinaryOperation = struct {
value: Value, instruction: Instruction,
n: u64, left: *Value,
right: *Value,
type: *Type, type: *Type,
id: Id,
const Id = enum{
add,
sub,
};
}; };
const Call = struct{ const Call = struct{
@ -839,6 +981,7 @@ const Thread = struct{
debug_info_file_map: PinnedHashMap(u32, LLVMFile) = .{}, debug_info_file_map: PinnedHashMap(u32, LLVMFile) = .{},
// pending_values_per_file: PinnedArray(PinnedArray(*Value)) = .{}, // pending_values_per_file: PinnedArray(PinnedArray(*Value)) = .{},
calls: PinnedArray(Call) = .{}, calls: PinnedArray(Call) = .{},
integer_binary_operations: PinnedArray(IntegerBinaryOperation) = .{},
loads: PinnedArray(Load) = .{}, loads: PinnedArray(Load) = .{},
stores: PinnedArray(Store) = .{}, stores: PinnedArray(Store) = .{},
returns: PinnedArray(Return) = .{}, returns: PinnedArray(Return) = .{},
@ -1991,15 +2134,12 @@ fn worker_thread(thread_index: u32, cpu_count: *u32) void {
for (nat_entry_basic_block.instructions.slice()) |instruction| { for (nat_entry_basic_block.instructions.slice()) |instruction| {
const value: *LLVM.Value = switch (instruction.id) { const value: *LLVM.Value = switch (instruction.id) {
.call => block: { .store => block: {
const call = instruction.get_payload(.call); const store = instruction.get_payload(.store);
const callee = llvm_get_value(thread, call.callable); const destination = llvm_get_value(thread, store.destination);
const callee_function = callee.toFunction() orelse unreachable; const source = llvm_get_value(thread, store.source);
const function_type = callee_function.getType(); const store_instruction = builder.createStore(source, destination, store.is_volatile, store.alignment);
break :block store_instruction.toValue();
const arguments: []const *LLVM.Value = &.{};
const call_i = thread.llvm.builder.createCall(function_type, callee, arguments.ptr, arguments.len, "", "".len, null);
break :block call_i.toValue();
}, },
.load => block: { .load => block: {
const load = instruction.get_payload(.load); const load = instruction.get_payload(.load);
@ -2009,19 +2149,34 @@ fn worker_thread(thread_index: u32, cpu_count: *u32) void {
const load_instruction = builder.createLoad(load_type, load_value, load.is_volatile, "", "".len, load.alignment); const load_instruction = builder.createLoad(load_type, load_value, load.is_volatile, "", "".len, load.alignment);
break :block load_instruction.toValue(); break :block load_instruction.toValue();
}, },
.store => block: {
const store = instruction.get_payload(.store);
const destination = llvm_get_value(thread, store.destination);
const source = llvm_get_value(thread, store.source);
const store_instruction = builder.createStore(source, destination, store.is_volatile, store.alignment);
break :block store_instruction.toValue();
},
.ret => block: { .ret => block: {
const return_instruction = instruction.get_payload(.ret); const return_instruction = instruction.get_payload(.ret);
const return_value = llvm_get_value(thread, return_instruction.value); const return_value = llvm_get_value(thread, return_instruction.value);
const ret = thread.llvm.builder.createRet(return_value); const ret = thread.llvm.builder.createRet(return_value);
break :block ret.toValue(); break :block ret.toValue();
}, },
.integer_binary_operation => block: {
const integer_binary_operation = instruction.get_payload(.integer_binary_operation);
const left = llvm_get_value(thread, integer_binary_operation.left);
const right = llvm_get_value(thread, integer_binary_operation.right);
const integer_type = integer_binary_operation.type.get_payload(.integer);
const no_unsigned_wrapping = integer_type.signedness == .unsigned;
const no_signed_wrapping = integer_type.signedness == .signed;
break :block switch (integer_binary_operation.id) {
.add => builder.createAdd(left, right, "", "".len, no_unsigned_wrapping, no_signed_wrapping),
.sub => builder.createSub(left, right, "", "".len, no_unsigned_wrapping, no_signed_wrapping),
};
},
.call => block: {
const call = instruction.get_payload(.call);
const callee = llvm_get_value(thread, call.callable);
const callee_function = callee.toFunction() orelse unreachable;
const function_type = callee_function.getType();
const arguments: []const *LLVM.Value = &.{};
const call_i = thread.llvm.builder.createCall(function_type, callee, arguments.ptr, arguments.len, "", "".len, null);
break :block call_i.toValue();
},
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
@ -2056,12 +2211,14 @@ fn worker_thread(thread_index: u32, cpu_count: *u32) void {
} }
const verify_module = true; const verify_module = true;
const print_module_at_failure = true;
const print_module = false;
if (verify_module) { if (verify_module) {
var verification_message: []const u8 = undefined; var verification_message: []const u8 = undefined;
const verification_success = thread.llvm.module.verify(&verification_message.ptr, &verification_message.len); const verification_success = thread.llvm.module.verify(&verification_message.ptr, &verification_message.len);
if (!verification_success) { if (!verification_success) {
const print_module = true; if (print_module_at_failure) {
if (print_module) {
var module_content: []const u8 = undefined; var module_content: []const u8 = undefined;
thread.llvm.module.toString(&module_content.ptr, &module_content.len); thread.llvm.module.toString(&module_content.ptr, &module_content.len);
write(module_content); write(module_content);
@ -2072,6 +2229,13 @@ fn worker_thread(thread_index: u32, cpu_count: *u32) void {
} }
} }
if (print_module) {
var module_content: []const u8 = undefined;
thread.llvm.module.toString(&module_content.ptr, &module_content.len);
write(module_content);
write("\n");
}
thread.add_control_work(.{ thread.add_control_work(.{
.id = .llvm_notify_ir_done, .id = .llvm_notify_ir_done,
}); });
@ -2316,7 +2480,7 @@ pub fn analyze_local_block(thread: *Thread, analyzer: *Analyzer, parser: *Parser
parser.skip_space(src); parser.skip_space(src);
if (function.declaration.return_type.sema.id != .unresolved) { if (function.declaration.return_type.sema.id != .unresolved) {
const return_value = parser.parse_typed_expression(analyzer, thread, file, function.declaration.return_type, .right); const return_value = parser.parse_expression(analyzer, thread, file, function.declaration.return_type, .right);
parser.expect_character(src, ';'); parser.expect_character(src, ';');
const return_expression = thread.returns.append(.{ const return_expression = thread.returns.append(.{
@ -2362,20 +2526,51 @@ pub fn analyze_local_block(thread: *Thread, analyzer: *Analyzer, parser: *Parser
exit_with_error("TODO: local attributes"); exit_with_error("TODO: local attributes");
} }
parser.expect_character(src, ':');
parser.skip_space(src); parser.skip_space(src);
const local_type = parser.parse_type_expression(thread, src); const LocalResult = struct {
initial_value: *Value,
type: *Type,
};
const result: LocalResult = switch (src[parser.i]) {
':' => block: {
parser.i += 1;
parser.skip_space(src);
const local_type = parser.parse_type_expression(thread, src);
parser.skip_space(src);
parser.expect_character(src, '=');
parser.skip_space(src);
const local_initial_value = parser.parse_expression(analyzer, thread, file, local_type, .right);
break :block .{
.initial_value = local_initial_value,
.type = local_type,
};
},
'=' => block: {
parser.i += 1;
parser.skip_space(src);
const local_initial_value = parser.parse_expression(analyzer, thread, file, null, .right);
const local_type = local_initial_value.get_type();
break :block .{
.initial_value = local_initial_value,
.type = local_type,
};
},
else => exit(1),
};
parser.skip_space(src); parser.skip_space(src);
parser.expect_character(src, '=');
parser.skip_space(src);
const local_initial_value = parser.parse_typed_expression(analyzer, thread, file, local_type, .right);
parser.expect_character(src, ';'); parser.expect_character(src, ';');
const local_symbol = thread.local_symbols.append(.{ const local_symbol = thread.local_symbols.append(.{
@ -2384,19 +2579,19 @@ pub fn analyze_local_block(thread: *Thread, analyzer: *Analyzer, parser: *Parser
.id = .local, .id = .local,
.name = local_name, .name = local_name,
}, },
}, },
.type = local_type, .type = result.type,
.instruction = .{ .instruction = .{
.value = .{ .value = .{
.sema = .{ .sema = .{
.thread = thread.get_index(), .thread = thread.get_index(),
.resolved = local_type.sema.resolved and local_initial_value.sema.resolved, .resolved = result.type.sema.resolved and result.initial_value.sema.resolved,
.id = .instruction, .id = .instruction,
}, },
}, },
.id = .local_symbol, .id = .local_symbol,
}, },
.alignment = local_type.alignment, .alignment = result.type.alignment,
}); });
_ = analyzer.current_function.stack_slots.append(local_symbol); _ = analyzer.current_function.stack_slots.append(local_symbol);
@ -2409,11 +2604,11 @@ pub fn analyze_local_block(thread: *Thread, analyzer: *Analyzer, parser: *Parser
.resolved = true, .resolved = true,
.id = .instruction, .id = .instruction,
}, },
}, },
.id = .store, .id = .store,
}, },
.destination = &local_symbol.instruction.value, .destination = &local_symbol.instruction.value,
.source = local_initial_value, .source = result.initial_value,
.alignment = local_symbol.alignment, .alignment = local_symbol.alignment,
.is_volatile = false, .is_volatile = false,
}); });

View File

@ -0,0 +1,8 @@
fn [cc(.c)] main [export]() s32 {
>a: s32 = 1;
>b: s32 = 2;
>c = a + b;
>d: s32 = 3;
>e = d - c;
return e;
}