diff --git a/TODOLIST b/TODOLIST index 22c296b..13df4c4 100644 --- a/TODOLIST +++ b/TODOLIST @@ -1,3 +1,4 @@ +pointers function pointers for loops arrays diff --git a/bootstrap/backend/llvm.zig b/bootstrap/backend/llvm.zig index a0e8cf8..6bbfeec 100644 --- a/bootstrap/backend/llvm.zig +++ b/bootstrap/backend/llvm.zig @@ -141,7 +141,6 @@ pub const LLVM = struct { const createConditionalBranch = bindings.NativityLLVMBuilderCreateConditionalBranch; const createSwitch = bindings.NativityLLVMBuilderCreateSwitch; const createGEP = bindings.NativityLLVMBuilderCreateGEP; - const createStructGEP = bindings.NativityLLVMBuilderCreateStructGEP; const createICmp = bindings.NativityLLVMBuilderCreateICmp; const createLoad = bindings.NativityLLVMBuilderCreateLoad; const createMultiply = bindings.NativityLLVMBuilderCreateMultiply; diff --git a/bootstrap/backend/llvm_bindings.zig b/bootstrap/backend/llvm_bindings.zig index 58b241c..2e925ff 100644 --- a/bootstrap/backend/llvm_bindings.zig +++ b/bootstrap/backend/llvm_bindings.zig @@ -98,7 +98,6 @@ pub extern fn NativityLLVMBuilderCreateXor(builder: *LLVM.Builder, left: *LLVM.V pub extern fn NativityLLVMBuilderCreateAnd(builder: *LLVM.Builder, left: *LLVM.Value, right: *LLVM.Value, name_ptr: [*]const u8, name_len: usize) *LLVM.Value; pub extern fn NativityLLVMBuilderCreateOr(builder: *LLVM.Builder, left: *LLVM.Value, right: *LLVM.Value, name_ptr: [*]const u8, name_len: usize) *LLVM.Value; pub extern fn NativityLLVMBuilderCreateGEP(builder: *LLVM.Builder, type: *LLVM.Type, pointer: *LLVM.Value, index_ptr: [*]const *LLVM.Value, index_count: usize, name_ptr: [*]const u8, name_len: usize, in_bounds: bool) *LLVM.Value; -pub extern fn NativityLLVMBuilderCreateStructGEP(builder: *LLVM.Builder, type: *LLVM.Type, pointer: *LLVM.Value, index: c_uint, name_ptr: [*]const u8, name_len: usize) *LLVM.Value; pub extern fn NativityLLVMBuilderCreateBranch(builder: *LLVM.Builder, basic_block: *LLVM.Value.BasicBlock) *LLVM.Value.Instruction.Branch; pub extern fn NativityLLVMBuilderCreateConditionalBranch(builder: *LLVM.Builder, condition: *LLVM.Value, true_block: *LLVM.Value.BasicBlock, false_block: *LLVM.Value.BasicBlock, branch_weights: ?*LLVM.Metadata.Node, unpredictable: ?*LLVM.Metadata.Node) *LLVM.Value.Instruction.Branch; pub extern fn NativityLLVMBuilderCreateSwitch(builder: *LLVM.Builder, condition: *LLVM.Value, default_block: ?*LLVM.Value.BasicBlock, case_ptr: [*]const *LLVM.Value.Constant.Int, case_block_ptr: [*]const *LLVM.Value.BasicBlock, case_count: c_uint, branch_weights: ?*LLVM.Metadata.Node, unpredictable: ?*LLVM.Metadata.Node) *LLVM.Value.Instruction.Switch; diff --git a/bootstrap/compiler.zig b/bootstrap/compiler.zig index 96e49cf..843be53 100644 --- a/bootstrap/compiler.zig +++ b/bootstrap/compiler.zig @@ -226,7 +226,7 @@ const Parser = struct{ parser.skip_space(src); parser.expect_character(src, '('); parser.skip_space(src); - const size_type = parser.parse_type_expression(thread, src); + const size_type = parser.parse_type_expression(thread, file); parser.skip_space(src); parser.expect_character(src, ')'); const constant_int = thread.constant_ints.append(.{ @@ -408,8 +408,10 @@ const Parser = struct{ return integer; } - fn parse_type_expression(parser: *Parser, thread: *Thread, src: []const u8) *Type { + fn parse_type_expression(parser: *Parser, thread: *Thread, file: *File) *Type { + const src = file.source_code; const starting_ch = src[parser.i]; + const is_array_start = starting_ch == '['; const is_start_u = starting_ch == 'u'; const is_start_s = starting_ch == 's'; const float_start = starting_ch == 'f'; @@ -460,6 +462,28 @@ const Parser = struct{ } else { exit(1); } + } else if (is_array_start) { + parser.i += 1; + + parser.skip_space(src); + + const element_count = parser.parse_constant_expression(thread, file, null); + switch (element_count.sema.id) { + .constant_int => { + const constant_int = element_count.get_payload(.constant_int); + parser.skip_space(src); + parser.expect_character(src, ']'); + parser.skip_space(src); + + const element_type = parser.parse_type_expression(thread, file); + const array_type = get_array_type(thread, .{ + .element_type = element_type, + .element_count = constant_int.n, + }); + return array_type; + }, + else => |t| @panic(@tagName(t)), + } } else { exit_with_error("Unrecognized type expression"); } @@ -597,9 +621,64 @@ const Parser = struct{ const value = parser.parse_intrinsic(analyzer, thread, file, maybe_type) orelse unreachable; return value; }, + '[' => { + parser.i += 1; + + const ty = maybe_type orelse exit(1); + const array_type = ty.get_payload(.array); + const element_count = array_type.descriptor.element_count; + const element_type = array_type.descriptor.element_type; + + parser.skip_space(src); + + var values = PinnedArray(*Value){}; + + var is_constant = true; + while (true) { + parser.skip_space(src); + + if (src[parser.i] == ']') { + break; + } + + const value = parser.parse_expression(analyzer, thread, file, element_type, .right); + is_constant = is_constant and value.is_constant(); + _ = values.append(value); + + parser.skip_space(src); + + switch (src[parser.i]) { + ']' => {}, + ',' => parser.i += 1, + else => unreachable, + } + } + + parser.i += 1; + + if (values.length != element_count) { + exit(1); + } + + if (is_constant) { + const constant_array = thread.constant_arrays.append(.{ + .value = .{ + .sema = .{ + .thread = thread.get_index(), + .resolved = true, + .id = .constant_array, + }, + }, + .values = values.const_slice(), + .type = ty, + }); + return &constant_array.value; + } else { + unreachable; + } + }, else => unreachable, }; - _ = side; // autofix const starting_index = parser.i; const starting_ch = src[starting_index]; const is_digit_start = is_decimal_digit(starting_ch); @@ -607,7 +686,7 @@ const Parser = struct{ if (is_digit_start) { assert(unary == .none); - const ty = maybe_type orelse exit(1); + const ty = maybe_type orelse &thread.integers[63].type; switch (ty.sema.id) { .integer => { const constant_int = parser.parse_constant_integer(thread, file, ty); @@ -751,6 +830,81 @@ const Parser = struct{ else => |t| @panic(@tagName(t)), } }, + '[' => { + parser.i += 1; + + parser.skip_space(src); + + const declaration_type = switch (lookup_result.declaration.*.id) { + .local => block: { + const local_declaration = lookup_result.declaration.*.get_payload(.local); + const local_symbol = local_declaration.to_symbol(); + break :block local_symbol.type; + }, + else => |t| @panic(@tagName(t)), + }; + const declaration_value = switch (lookup_result.declaration.*.id) { + .local => block: { + const local_declaration = lookup_result.declaration.*.get_payload(.local); + const local_symbol = local_declaration.to_symbol(); + break :block &local_symbol.instruction.value; + }, + else => |t| @panic(@tagName(t)), + }; + const declaration_element_type = switch (declaration_type.sema.id) { + .array => block: { + const array_type = declaration_type.get_payload(.array); + break :block array_type.descriptor.element_type; + }, + else => |t| @panic(@tagName(t)), + }; + + const index = parser.parse_expression(analyzer, thread, file, null, .right); + + parser.skip_space(src); + + parser.expect_character(src, ']'); + const gep = thread.geps.append(.{ + .instruction = .{ + .value = .{ + .sema = .{ + .thread = thread.get_index(), + .resolved = true, + .id = .instruction, + }, + }, + .id = .get_element_pointer, + }, + .pointer = declaration_value, + .index = index, + .type = declaration_element_type, + .is_struct = false, + }); + _ = analyzer.current_basic_block.instructions.append(&gep.instruction); + return switch (side) { + .left => &gep.instruction.value, + .right => block: { + const load = thread.loads.append(.{ + .instruction = .{ + .value = .{ + .sema = .{ + .thread = thread.get_index(), + .resolved = true, + .id = .instruction, + }, + }, + .id = .load, + }, + .value = &gep.instruction.value, + .type = declaration_element_type, + .alignment = declaration_type.alignment, + .is_volatile = false, + }); + _ = analyzer.current_basic_block.instructions.append(&load.instruction); + break :block &load.instruction.value; + }, + }; + }, ' ', ',', ';', ')' => { const declaration_value = switch (lookup_result.declaration.*.id) { .local => block: { @@ -943,7 +1097,7 @@ const Parser = struct{ const is_alpha_start = is_alphabetic(starting_ch); _ = is_alpha_start; // autofix if (is_digit_start) { - const ty = maybe_type orelse exit(1); + const ty = maybe_type orelse &thread.integers[63].type; switch (ty.sema.id) { .integer => { const constant_int = parser.parse_constant_integer(thread, file, ty); @@ -1088,7 +1242,7 @@ const Parser = struct{ const original_index = parser.i; const original = src[original_index]; switch (original) { - ')', ';', ',' => return previous_value, + ')', ';', ',', ']' => return previous_value, 'o' => { const identifier = parser.parse_raw_identifier(src); if (byte_equal(identifier, "orelse")) { @@ -1501,6 +1655,7 @@ const Value = struct { const Id = enum(u8){ argument, basic_block, + constant_array, constant_int, instruction, global_symbol, @@ -1510,12 +1665,20 @@ const Value = struct { const id_to_value_map = std.EnumArray(Id, type).init(.{ .argument = ArgumentSymbol, .basic_block = BasicBlock, + .constant_array = ConstantArray, .constant_int = ConstantInt, .global_symbol = GlobalSymbol, .instruction = Instruction, .lazy_expression = LazyExpression, }); + fn is_constant(value: *Value) bool { + return switch (value.sema.id) { + .constant_int => true, + else => |t| @panic(@tagName(t)), + }; + } + fn get_payload(value: *Value, comptime id: Id) *id_to_value_map.get(id) { assert(value.sema.id == id); return @fieldParentPtr("value", value); @@ -1589,6 +1752,7 @@ const Type = struct { unresolved, void, integer, + array, }; const Integer = struct { @@ -1602,10 +1766,21 @@ const Type = struct { }; }; + const Array = struct { + type: Type, + descriptor: Descriptor, + + const Descriptor = struct{ + element_count: u64, + element_type: *Type, + }; + }; + const id_to_type_map = std.EnumArray(Id, type).init(.{ .unresolved = void, .void = void, .integer = Integer, + .array = Array, }); fn get_payload(ty: *Type, comptime id: Id) *id_to_type_map.get(id) { @@ -1836,6 +2011,12 @@ const ConstantInt = struct{ type: *Type, }; +const ConstantArray = struct{ + value: Value, + values: []const *Value, + type: *Type, +}; + const Instruction = struct{ value: Value, id: Id, @@ -1844,6 +2025,7 @@ const Instruction = struct{ argument_storage, branch, call, + get_element_pointer, integer_binary_operation, integer_compare, jump, @@ -1862,6 +2044,7 @@ const Instruction = struct{ .argument_storage = ArgumentSymbol, .branch = Branch, .call = Call, + .get_element_pointer = GEP, .integer_binary_operation = IntegerBinaryOperation, .integer_compare = IntegerCompare, .jump = Jump, @@ -1882,6 +2065,14 @@ const Instruction = struct{ } }; +const GEP = struct { + instruction: Instruction, + pointer: *Value, + index: *Value, + type: *Type, + is_struct: bool, +}; + const IntegerBinaryOperation = struct { instruction: Instruction, left: *Value, @@ -2015,6 +2206,7 @@ const Thread = struct{ external_functions: PinnedArray(Function.Declaration) = .{}, identifiers: PinnedHashMap(u32, []const u8) = .{}, constant_ints: PinnedArray(ConstantInt) = .{}, + constant_arrays: PinnedArray(ConstantArray) = .{}, basic_blocks: PinnedArray(BasicBlock) = .{}, task_system: TaskSystem = .{}, debug_info_file_map: PinnedHashMap(u32, LLVMFile) = .{}, @@ -2028,6 +2220,7 @@ const Thread = struct{ stores: PinnedArray(Store) = .{}, phis: PinnedArray(Phi) = .{}, returns: PinnedArray(Return) = .{}, + geps: PinnedArray(GEP) = .{}, lazy_expressions: PinnedArray(LazyExpression) = .{}, imports: PinnedArray(Import) = .{}, local_blocks: PinnedArray(LocalBlock) = .{}, @@ -2037,6 +2230,8 @@ const Thread = struct{ unreachables: PinnedArray(Unreachable) = .{}, leading_zeroes: PinnedArray(LeadingZeroes) = .{}, trailing_zeroes: PinnedArray(TrailingZeroes) = .{}, + array_type_map: PinnedHashMap(Type.Array.Descriptor, *Type) = .{}, + array_types: PinnedArray(Type.Array) = .{}, analyzed_file_count: u32 = 0, assigned_file_count: u32 = 0, llvm: struct { @@ -3438,6 +3633,18 @@ fn worker_thread(thread_index: u32, cpu_count: *u32) void { const call_i = builder.createCall(intrinsic_function_type, intrinsic_function.toValue(), args.ptr, args.len, "", "".len, null); break :block call_i.toValue(); }, + .get_element_pointer => block: { + const gep = instruction.get_payload(.get_element_pointer); + const base_type = llvm_get_type(thread, gep.type); + const pointer = llvm_get_value(thread, gep.pointer); + const in_bounds = true; + const index = llvm_get_value(thread, gep.index); + const struct_index = context.getConstantInt(@bitSizeOf(u32), 0, false); + const index_buffer = [2]*LLVM.Value{ struct_index.toValue(), index }; + const indices = index_buffer[@intFromBool(!gep.is_struct)..]; + const get_element_pointer = builder.createGEP(base_type, pointer, indices.ptr, indices.len, "".ptr, "".len, in_bounds); + break :block get_element_pointer; + }, else => |t| @panic(@tagName(t)), }; @@ -3582,6 +3789,17 @@ fn llvm_get_value(thread: *Thread, value: *Value) *LLVM.Value { const result = thread.llvm.context.getConstantInt(integer_type.bit_count, constant_int.n, @intFromEnum(integer_type.signedness) != 0); break :b result.toValue(); }, + .constant_array => b: { + const constant_array = value.get_payload(.constant_array); + const array_type = llvm_get_type(thread, constant_array.type); + var values = PinnedArray(*LLVM.Value.Constant){}; + for (constant_array.values) |v| { + const val = llvm_get_value(thread, v); + _ = values.append(val.toConstant() orelse unreachable); + } + const result = array_type.toArray().?.getConstant(values.pointer, values.length); + break :b result.toValue(); + }, .instruction => { const instruction = value.get_payload(.instruction); switch (instruction.id) { @@ -3609,6 +3827,12 @@ fn llvm_get_type(thread: *Thread, ty: *Type) *LLVM.Type { const integer_type = thread.llvm.context.getIntegerType(int_ty.bit_count); break :b integer_type.toType(); }, + .array => b: { + const array_ty = ty.get_payload(.array); + const element_type = llvm_get_type(thread, array_ty.descriptor.element_type); + const array_type = LLVM.Type.Array.get(element_type, array_ty.descriptor.element_count); + break :b array_type.toType(); + }, else => |t| @panic(@tagName(t)), }; return llvm_type; @@ -3835,7 +4059,7 @@ pub fn analyze_local_block(thread: *Thread, analyzer: *Analyzer, parser: *Parser parser.skip_space(src); - const local_type = parser.parse_type_expression(thread, src); + const local_type = parser.parse_type_expression(thread, file); parser.skip_space(src); parser.expect_character(src, '='); @@ -4293,7 +4517,7 @@ pub fn analyze_file(thread: *Thread, file_index: u32) void { parser.skip_space(src); - const global_type = parser.parse_type_expression(thread, src); + const global_type = parser.parse_type_expression(thread, file); parser.skip_space(src); @@ -4528,7 +4752,7 @@ pub fn analyze_file(thread: *Thread, file_index: u32) void { parser.skip_space(src); - const argument_type = parser.parse_type_expression(thread, src); + const argument_type = parser.parse_type_expression(thread, file); _ = arguments.append(.{ .type = argument_type, .name = argument_name, @@ -4550,7 +4774,7 @@ pub fn analyze_file(thread: *Thread, file_index: u32) void { parser.skip_space(src); - function.declaration.return_type = parser.parse_type_expression(thread, src); + function.declaration.return_type = parser.parse_type_expression(thread, file); parser.skip_space(src); @@ -4877,6 +5101,27 @@ fn emit_condition(analyzer: *Analyzer, thread: *Thread, condition: *Value) *Valu return compare; } +fn get_array_type(thread: *Thread, descriptor: Type.Array.Descriptor) *Type { + assert(descriptor.element_type.sema.resolved); + if (thread.array_type_map.get(descriptor)) |result| return result else { + const array_type = thread.array_types.append(.{ + .type = .{ + .sema = .{ + .thread = thread.get_index(), + .id = .array, + .resolved = true, + }, + .size = descriptor.element_type.size * descriptor.element_count, + .alignment = descriptor.element_type.alignment, + }, + .descriptor = descriptor, + }); + + thread.array_type_map.put_no_clobber(descriptor, &array_type.type); + return &array_type.type; + } +} + pub const LLVM = struct { const bindings = @import("backend/llvm_bindings.zig"); pub const x86_64 = struct { @@ -4974,7 +5219,6 @@ pub const LLVM = struct { const createConditionalBranch = bindings.NativityLLVMBuilderCreateConditionalBranch; const createSwitch = bindings.NativityLLVMBuilderCreateSwitch; const createGEP = bindings.NativityLLVMBuilderCreateGEP; - const createStructGEP = bindings.NativityLLVMBuilderCreateStructGEP; const createICmp = bindings.NativityLLVMBuilderCreateICmp; const createIsNotNull = bindings.NativityLLVMBuilderCreateIsNotNull; const createLoad = bindings.NativityLLVMBuilderCreateLoad; @@ -6069,7 +6313,3 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, return_ }, } } - - - - diff --git a/retest/standalone/arrays/main.nat b/retest/standalone/arrays/main.nat new file mode 100644 index 0000000..7066809 --- /dev/null +++ b/retest/standalone/arrays/main.nat @@ -0,0 +1,4 @@ +fn[cc(.c)] main[export]() s32 { + >array: [1]s32 = [0]; + return array[0]; +} diff --git a/src/llvm/llvm.cpp b/src/llvm/llvm.cpp index 296481d..fc6fce1 100644 --- a/src/llvm/llvm.cpp +++ b/src/llvm/llvm.cpp @@ -603,13 +603,6 @@ extern "C" Value* NativityLLVMBuilderCreateGEP(IRBuilder<>& builder, Type* type, return GEP; } -extern "C" Value* NativityLLVMBuilderCreateStructGEP(IRBuilder<>& builder, Type* type, Value* pointer, unsigned index, const char* name_ptr, size_t name_len) -{ - auto name = StringRef(name_ptr, name_len); - auto* gep = builder.CreateStructGEP(type, pointer, index, name); - return gep; -} - extern "C" BranchInst* NativityLLVMBuilderCreateBranch(IRBuilder<>& builder, BasicBlock* basic_block) { auto* conditional_branch = builder.CreateBr(basic_block);