Basic switch
All checks were successful
CI / ci (ReleaseFast, ubuntu-latest) (pull_request) Successful in 1m57s
CI / ci (ReleaseSmall, ubuntu-latest) (pull_request) Successful in 1m56s
CI / ci (ReleaseSafe, ubuntu-latest) (pull_request) Successful in 2m2s
CI / ci (Debug, ubuntu-latest) (pull_request) Successful in 3m10s
CI / ci (ReleaseFast, ubuntu-latest) (push) Successful in 2m0s
CI / ci (ReleaseSafe, ubuntu-latest) (push) Successful in 2m7s
CI / ci (ReleaseSmall, ubuntu-latest) (push) Successful in 1m55s
CI / ci (Debug, ubuntu-latest) (push) Successful in 3m6s
All checks were successful
CI / ci (ReleaseFast, ubuntu-latest) (pull_request) Successful in 1m57s
CI / ci (ReleaseSmall, ubuntu-latest) (pull_request) Successful in 1m56s
CI / ci (ReleaseSafe, ubuntu-latest) (pull_request) Successful in 2m2s
CI / ci (Debug, ubuntu-latest) (pull_request) Successful in 3m10s
CI / ci (ReleaseFast, ubuntu-latest) (push) Successful in 2m0s
CI / ci (ReleaseSafe, ubuntu-latest) (push) Successful in 2m7s
CI / ci (ReleaseSmall, ubuntu-latest) (push) Successful in 1m55s
CI / ci (Debug, ubuntu-latest) (push) Successful in 3m6s
This commit is contained in:
parent
03484afd75
commit
b7aaa12f61
@ -982,6 +982,8 @@ pub const Builder = opaque {
|
|||||||
pub fn create_not(builder: *Builder, value: *Value) *Value {
|
pub fn create_not(builder: *Builder, value: *Value) *Value {
|
||||||
return api.LLVMBuildNot(builder, value, "");
|
return api.LLVMBuildNot(builder, value, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const create_switch = api.LLVMBuildSwitch;
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const GlobalValue = opaque {
|
pub const GlobalValue = opaque {
|
||||||
@ -1195,6 +1197,12 @@ pub const Instruction = opaque {
|
|||||||
return api.LLVMAddIncoming(phi, values.ptr, basic_blocks.ptr, @intCast(values.len));
|
return api.LLVMAddIncoming(phi, values.ptr, basic_blocks.ptr, @intCast(values.len));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Switch = opaque {
|
||||||
|
pub fn add_case(switchi: *Switch, case_value: *Value, case_block: *BasicBlock) void {
|
||||||
|
return api.LLVMAddCase(switchi, case_value, case_block);
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const DI = struct {
|
pub const DI = struct {
|
||||||
|
@ -450,6 +450,7 @@ pub const Type = struct {
|
|||||||
return switch (ty.bb) {
|
return switch (ty.bb) {
|
||||||
.integer => |integer| integer.signed,
|
.integer => |integer| integer.signed,
|
||||||
.bits => |bits| bits.backing_type.is_signed(),
|
.bits => |bits| bits.backing_type.is_signed(),
|
||||||
|
.enumerator => |enumerator| enumerator.backing_type.is_signed(),
|
||||||
else => @trap(),
|
else => @trap(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -470,6 +471,7 @@ pub const Type = struct {
|
|||||||
else => true,
|
else => true,
|
||||||
},
|
},
|
||||||
.bits => |bits| bits.backing_type.is_arbitrary_bit_integer(),
|
.bits => |bits| bits.backing_type.is_arbitrary_bit_integer(),
|
||||||
|
.enumerator => |enumerator| enumerator.backing_type.is_arbitrary_bit_integer(),
|
||||||
else => false,
|
else => false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -574,15 +576,9 @@ pub const Statement = struct {
|
|||||||
@"return": ?*Value,
|
@"return": ?*Value,
|
||||||
assignment: Assignment,
|
assignment: Assignment,
|
||||||
expression: *Value,
|
expression: *Value,
|
||||||
@"if": struct {
|
@"if": If,
|
||||||
condition: *Value,
|
@"while": While,
|
||||||
if_block: *LexicalBlock,
|
@"switch": Switch,
|
||||||
else_block: ?*LexicalBlock,
|
|
||||||
},
|
|
||||||
@"while": struct {
|
|
||||||
condition: *Value,
|
|
||||||
block: *LexicalBlock,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
line: u32,
|
line: u32,
|
||||||
column: u32,
|
column: u32,
|
||||||
@ -606,6 +602,28 @@ pub const Statement = struct {
|
|||||||
@"^=",
|
@"^=",
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const If = struct {
|
||||||
|
condition: *Value,
|
||||||
|
if_block: *LexicalBlock,
|
||||||
|
else_block: ?*LexicalBlock,
|
||||||
|
};
|
||||||
|
|
||||||
|
const While = struct {
|
||||||
|
condition: *Value,
|
||||||
|
block: *LexicalBlock,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Switch = struct {
|
||||||
|
discriminant: *Value,
|
||||||
|
clauses: []Clause,
|
||||||
|
|
||||||
|
const Clause = struct {
|
||||||
|
values: []const *Value,
|
||||||
|
block: *LexicalBlock,
|
||||||
|
basic_block: *llvm.BasicBlock = undefined,
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const Unary = struct {
|
const Unary = struct {
|
||||||
@ -788,6 +806,7 @@ pub const Value = struct {
|
|||||||
},
|
},
|
||||||
.undefined => true,
|
.undefined => true,
|
||||||
.call => false,
|
.call => false,
|
||||||
|
.enum_literal => true,
|
||||||
else => @trap(),
|
else => @trap(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1561,6 +1580,7 @@ pub const Module = struct {
|
|||||||
@"if",
|
@"if",
|
||||||
// TODO: make `unreachable` a statement start keyword?
|
// TODO: make `unreachable` a statement start keyword?
|
||||||
@"while",
|
@"while",
|
||||||
|
@"switch",
|
||||||
};
|
};
|
||||||
|
|
||||||
const rules = blk: {
|
const rules = blk: {
|
||||||
@ -2245,8 +2265,7 @@ pub const Module = struct {
|
|||||||
'A'...'Z', 'a'...'z' => blk: {
|
'A'...'Z', 'a'...'z' => blk: {
|
||||||
const statement_start_identifier = module.parse_identifier();
|
const statement_start_identifier = module.parse_identifier();
|
||||||
|
|
||||||
if (lib.string.to_enum(StatementStartKeyword, statement_start_identifier)) |statement_start_keyword| {
|
if (lib.string.to_enum(StatementStartKeyword, statement_start_identifier)) |statement_start_keyword| switch (statement_start_keyword) {
|
||||||
switch (statement_start_keyword) {
|
|
||||||
._ => @trap(),
|
._ => @trap(),
|
||||||
.@"return" => break :blk .{
|
.@"return" => break :blk .{
|
||||||
.@"return" = module.parse_value(function, .{}),
|
.@"return" = module.parse_value(function, .{}),
|
||||||
@ -2315,7 +2334,77 @@ pub const Module = struct {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
.@"switch" => {
|
||||||
|
module.skip_space();
|
||||||
|
module.expect_character(left_parenthesis);
|
||||||
|
module.skip_space();
|
||||||
|
|
||||||
|
const discriminant = module.parse_value(function, .{});
|
||||||
|
|
||||||
|
module.skip_space();
|
||||||
|
module.expect_character(right_parenthesis);
|
||||||
|
|
||||||
|
module.skip_space();
|
||||||
|
module.expect_character(left_brace);
|
||||||
|
|
||||||
|
var clause_buffer: [64]Statement.Switch.Clause = undefined;
|
||||||
|
var clause_count: u64 = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
module.skip_space();
|
||||||
|
|
||||||
|
var case_buffer: [64]*Value = undefined;
|
||||||
|
var case_count: u64 = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const case_value = module.parse_value(function, .{});
|
||||||
|
case_buffer[case_count] = case_value;
|
||||||
|
case_count += 1;
|
||||||
|
|
||||||
|
_ = module.consume_character_if_match(',');
|
||||||
|
|
||||||
|
module.skip_space();
|
||||||
|
|
||||||
|
if (module.consume_character_if_match('=')) {
|
||||||
|
module.expect_character('>');
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.skip_space();
|
||||||
|
|
||||||
|
const clause_block = module.parse_block(function);
|
||||||
|
|
||||||
|
const clause_values = module.arena.allocate(*Value, case_count);
|
||||||
|
@memcpy(clause_values, case_buffer[0..case_count]);
|
||||||
|
|
||||||
|
clause_buffer[clause_count] = .{
|
||||||
|
.values = clause_values,
|
||||||
|
.block = clause_block,
|
||||||
|
};
|
||||||
|
clause_count += 1;
|
||||||
|
|
||||||
|
_ = module.consume_character_if_match(',');
|
||||||
|
|
||||||
|
module.skip_space();
|
||||||
|
|
||||||
|
if (module.consume_character_if_match(right_brace)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const clauses = module.arena.allocate(Statement.Switch.Clause, clause_count);
|
||||||
|
@memcpy(clauses, clause_buffer[0..clause_count]);
|
||||||
|
|
||||||
|
require_semicolon = false;
|
||||||
|
|
||||||
|
break :blk .{
|
||||||
|
.@"switch" = .{
|
||||||
|
.discriminant = discriminant,
|
||||||
|
.clauses = clauses,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
} else {
|
} else {
|
||||||
module.offset -= statement_start_identifier.len;
|
module.offset -= statement_start_identifier.len;
|
||||||
|
|
||||||
@ -3356,14 +3445,12 @@ pub const Module = struct {
|
|||||||
const field_index = field_count;
|
const field_index = field_count;
|
||||||
const field_name = module.parse_identifier();
|
const field_name = module.parse_identifier();
|
||||||
module.skip_space();
|
module.skip_space();
|
||||||
|
const has_explicit_value = module.consume_character_if_match('=');
|
||||||
const field_value = if (module.consume_character_if_match('=')) blk: {
|
const field_value = if (has_explicit_value) blk: {
|
||||||
module.skip_space();
|
module.skip_space();
|
||||||
const field_value = module.parse_integer_value(false);
|
const field_value = module.parse_integer_value(false);
|
||||||
break :blk field_value;
|
break :blk field_value;
|
||||||
} else {
|
} else field_index;
|
||||||
@trap();
|
|
||||||
};
|
|
||||||
|
|
||||||
field_buffer[field_index] = .{
|
field_buffer[field_index] = .{
|
||||||
.name = field_name,
|
.name = field_name,
|
||||||
@ -6448,6 +6535,69 @@ pub const Module = struct {
|
|||||||
|
|
||||||
module.llvm.builder.position_at_end(loop_end_block);
|
module.llvm.builder.position_at_end(loop_end_block);
|
||||||
},
|
},
|
||||||
|
.@"switch" => |switch_statement| {
|
||||||
|
const previous_exit_block = current_function.exit_block;
|
||||||
|
defer current_function.exit_block = previous_exit_block;
|
||||||
|
|
||||||
|
const exit_block = module.llvm.context.create_basic_block("exit_block", null);
|
||||||
|
current_function.exit_block = exit_block;
|
||||||
|
|
||||||
|
module.analyze(function, switch_statement.discriminant, .{});
|
||||||
|
const switch_discriminant_type = switch_statement.discriminant.type.?;
|
||||||
|
|
||||||
|
switch (switch_discriminant_type.bb) {
|
||||||
|
.enumerator => |enumerator| {
|
||||||
|
_ = enumerator;
|
||||||
|
var else_clause_index: ?usize = null;
|
||||||
|
var total_discriminant_cases: u32 = 0;
|
||||||
|
for (switch_statement.clauses, 0..) |*clause, clause_index| {
|
||||||
|
clause.basic_block = module.llvm.context.create_basic_block("case_block", llvm_function);
|
||||||
|
total_discriminant_cases += @intCast(clause.values.len);
|
||||||
|
if (clause.values.len == 0) {
|
||||||
|
if (else_clause_index != null) {
|
||||||
|
module.report_error();
|
||||||
|
}
|
||||||
|
else_clause_index = clause_index;
|
||||||
|
} else {
|
||||||
|
for (clause.values) |v| {
|
||||||
|
module.analyze(function, v, .{ .type = switch_discriminant_type });
|
||||||
|
if (!v.is_constant()) {
|
||||||
|
module.report_error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const else_block = if (else_clause_index) |i| switch_statement.clauses[i].basic_block else module.llvm.context.create_basic_block("else_case_block", llvm_function);
|
||||||
|
const switch_instruction = module.llvm.builder.create_switch(switch_statement.discriminant.llvm.?, else_block, total_discriminant_cases);
|
||||||
|
for (switch_statement.clauses) |clause| {
|
||||||
|
for (clause.values) |v| {
|
||||||
|
switch_instruction.add_case(v.llvm.?, clause.basic_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
current_function.exit_block = exit_block;
|
||||||
|
module.llvm.builder.position_at_end(clause.basic_block);
|
||||||
|
module.analyze_block(function, clause.block);
|
||||||
|
if (module.llvm.builder.get_insert_block() != null) {
|
||||||
|
_ = module.llvm.builder.create_branch(exit_block);
|
||||||
|
module.llvm.builder.clear_insertion_position();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current_function.exit_block = exit_block;
|
||||||
|
|
||||||
|
if (else_clause_index) |i| {
|
||||||
|
_ = i;
|
||||||
|
@trap();
|
||||||
|
} else {
|
||||||
|
module.llvm.builder.position_at_end(else_block);
|
||||||
|
_ = module.llvm.builder.create_unreachable();
|
||||||
|
module.llvm.builder.clear_insertion_position();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => @trap(),
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6688,7 +6838,7 @@ pub const Module = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn align_integer_type(module: *Module, ty: *Type) *Type {
|
pub fn align_integer_type(module: *Module, ty: *Type) *Type {
|
||||||
assert(ty.bb == .integer);
|
assert(ty.bb == .integer or ty.bb == .enumerator);
|
||||||
const bit_count = ty.get_bit_size();
|
const bit_count = ty.get_bit_size();
|
||||||
const abi_bit_count: u32 = @intCast(@max(8, lib.next_power_of_two(bit_count)));
|
const abi_bit_count: u32 = @intCast(@max(8, lib.next_power_of_two(bit_count)));
|
||||||
if (bit_count != abi_bit_count) {
|
if (bit_count != abi_bit_count) {
|
||||||
|
@ -99,6 +99,8 @@ pub extern fn LLVMAddIncoming(phi: *llvm.Instruction.Phi, incoming_value_pointer
|
|||||||
pub extern fn LLVMBuildSelect(builder: *llvm.Builder, condition: *llvm.Value, true_value: *llvm.Value, false_value: *llvm.Value, name: [*:0]const u8) *llvm.Value;
|
pub extern fn LLVMBuildSelect(builder: *llvm.Builder, condition: *llvm.Value, true_value: *llvm.Value, false_value: *llvm.Value, name: [*:0]const u8) *llvm.Value;
|
||||||
|
|
||||||
pub extern fn LLVMBuildVAArg(builder: *llvm.Builder, va_list: *llvm.Value, arg_type: *llvm.Type, name: [*:0]const u8) *llvm.Value;
|
pub extern fn LLVMBuildVAArg(builder: *llvm.Builder, va_list: *llvm.Value, arg_type: *llvm.Type, name: [*:0]const u8) *llvm.Value;
|
||||||
|
pub extern fn LLVMBuildSwitch(builder: *llvm.Builder, discriminant: *llvm.Value, else_basic_block: *llvm.BasicBlock, case_count: c_uint) *llvm.Instruction.Switch;
|
||||||
|
pub extern fn LLVMAddCase(switchi: *llvm.Instruction.Switch, case_value: *llvm.Value, case_block: *llvm.BasicBlock) void;
|
||||||
|
|
||||||
// Casts
|
// Casts
|
||||||
pub extern fn LLVMBuildZExt(builder: *llvm.Builder, value: *llvm.Value, destination_type: *llvm.Type, name: [*:0]const u8) *llvm.Value;
|
pub extern fn LLVMBuildZExt(builder: *llvm.Builder, value: *llvm.Value, destination_type: *llvm.Type, name: [*:0]const u8) *llvm.Value;
|
||||||
|
26
tests/basic_switch.bbb
Normal file
26
tests/basic_switch.bbb
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
E = enum
|
||||||
|
{
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c,
|
||||||
|
}
|
||||||
|
|
||||||
|
[export] main = fn [cc(c)] () s32
|
||||||
|
{
|
||||||
|
>some_enum: E = .a;
|
||||||
|
switch (some_enum)
|
||||||
|
{
|
||||||
|
.a =>
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
.b =>
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
},
|
||||||
|
.c =>
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user