diff --git a/src/LLVM.zig b/src/LLVM.zig index 4bd6546..8c89876 100644 --- a/src/LLVM.zig +++ b/src/LLVM.zig @@ -499,7 +499,9 @@ pub const Context = opaque { } }; -pub const BasicBlock = opaque {}; +pub const BasicBlock = opaque { + pub const get_terminator = api.LLVMGetBasicBlockTerminator; +}; pub const Module = opaque { pub const create_di_builder = api.LLVMCreateDIBuilder; @@ -628,6 +630,12 @@ pub const Builder = opaque { } pub const set_current_debug_location = api.LLVMSetCurrentDebugLocation2; + + pub fn create_compare(builder: *Builder, predicate: IntPredicate, left: *Value, right: *Value) *Value { + return api.LLVMBuildICmp(builder, predicate, left, right, ""); + } + + pub const create_conditional_branch = api.LLVMBuildCondBr; }; pub const GlobalValue = opaque { @@ -663,6 +671,10 @@ pub const Function = opaque { } pub const set_subprogram = api.LLVMSetSubprogram; pub const get_subprogram = api.LLVMGetSubprogram; + + pub fn to_string(function: *Function) []const u8 { + return api.llvm_function_to_string(function).to_slice(); + } }; pub const Constant = opaque { @@ -734,7 +746,8 @@ pub const DI = struct { const declaration: ?*DI.Metadata = null; return api.LLVMDIBuilderCreateGlobalVariableExpression(builder, scope, name.ptr, name.len, linkage_name.ptr, linkage_name.len, file, line, global_type, @intFromBool(local_to_unit), expression, declaration, align_in_bits); } - // pub extern fn LLVMDIBuilderCreateGlobalVariableExpression(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name_pointer: [*]const u8, name_length: usize, linkage_name_pointer: [*]const u8, linkage_name_length: usize, file: *llvm.DI.File, line: c_uint, global_type: *llvm.DI.Type, local_to_unit: Bool, expression: *llvm.DI.Expression, declaration: ?*llvm.DI.Metadata, align_in_bits: u32) *llvm.DI.GlobalVariableExpression; + + pub const create_lexical_block = api.LLVMDIBuilderCreateLexicalBlock; }; pub const create_debug_location = api.LLVMDIBuilderCreateDebugLocation; @@ -749,6 +762,11 @@ pub const DI = struct { pub const Subprogram = opaque {}; pub const Expression = opaque {}; pub const GlobalVariableExpression = opaque {}; + pub const LexicalBlock = opaque { + pub fn to_scope(lexical_block: *LexicalBlock) *Scope { + return @ptrCast(lexical_block); + } + }; pub const LocalVariable = opaque {}; pub const Location = opaque {}; pub const Metadata = opaque {}; @@ -956,6 +974,19 @@ pub const Dwarf = struct { }; }; +pub const IntPredicate = enum(c_int) { + eq = 32, + ne, + ugt, + uge, + ult, + ule, + sgt, + sge, + slt, + sle, +}; + pub const LinkageType = enum(c_int) { ExternalLinkage, AvailableExternallyLinkage, diff --git a/src/converter.zig b/src/converter.zig index 5b9c6eb..3a51098 100644 --- a/src/converter.zig +++ b/src/converter.zig @@ -104,7 +104,7 @@ const ModuleBuilder = struct { }; pub const FunctionBuilder = struct { - function: *llvm.Function, + handle: *llvm.Function, current_basic_block: *llvm.BasicBlock, current_scope: *llvm.DI.Scope, return_type: Type, @@ -315,6 +315,17 @@ const Converter = struct { fn parse_block(noalias converter: *Converter, noalias thread: *llvm.Thread, noalias module: *ModuleBuilder, noalias function: *FunctionBuilder) void { converter.skip_space(); + const block_line = converter.get_line(); + const block_column = converter.get_column(); + + const current_scope = function.current_scope; + defer function.current_scope = current_scope; + + if (module.di_builder) |di_builder| { + const lexical_block = di_builder.create_lexical_block(current_scope, module.file, block_line, block_column); + function.current_scope = lexical_block.to_scope(); + } + converter.expect_character(left_brace); const local_offset = function.locals.count; @@ -331,7 +342,7 @@ const Converter = struct { break; } - const require_semicolon = true; + var require_semicolon = true; const line = converter.get_line(); const column = converter.get_column(); @@ -404,7 +415,56 @@ const Converter = struct { const return_value = converter.parse_value(thread, module, function, function.return_type); thread.builder.create_ret(return_value); }, - else => unreachable, + .@"if" => { + const taken_block = thread.context.create_basic_block("", function.handle); + const not_taken_block = thread.context.create_basic_block("", function.handle); + + converter.skip_space(); + + converter.expect_character(left_parenthesis); + converter.skip_space(); + + const condition = converter.parse_value(thread, module, function, null); + + converter.skip_space(); + converter.expect_character(right_parenthesis); + + _ = thread.builder.create_conditional_branch(condition, taken_block, not_taken_block); + thread.builder.position_at_end(taken_block); + + converter.parse_block(thread, module, function); + + const is_first_block_terminated = function.current_basic_block.get_terminator() != null; + if (!is_first_block_terminated) { + @trap(); + } + + converter.skip_space(); + + var is_else = false; + if (is_identifier_start_ch(converter.content[converter.offset])) { + const identifier = converter.parse_identifier(); + is_else = lib.string.equal(identifier, "else"); + if (!is_else) { + converter.offset -= identifier.len; + } + } + + var is_second_block_terminated = false; + if (is_else) { + thread.builder.position_at_end(not_taken_block); + converter.parse_block(thread, module, function); + is_second_block_terminated = function.current_basic_block.get_terminator() != null; + } else { + @trap(); + } + + if (!(is_first_block_terminated and is_second_block_terminated)) { + @trap(); + } + + require_semicolon = false; + }, } } else { converter.report_error(); @@ -438,18 +498,32 @@ const Converter = struct { @"and", @"or", xor, + icmp_ne, + + pub fn to_int_predicate(expression_state: ExpressionState) llvm.IntPredicate { + return switch (expression_state) { + .icmp_ne => .ne, + else => unreachable, + }; + } }; - fn parse_value(noalias converter: *Converter, noalias thread: *llvm.Thread, noalias module: *ModuleBuilder, noalias function: ?*FunctionBuilder, expected_type: Type) *llvm.Value { + fn parse_value(noalias converter: *Converter, noalias thread: *llvm.Thread, noalias module: *ModuleBuilder, noalias function: ?*FunctionBuilder, maybe_expected_type: ?Type) *llvm.Value { converter.skip_space(); var value_state = ExpressionState.none; var previous_value: *llvm.Value = undefined; + var iterations: usize = 0; + var iterative_expected_type: ?Type = maybe_expected_type; + + const value = while (true) : (iterations += 1) { + if (iterations == 1 and iterative_expected_type == null) { + iterative_expected_type = Type.new(previous_value.get_type(), false); + } - const value = while (true) { const current_value = switch (converter.content[converter.offset] == left_parenthesis) { true => os.abort(), - false => converter.parse_single_value(thread, module, function, expected_type), + false => converter.parse_single_value(thread, module, function, iterative_expected_type), }; converter.skip_space(); @@ -472,11 +546,12 @@ const Converter = struct { .@"and" => thread.builder.create_and(left, right), .@"or" => thread.builder.create_or(left, right), .xor => thread.builder.create_xor(left, right), + .icmp_ne => |icmp| thread.builder.create_compare(icmp.to_int_predicate(), left, right), }; const ch = converter.content[converter.offset]; value_state = switch (ch) { - ';' => break previous_value, + ';', right_parenthesis => break previous_value, '-' => blk: { converter.offset += 1; break :blk .sub; @@ -491,14 +566,14 @@ const Converter = struct { }, '/' => blk: { converter.offset += 1; - break :blk switch (expected_type.signedness) { + break :blk switch (iterative_expected_type.?.signedness) { true => .sdiv, false => .udiv, }; }, '%' => blk: { converter.offset += 1; - switch (expected_type.signedness) { + switch (iterative_expected_type.?.signedness) { true => break :blk .srem, false => break :blk .urem, } @@ -520,7 +595,7 @@ const Converter = struct { break :blk switch (converter.content[converter.offset]) { '>' => b: { converter.offset += 1; - break :b switch (expected_type.signedness) { + break :b switch (iterative_expected_type.?.signedness) { true => .ashr, false => .lshr, }; @@ -540,6 +615,16 @@ const Converter = struct { converter.offset += 1; break :blk .xor; }, + '!' => blk: { + converter.offset += 1; + break :blk switch (converter.content[converter.offset]) { + '=' => b: { + converter.offset += 1; + break :b .icmp_ne; + }, + else => os.abort(), + }; + }, else => os.abort(), }; @@ -554,7 +639,7 @@ const Converter = struct { negative, }; - fn parse_single_value(noalias converter: *Converter, noalias thread: *llvm.Thread, noalias module: *ModuleBuilder, noalias maybe_function: ?*FunctionBuilder, expected_type: Type) *llvm.Value { + fn parse_single_value(noalias converter: *Converter, noalias thread: *llvm.Thread, noalias module: *ModuleBuilder, noalias maybe_function: ?*FunctionBuilder, expected_type: ?Type) *llvm.Value { converter.skip_space(); if (maybe_function) |function| { @@ -601,7 +686,7 @@ const Converter = struct { converter.report_error(); } }, - '0'...'9' => converter.parse_integer(expected_type, prefix == .negative), + '0'...'9' => converter.parse_integer(expected_type.?, prefix == .negative), else => os.abort(), }; @@ -615,7 +700,7 @@ fn is_space(ch: u8) bool { const StatementStartKeyword = enum { @"return", - foooooooooo, + @"if", }; pub const BuildMode = enum { @@ -855,7 +940,7 @@ pub noinline fn convert(options: ConvertOptions) void { thread.builder.position_at_end(entry_block); var function = FunctionBuilder{ - .function = handle, + .handle = handle, .current_basic_block = entry_block, .return_type = return_type, .current_scope = undefined, @@ -938,6 +1023,9 @@ pub noinline fn convert(options: ConvertOptions) void { if (lib.optimization_mode == .Debug) { const verify_result = module.handle.verify(); if (!verify_result.success) { + lib.print_string(module.handle.to_string()); + lib.print_string("============================\n"); + lib.print_string(verify_result.error_message orelse unreachable); os.abort(); } diff --git a/src/converter_test.zig b/src/converter_test.zig index 5f9f005..28a7c22 100644 --- a/src/converter_test.zig +++ b/src/converter_test.zig @@ -142,3 +142,7 @@ test "stack_sub" { test "global" { try invsrc(@src()); } + +test "simple_branch" { + try invsrc(@src()); +} diff --git a/src/llvm.cpp b/src/llvm.cpp index d731668..e103b6b 100644 --- a/src/llvm.cpp +++ b/src/llvm.cpp @@ -133,6 +133,16 @@ fn BBLLVMString stream_to_string(raw_string_ostream& stream) return { result, length }; } +EXPORT BBLLVMString llvm_function_to_string(Function& function) +{ + std::string buffer; + raw_string_ostream os(buffer); + function.print(os); + os.flush(); + auto result = stream_to_string(os); + return result; +} + EXPORT bool llvm_function_verify(Function& function, BBLLVMString* error_message) { std::string message_buffer; @@ -146,6 +156,7 @@ EXPORT bool llvm_function_verify(Function& function, BBLLVMString* error_message return !result; } + EXPORT bool llvm_module_verify(const Module& module, BBLLVMString* error_message) { std::string message_buffer; diff --git a/src/llvm_api.zig b/src/llvm_api.zig index 534211f..8fd9121 100644 --- a/src/llvm_api.zig +++ b/src/llvm_api.zig @@ -12,6 +12,9 @@ pub extern fn llvm_module_create_global_variable(module: *llvm.Module, global_ty pub extern fn llvm_module_create_function(module: *llvm.Module, function_type: *llvm.Type.Function, linkage_type: llvm.LinkageType, address_space: c_uint, name: llvm.String) *llvm.Function; pub extern fn llvm_context_create_basic_block(context: *llvm.Context, name: llvm.String, parent: *llvm.Function) *llvm.BasicBlock; +pub extern fn LLVMGetBasicBlockTerminator(basic_block: *llvm.BasicBlock) ?*llvm.Value; + +pub extern fn llvm_function_to_string(function: *llvm.Function) *llvm.String; pub extern fn llvm_function_verify(function: *llvm.Function, error_message: *llvm.String) bool; pub extern fn llvm_module_verify(module: *llvm.Module, error_message: *llvm.String) bool; @@ -33,6 +36,8 @@ pub extern fn LLVMBuildLShr(builder: *llvm.Builder, left: *llvm.Value, right: *l pub extern fn LLVMBuildAnd(builder: *llvm.Builder, left: *llvm.Value, right: *llvm.Value, name: [*:0]const u8) *llvm.Value; 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 LLVMBuildICmp(builder: *llvm.Builder, predicate: llvm.IntPredicate, left: *llvm.Value, right: *llvm.Value, name: [*:0]const u8) *llvm.Value; +pub extern fn LLVMBuildCondBr(builder: *llvm.Builder, condition: *llvm.Value, taken: *llvm.BasicBlock, not_taken: *llvm.BasicBlock) *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; @@ -108,6 +113,7 @@ pub extern fn LLVMDIBuilderCreateAutoVariable(builder: *llvm.DI.Builder, scope: pub extern fn LLVMDIBuilderInsertDeclareRecordAtEnd(builder: *llvm.DI.Builder, storage: *llvm.Value, local_variable: *llvm.DI.LocalVariable, expression: *llvm.DI.Expression, debug_location: *llvm.DI.Location, basic_block: *llvm.BasicBlock) *llvm.DI.Record; pub extern fn LLVMDIBuilderCreateGlobalVariableExpression(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name_pointer: [*]const u8, name_length: usize, linkage_name_pointer: [*]const u8, linkage_name_length: usize, file: *llvm.DI.File, line: c_uint, global_type: *llvm.DI.Type, local_to_unit: Bool, expression: *llvm.DI.Expression, declaration: ?*llvm.DI.Metadata, align_in_bits: u32) *llvm.DI.GlobalVariableExpression; pub extern fn llvm_global_variable_add_debug_info(global_variable: *llvm.GlobalVariable, debug_global_variable: *llvm.DI.GlobalVariableExpression) void; +pub extern fn LLVMDIBuilderCreateLexicalBlock(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, file: *llvm.DI.File, line: c_uint, column: c_uint) *llvm.DI.LexicalBlock; // Target pub extern fn llvm_default_target_triple() llvm.String; diff --git a/tests/simple_branch.bbb b/tests/simple_branch.bbb new file mode 100644 index 0000000..788d962 --- /dev/null +++ b/tests/simple_branch.bbb @@ -0,0 +1,12 @@ +[export] main = fn [cc(c)] () s32 +{ + >result: s32 = 1; + if (result != 1) + { + return 1; + } + else + { + return 0; + } +}