diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dcea904..e6f13a6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,16 +72,8 @@ jobs: - uses: mlugg/setup-zig@v1 with: version: master - - name: Install dependencies - run: sudo apt-get update && sudo apt-get install -y llvm-dev liblld-dev - if: matrix.os == 'ubuntu-24.04' - name: Build and test (Packaged LLVM) run: | zig build test --summary all -Doptimize=${{matrix.BIRTH_ZIG_BUILD_TYPE}} -Dsystem_llvm=false zig build install -Doptimize=${{matrix.BIRTH_ZIG_BUILD_TYPE}} -Dsystem_llvm=false ldd zig-out/bin/bloat-buster - - name: Build and test (System LLVM) - run: | - zig build test --summary all -Doptimize=${{matrix.BIRTH_ZIG_BUILD_TYPE}} -Dsystem_llvm=true - zig build install -Doptimize=${{matrix.BIRTH_ZIG_BUILD_TYPE}} -Dsystem_llvm=true - ldd zig-out/bin/bloat-buster diff --git a/src/LLVM.zig b/src/LLVM.zig index d8af825..e28826d 100644 --- a/src/LLVM.zig +++ b/src/LLVM.zig @@ -540,6 +540,11 @@ pub const Builder = opaque { pub const position_at_end = api.LLVMPositionBuilderAtEnd; pub const create_ret = api.LLVMBuildRet; + + pub fn create_ret_void(builder: *Builder) void { + builder.create_ret(null); + } + pub fn create_add(builder: *Builder, left: *Value, right: *Value) *Value { return api.LLVMBuildAdd(builder, left, right, ""); } @@ -592,8 +597,14 @@ pub const Builder = opaque { return api.LLVMBuildXor(builder, left, right, ""); } - pub fn create_ret_void(builder: *Builder) void { - builder.create_ret(null); + pub fn create_alloca(builder: *Builder, ty: *Type, name: []const u8) *Value { + return api.llvm_builder_create_alloca(builder, ty, 0, String.from_slice(name)); + } + + pub const create_store = api.LLVMBuildStore; + + pub fn create_load(builder: *Builder, ty: *Type, pointer: *Value) *Value { + return api.LLVMBuildLoad2(builder, ty, pointer, ""); } }; diff --git a/src/converter.zig b/src/converter.zig index 2d204b3..ab67acd 100644 --- a/src/converter.zig +++ b/src/converter.zig @@ -63,10 +63,18 @@ const CallingConvention = enum { c, }; +const Local = struct { + name: []const u8, + alloca: *llvm.Value, + type: Type, +}; + pub const FunctionBuilder = struct { function: *llvm.Function, current_basic_block: *llvm.BasicBlock, return_type: Type, + local_buffer: [64]Local = undefined, + local_count: u32 = 0, }; const Type = packed struct(u64) { @@ -252,6 +260,9 @@ const Converter = struct { converter.expect_character(left_brace); + const local_offset = function_builder.local_count; + defer function_builder.local_count = local_offset; + while (true) { converter.skip_space(); @@ -263,31 +274,66 @@ const Converter = struct { break; } + const require_semicolon = true; + const statement_start_ch = converter.content[converter.offset]; - if (is_identifier_start_ch(statement_start_ch)) { + if (statement_start_ch == '>') { + converter.offset += 1; + + converter.skip_space(); + + const local_name = converter.parse_identifier(); + + converter.skip_space(); + + if (converter.consume_character_if_match(':')) { + converter.skip_space(); + + const local_type = converter.parse_type(thread); + + converter.skip_space(); + + converter.expect_character('='); + + converter.skip_space(); + + const value = converter.parse_value(thread, local_type, function_builder); + const alloca = thread.builder.create_alloca(local_type.get(), local_name); + _ = thread.builder.create_store(value, alloca); + + const local = &function_builder.local_buffer[function_builder.local_count]; + function_builder.local_count += 1; + local.* = .{ + .name = local_name, + .alloca = alloca, + .type = local_type, + }; + } else { + converter.report_error(); + } + } else if (is_identifier_start_ch(statement_start_ch)) { const statement_start_identifier = converter.parse_identifier(); if (string_to_enum(StatementStartKeyword, statement_start_identifier)) |statement_start_keyword| { switch (statement_start_keyword) { .@"return" => { - const return_value = converter.parse_value(thread, function_builder.return_type); + const return_value = converter.parse_value(thread, function_builder.return_type, function_builder); thread.builder.create_ret(return_value); }, else => unreachable, } - - const require_semicolon = switch (statement_start_keyword) { - .@"return" => true, - else => converter.report_error(), - }; - - _ = converter.expect_or_consume(';', require_semicolon); } else { converter.report_error(); } } else { converter.report_error(); } + + converter.skip_space(); + + if (require_semicolon) { + converter.expect_character(';'); + } } converter.expect_character(right_brace); @@ -310,7 +356,7 @@ const Converter = struct { xor, }; - fn parse_value(noalias converter: *Converter, noalias thread: *llvm.Thread, expected_type: Type) *llvm.Value { + fn parse_value(noalias converter: *Converter, noalias thread: *llvm.Thread, expected_type: Type, function_builder: *FunctionBuilder) *llvm.Value { converter.skip_space(); var value_state = ExpressionState.none; @@ -319,7 +365,7 @@ const Converter = struct { const value = while (true) { const current_value = switch (converter.content[converter.offset] == left_parenthesis) { true => os.abort(), - false => converter.parse_single_value(expected_type), + false => converter.parse_single_value(thread, expected_type, function_builder), }; converter.skip_space(); @@ -424,7 +470,7 @@ const Converter = struct { negative, }; - fn parse_single_value(noalias converter: *Converter, expected_type: Type) *llvm.Value { + fn parse_single_value(noalias converter: *Converter, thread: *llvm.Thread, expected_type: Type, noalias function_builder: *FunctionBuilder) *llvm.Value { converter.skip_space(); const prefix_offset = converter.offset; @@ -444,7 +490,16 @@ const Converter = struct { const value_offset = converter.offset; const value_start_ch = converter.content[value_offset]; const value = switch (value_start_ch) { - 'a'...'z', 'A'...'Z', '_' => os.abort(), + 'a'...'z', 'A'...'Z', '_' => b: { + const identifier = converter.parse_identifier(); + for (function_builder.local_buffer[0..function_builder.local_count]) |*local| { + if (lib.string.equal(identifier, local.name)) { + break :b thread.builder.create_load(local.type.get(), local.alloca); + } + } else { + os.abort(); + } + }, '0'...'9' => converter.parse_integer(expected_type, prefix == .negative), else => os.abort(), }; diff --git a/src/converter_test.zig b/src/converter_test.zig index fee7c16..c35cf61 100644 --- a/src/converter_test.zig +++ b/src/converter_test.zig @@ -126,3 +126,7 @@ test "constant_or" { test "constant_xor" { try invsrc(@src()); } + +test "minimal_stack" { + try invsrc(@src()); +} diff --git a/src/llvm.cpp b/src/llvm.cpp index 79e3875..add167f 100644 --- a/src/llvm.cpp +++ b/src/llvm.cpp @@ -65,12 +65,6 @@ EXPORT bool llvm_type_is_integer(const Type& type) return result; } -EXPORT Value* llvm_builder_create_add(IRBuilder<>& builder, Value* left, Value* right, bool nuw, bool nsw) -{ - auto* result = builder.CreateAdd(left, right, "", nuw, nsw); - return result; -} - EXPORT Function* llvm_module_create_function(Module* module, FunctionType* function_type, GlobalValue::LinkageTypes linkage_type, unsigned address_space, BBLLVMString name) { auto* function = Function::Create(function_type, linkage_type, address_space, name.string_ref(), module); @@ -97,6 +91,13 @@ EXPORT BasicBlock* llvm_context_create_basic_block(LLVMContext& context, BBLLVMS return basic_block; } +EXPORT AllocaInst* llvm_builder_create_alloca(IRBuilder<>& builder, Type* type, unsigned address_space, BBLLVMString name) +{ + const DataLayout &data_layout = builder.GetInsertBlock()->getDataLayout(); + Align alignment = data_layout.getABITypeAlign(type); + return builder.Insert(new AllocaInst(type, address_space, 0, alignment), name.string_ref()); +} + fn BBLLVMString stream_to_string(raw_string_ostream& stream) { // No need to call stream.flush(); because it's string-based diff --git a/src/llvm_api.zig b/src/llvm_api.zig index 5af1f5e..116fbaf 100644 --- a/src/llvm_api.zig +++ b/src/llvm_api.zig @@ -33,6 +33,10 @@ pub extern fn LLVMBuildAnd(builder: *llvm.Builder, left: *llvm.Value, right: *ll pub extern fn LLVMBuildOr(builder: *llvm.Builder, left: *llvm.Value, right: *llvm.Value, name: [*:0]const u8) *llvm.Value; pub extern fn LLVMBuildXor(builder: *llvm.Builder, left: *llvm.Value, right: *llvm.Value, name: [*:0]const u8) *llvm.Value; +pub extern fn llvm_builder_create_alloca(builder: *llvm.Builder, ty: *llvm.Type, address_space: c_uint, name: llvm.String) *llvm.Value; +pub extern fn LLVMBuildStore(builder: *llvm.Builder, value: *llvm.Value, pointer: *llvm.Value) *llvm.Value; +pub extern fn LLVMBuildLoad2(builder: *llvm.Builder, ty: *llvm.Type, pointer: *llvm.Value, name: [*:0]const u8) *llvm.Value; + pub extern fn LLVMTypeOf(value: *llvm.Value) *llvm.Type; pub extern fn LLVMGlobalGetValueType(value: *llvm.GlobalValue) *llvm.Type; diff --git a/tests/minimal_stack.bbb b/tests/minimal_stack.bbb new file mode 100644 index 0000000..0525a16 --- /dev/null +++ b/tests/minimal_stack.bbb @@ -0,0 +1,5 @@ +[export] main = fn [cc(c)] () s32 +{ + >result: s32 = 0; + return result; +}