Else if and empty if
All checks were successful
CI / ci (ReleaseFast, ubuntu-latest) (pull_request) Successful in 2m3s
CI / ci (ReleaseSmall, ubuntu-latest) (pull_request) Successful in 2m3s
CI / ci (ReleaseSafe, ubuntu-latest) (pull_request) Successful in 2m12s
CI / ci (Debug, ubuntu-latest) (pull_request) Successful in 3m13s
CI / ci (ReleaseFast, ubuntu-latest) (push) Successful in 2m3s
CI / ci (ReleaseSmall, ubuntu-latest) (push) Successful in 2m2s
CI / ci (ReleaseSafe, ubuntu-latest) (push) Successful in 2m7s
CI / ci (Debug, ubuntu-latest) (push) Successful in 3m10s

This commit is contained in:
David Gonzalez Martin 2025-04-17 14:07:55 -06:00
parent d2e488edb1
commit 4dedaf3006
6 changed files with 795 additions and 685 deletions

View File

@ -617,6 +617,7 @@ pub const Statement = struct {
@"if": If,
@"while": While,
@"switch": Switch,
block: *LexicalBlock,
},
line: u32,
column: u32,
@ -643,8 +644,8 @@ pub const Statement = struct {
const If = struct {
condition: *Value,
if_block: *LexicalBlock,
else_block: ?*LexicalBlock,
if_statement: *Statement,
else_statement: ?*Statement,
};
const While = struct {
@ -2242,34 +2243,7 @@ pub const Module = struct {
return result.?;
}
fn parse_block(module: *Module, function: *Global) *LexicalBlock {
const parent_scope = module.current_scope;
const block = module.lexical_blocks.append(.{
.statements = .initialize(),
.locals = .initialize(),
.scope = .{
.kind = .local,
.parent = parent_scope,
.line = module.get_line(),
.column = module.get_column(),
},
});
module.current_scope = &block.scope;
defer module.current_scope = parent_scope;
module.expect_character(left_brace);
while (true) {
module.skip_space();
if (module.offset == module.content.len) {
break;
}
if (module.consume_character_if_match(right_brace)) {
break;
}
fn parse_statement(module: *Module, function: *Global, block: *LexicalBlock) *Statement {
const statement_line = module.get_line();
const statement_column = module.get_column();
@ -2333,7 +2307,7 @@ pub const Module = struct {
module.skip_space();
const if_block = module.parse_block(function);
const if_statement = module.parse_statement(function, block);
module.skip_space();
@ -2348,15 +2322,18 @@ pub const Module = struct {
}
}
const else_block = if (is_else) module.parse_block(function) else null;
const else_block = switch (is_else) {
true => module.parse_statement(function, block),
false => null,
};
require_semicolon = false;
break :blk .{
.@"if" = .{
.condition = condition,
.if_block = if_block,
.else_block = else_block,
.if_statement = if_statement,
.else_statement = else_block,
},
};
},
@ -2575,16 +2552,55 @@ pub const Module = struct {
}
}
},
left_brace => blk: {
require_semicolon = false;
break :blk .{
.block = module.parse_block(function),
};
},
else => @trap(),
},
.line = statement_line,
.column = statement_column,
};
_ = block.statements.append(statement);
if (require_semicolon) {
module.expect_character(';');
}
return statement;
}
fn parse_block(module: *Module, function: *Global) *LexicalBlock {
const parent_scope = module.current_scope;
const block = module.lexical_blocks.append(.{
.statements = .initialize(),
.locals = .initialize(),
.scope = .{
.kind = .local,
.parent = parent_scope,
.line = module.get_line(),
.column = module.get_column(),
},
});
module.current_scope = &block.scope;
defer module.current_scope = parent_scope;
module.expect_character(left_brace);
while (true) {
module.skip_space();
if (module.offset == module.content.len) {
break;
}
if (module.consume_character_if_match(right_brace)) {
break;
}
const statement = module.parse_statement(function, block);
_ = block.statements.append(statement);
}
return block;
@ -4445,6 +4461,7 @@ pub const Module = struct {
};
_ = &destination_pointer;
var destination_type = return_type_abi.semantic_type;
if (return_type_abi.semantic_type.bb.structure.fields.len > 0) {
// CreateCoercedStore(
// CI, StorePtr,
@ -4454,7 +4471,6 @@ pub const Module = struct {
const source_value = llvm_call;
const source_type = function_type.abi_return_type;
// const source_size = source_type.get_byte_size();
var destination_type = return_type_abi.semantic_type;
const destination_size = destination_type.get_byte_size();
// const destination_alignment = destination_type.get_byte_alignment();
const left_destination_size = destination_size - return_type_abi.attributes.direct.offset;
@ -4465,7 +4481,13 @@ pub const Module = struct {
@trap();
}
if (left_llvm) |l| {
assert(destination_pointer == l);
return destination_pointer;
} else return switch (value.kind) {
.left => @trap(),
.right => module.create_load(.{ .type = destination_type, .value = destination_pointer }),
};
},
.indirect => {
return llvm_indirect_return_value;
@ -5700,7 +5722,7 @@ pub const Module = struct {
const array_element_pointer = module.llvm.builder.create_gep(.{
.type = name_array_variable_type.to_type(),
.aggregate = name_array_variable.to_value(),
.indices = &.{uint64_zero, body_index_load},
.indices = &.{ uint64_zero, body_index_load },
});
const element_length_pointer = module.llvm.builder.create_struct_gep(slice_struct_type.llvm.abi.?.to_struct(), array_element_pointer, 1);
@ -5716,10 +5738,7 @@ pub const Module = struct {
module.llvm.builder.position_at_end(length_match_block);
const s32 = module.integer_type(32, true);
s32.resolve(module);
const memcmp = if (module.llvm.memcmp) |memcmp| {
_ = memcmp;
@trap();
} else b: {
const memcmp = if (module.llvm.memcmp) |memcmp| memcmp else b: {
if (module.llvm.module.get_named_function("memcmp")) |memcmp| {
module.llvm.memcmp = memcmp;
break :b memcmp;
@ -5738,7 +5757,7 @@ pub const Module = struct {
const length_array_element_pointer = module.llvm.builder.create_gep(.{
.type = name_array_variable_type.to_type(),
.aggregate = name_array_variable.to_value(),
.indices = &.{uint64_zero, length_index_load},
.indices = &.{ uint64_zero, length_index_load },
});
const element_pointer_pointer = module.llvm.builder.create_struct_gep(slice_struct_type.llvm.abi.?.to_struct(), length_array_element_pointer, 0);
const element_pointer = module.llvm.builder.create_load(module.llvm.pointer_type, element_pointer_pointer);
@ -5754,7 +5773,7 @@ pub const Module = struct {
const value_array_element_pointer = module.llvm.builder.create_gep(.{
.type = value_array_variable_type.to_type(),
.aggregate = value_array_variable.to_value(),
.indices = &.{uint64_zero, content_index_load},
.indices = &.{ uint64_zero, content_index_load },
});
const enum_value_load = module.llvm.builder.create_load(string_to_enum.enum_type.llvm.memory.?, value_array_element_pointer);
enum_value_load.set_alignment(string_to_enum.enum_type.get_byte_alignment());
@ -6581,27 +6600,16 @@ pub const Module = struct {
value.llvm = llvm_value;
}
pub fn analyze_block(module: *Module, function: *Global, block: *LexicalBlock) void {
pub fn analyze_statement(module: *Module, function: *Global, block: *LexicalBlock, statement: *Statement, last_line: *u32, last_column: *u32, last_statement_debug_location: **llvm.DI.Location) void {
const llvm_function = function.variable.storage.?.llvm.?.to_function();
if (module.has_debug_info) {
const lexical_block = module.llvm.di_builder.create_lexical_block(block.scope.parent.?.llvm.?, module.llvm.file, block.scope.line, block.scope.column);
block.scope.llvm = lexical_block.to_scope();
}
const current_function = &function.variable.storage.?.bb.function;
var last_line: u32 = 0;
var last_column: u32 = 0;
var last_statement_debug_location: *llvm.DI.Location = undefined;
for (block.statements.get_slice()) |statement| {
if (module.has_debug_info) {
if (statement.line != last_line or statement.column != last_column) {
if (statement.line != last_line.* or statement.column != last_column.*) {
const inlined_at: ?*llvm.DI.Metadata = null; // TODO
last_statement_debug_location = llvm.DI.create_debug_location(module.llvm.context, statement.line, statement.column, block.scope.llvm.?, inlined_at);
module.llvm.builder.set_current_debug_location(last_statement_debug_location);
last_line = statement.line;
last_column = statement.column;
last_statement_debug_location.* = llvm.DI.create_debug_location(module.llvm.context, statement.line, statement.column, block.scope.llvm.?, inlined_at);
module.llvm.builder.set_current_debug_location(last_statement_debug_location.*);
last_line.* = statement.line;
last_column.* = statement.column;
}
}
@ -6624,7 +6632,7 @@ pub const Module = struct {
}, .memory);
if (module.has_debug_info) {
module.llvm.builder.set_current_debug_location(last_statement_debug_location);
module.llvm.builder.set_current_debug_location(last_statement_debug_location.*);
}
// Clang equivalent: CodeGenFunction::EmitReturnStmt
@ -6719,7 +6727,7 @@ pub const Module = struct {
module.analyze_value_type(function, local.variable.initial_value, .{ .type = local.variable.type });
local.variable.resolve_type(local.variable.initial_value.type.?);
if (expected_type) |lvt| assert(lvt == local.variable.type);
module.emit_local_storage(local, last_statement_debug_location);
module.emit_local_storage(local, last_statement_debug_location.*);
module.emit_assignment(function, local.variable.storage.?.llvm.?, local.variable.storage.?.type.?, local.variable.initial_value);
},
@ -6816,7 +6824,7 @@ pub const Module = struct {
.@"if" => |if_statement| {
const taken_block = module.llvm.context.create_basic_block("if.true", llvm_function);
const not_taken_block = module.llvm.context.create_basic_block("if.false", llvm_function);
const exit_block = module.llvm.context.create_basic_block("if.end", null);
const exit_block = module.llvm.context.create_basic_block("if.end", llvm_function);
module.analyze(function, if_statement.condition, .{}, .memory);
const llvm_condition = switch (if_statement.condition.type.?.bb) {
@ -6833,49 +6841,21 @@ pub const Module = struct {
current_function.exit_block = exit_block;
module.analyze_block(function, if_statement.if_block);
const if_final_block = module.llvm.builder.get_insert_block();
module.analyze_statement(function, block, if_statement.if_statement, last_line, last_line, last_statement_debug_location);
if (module.llvm.builder.get_insert_block() != null) {
_ = module.llvm.builder.create_branch(exit_block);
}
module.llvm.builder.position_at_end(not_taken_block);
var is_second_block_terminated = false;
if (if_statement.else_block) |else_block| {
current_function.exit_block = exit_block;
module.analyze_block(function, else_block);
is_second_block_terminated = module.llvm.builder.get_insert_block() == null;
} else {
if (if_final_block) |final_block| {
const current_insert_block = module.llvm.builder.get_insert_block();
defer if (current_insert_block) |b| {
module.llvm.builder.position_at_end(b);
};
module.llvm.builder.position_at_end(final_block);
_ = module.llvm.builder.create_branch(not_taken_block);
module.llvm.builder.clear_insertion_position();
if (if_statement.else_statement) |else_statement| {
module.analyze_statement(function, block, else_statement, last_line, last_line, last_statement_debug_location);
}
assert(exit_block.to_value().use_empty());
not_taken_block.to_value().set_name("if.end");
assert(exit_block.get_parent() == null);
exit_block.delete();
if (module.llvm.builder.get_insert_block() != null) {
_ = module.llvm.builder.create_branch(exit_block);
}
if (!(if_final_block == null and is_second_block_terminated)) {
if (if_final_block != null) {
// @trap();
}
if (!is_second_block_terminated) {
// if (is_else) {
// @trap();
// } else {}
}
} else {
assert(exit_block.get_parent() == null);
// TODO:
// if call `exit_block.erase_from_paren()`, it crashes, investigate
exit_block.delete();
}
module.llvm.builder.position_at_end(exit_block);
},
.@"while" => |while_loop| {
const loop_entry_block = module.llvm.context.create_basic_block("while.entry", llvm_function);
@ -6982,8 +6962,23 @@ pub const Module = struct {
else => @trap(),
}
},
.block => |child_block| module.analyze_block(function, child_block),
}
}
pub fn analyze_block(module: *Module, function: *Global, block: *LexicalBlock) void {
if (module.has_debug_info) {
const lexical_block = module.llvm.di_builder.create_lexical_block(block.scope.parent.?.llvm.?, module.llvm.file, block.scope.line, block.scope.column);
block.scope.llvm = lexical_block.to_scope();
}
var last_line: u32 = 0;
var last_column: u32 = 0;
var last_statement_debug_location: *llvm.DI.Location = undefined;
for (block.statements.get_slice()) |statement| {
module.analyze_statement(function, block, statement, &last_line, &last_column, &last_statement_debug_location);
}
}
fn emit_assignment(module: *Module, function: *Global, left_llvm: *llvm.Value, left_type: *Type, right: *Value) void {

View File

@ -231,6 +231,18 @@ CompilerCommand = enum
test,
}
BuildMode = enum
{
debug_none,
debug_fast,
debug_size,
soft_optimize,
optimize_for_speed,
optimize_for_size,
aggressively_optimize_for_speed,
aggressively_optimize_for_size,
}
[export] main = fn [cc(c)] (argument_count: u32, argv: &&u8) s32
{
global_state_initialize();
@ -242,7 +254,7 @@ CompilerCommand = enum
>command_string = c_string_to_slice(argv[1]);
> command_string_to_enum = #string_to_enum(CompilerCommand, command_string);
>command_string_to_enum = #string_to_enum(CompilerCommand, command_string);
if (!command_string_to_enum.is_valid)
{
return 1;
@ -253,6 +265,41 @@ CompilerCommand = enum
{
.compile =>
{
if (argument_count < 3)
{
return 1;
}
>build_mode: BuildMode = .debug_none;
>has_debug_info: u1 = 1;
if (argument_count >= 4)
{
>build_mode_string_to_enum = #string_to_enum(BuildMode, c_string_to_slice(argv[3]));
if (!build_mode_string_to_enum.is_valid)
{
return 1;
}
build_mode = build_mode_string_to_enum.enum_value;
}
if (argument_count >= 5)
{
>has_debug_info_string = c_string_to_slice(argv[4]);
if (string_equal(has_debug_info_string, "true"))
{
has_debug_info = 1;
}
else if (string_equal(has_debug_info_string, "false"))
{
has_debug_info = 0;
}
else
{
return 1;
}
}
>relative_file_path_pointer = argv[2];
if (!relative_file_path_pointer)
{

View File

@ -311,4 +311,7 @@ const names = &[_][]const u8{
"c_abi",
"string_to_enum",
"abi_enum_bool",
"empty_if",
"else_if",
"else_if_complicated",
};

16
tests/else_if.bbb Normal file
View File

@ -0,0 +1,16 @@
[export] main = fn [cc(c)] () s32
{
>result: s32 = 0;
if (result == 1)
{
return 1;
}
else if (result == 0)
{
return 0;
}
else
{
return 5;
}
}

View File

@ -0,0 +1,37 @@
Foo = enum
{
a,b,c,
}
[export] main = fn [cc(c)] (argument_count: u32) s32
{
>result: s32 = 0;
>foo: Foo = .b;
switch (foo)
{
.b =>
{
if (argument_count != 0)
{
>a: s32 = 1;
if (result == 1)
{
}
else if (result == 0)
{
return 0;
}
else
{
return 5;
}
return a;
}
},
else =>
{
}
}
return 0;
}

12
tests/empty_if.bbb Normal file
View File

@ -0,0 +1,12 @@
[export] main = fn [cc(c)] (argument_count: u32) s32
{
>result: s32 = 0;
if (argument_count != 1)
{
result = 1;
}
else
{
}
return result;
}