wip
This commit is contained in:
parent
9b1c5ce7b0
commit
a1113f2555
1034
src/compiler.bbb
1034
src/compiler.bbb
File diff suppressed because it is too large
Load Diff
@ -435,6 +435,10 @@ global_variable String names[] =
|
||||
string_literal("opaque"),
|
||||
string_literal("basic_struct_passing"),
|
||||
string_literal("enum_arbitrary_abi"),
|
||||
string_literal("enum_debug_info"),
|
||||
string_literal("return_array"),
|
||||
string_literal("bool_pair"),
|
||||
string_literal("min_max"),
|
||||
};
|
||||
|
||||
void entry_point(Slice<char* const> arguments, Slice<char* const> envp)
|
||||
|
@ -351,13 +351,16 @@ union AbiRegisterCount
|
||||
AbiRegisterCountSystemV system_v;
|
||||
};
|
||||
|
||||
struct TypeFunction
|
||||
struct TypeFunctionBase
|
||||
{
|
||||
Type* semantic_return_type;
|
||||
Slice<Type*> semantic_argument_types;
|
||||
CallingConvention calling_convention;
|
||||
bool is_variable_arguments;
|
||||
// ABI
|
||||
};
|
||||
|
||||
struct TypeFunctionAbi
|
||||
{
|
||||
Slice<Type*> abi_argument_types;
|
||||
Type* abi_return_type;
|
||||
AbiRegisterCount available_registers;
|
||||
@ -365,6 +368,13 @@ struct TypeFunction
|
||||
AbiInformation return_abi;
|
||||
};
|
||||
|
||||
struct TypeFunction
|
||||
{
|
||||
TypeFunctionBase base;
|
||||
TypeFunctionAbi abi;
|
||||
Type* next;
|
||||
};
|
||||
|
||||
struct TypePointer
|
||||
{
|
||||
Type* element_type;
|
||||
@ -629,6 +639,11 @@ fn u64 get_bit_size(Type* type)
|
||||
case TypeId::integer: return type->integer.bit_count;
|
||||
case TypeId::enumerator: return get_bit_size(type->enumerator.backing_type);
|
||||
case TypeId::alias: return get_bit_size(type->alias.type);
|
||||
case TypeId::array: return get_byte_size(type->array.element_type) * type->array.element_count * 8;
|
||||
case TypeId::pointer: return 64;
|
||||
case TypeId::structure: return type->structure.byte_size * 8;
|
||||
case TypeId::union_type: return type->union_type.byte_size * 8;
|
||||
case TypeId::enum_array: return get_byte_size(type->enum_array.element_type) * type->enum_array.enum_type->enumerator.fields.length * 8;
|
||||
default: trap();
|
||||
}
|
||||
}
|
||||
@ -793,7 +808,7 @@ struct Block
|
||||
enum class ValueId
|
||||
{
|
||||
infer_or_ignore,
|
||||
external_function,
|
||||
forward_declared_function,
|
||||
function,
|
||||
constant_integer,
|
||||
unary,
|
||||
@ -905,6 +920,8 @@ enum class BinaryId
|
||||
logical_or,
|
||||
logical_and_shortcircuit,
|
||||
logical_or_shortcircuit,
|
||||
max,
|
||||
min,
|
||||
};
|
||||
|
||||
struct ValueBinary
|
||||
@ -1078,6 +1095,7 @@ struct Value
|
||||
case ValueId::array_expression:
|
||||
case ValueId::call:
|
||||
case ValueId::select:
|
||||
case ValueId::slice_expression:
|
||||
return false;
|
||||
case ValueId::variable_reference:
|
||||
{
|
||||
@ -1164,7 +1182,11 @@ struct LLVMIntrinsicId
|
||||
|
||||
enum class IntrinsicIndex
|
||||
{
|
||||
smax,
|
||||
smin,
|
||||
trap,
|
||||
umax,
|
||||
umin,
|
||||
va_start,
|
||||
va_end,
|
||||
va_copy,
|
||||
@ -1172,7 +1194,11 @@ enum class IntrinsicIndex
|
||||
};
|
||||
|
||||
global_variable String intrinsic_names[] = {
|
||||
string_literal("llvm.smax"),
|
||||
string_literal("llvm.smin"),
|
||||
string_literal("llvm.trap"),
|
||||
string_literal("llvm.umax"),
|
||||
string_literal("llvm.umin"),
|
||||
string_literal("llvm.va_start"),
|
||||
string_literal("llvm.va_end"),
|
||||
string_literal("llvm.va_copy"),
|
||||
@ -1180,6 +1206,52 @@ global_variable String intrinsic_names[] = {
|
||||
|
||||
static_assert(array_length(intrinsic_names) == (u64)IntrinsicIndex::count);
|
||||
|
||||
struct LLVMAttributeId
|
||||
{
|
||||
u32 n;
|
||||
};
|
||||
|
||||
enum class AttributeIndex
|
||||
{
|
||||
align,
|
||||
alwaysinline,
|
||||
byval,
|
||||
dead_on_unwind,
|
||||
inlinehint,
|
||||
inreg,
|
||||
naked,
|
||||
noalias,
|
||||
noinline,
|
||||
noreturn,
|
||||
nounwind,
|
||||
signext,
|
||||
sret,
|
||||
writable,
|
||||
zeroext,
|
||||
|
||||
count,
|
||||
};
|
||||
|
||||
global_variable String attribute_names[] = {
|
||||
string_literal("align"),
|
||||
string_literal("alwaysinline"),
|
||||
string_literal("byval"),
|
||||
string_literal("dead_on_unwind"),
|
||||
string_literal("inlinehint"),
|
||||
string_literal("inreg"),
|
||||
string_literal("naked"),
|
||||
string_literal("noalias"),
|
||||
string_literal("noinline"),
|
||||
string_literal("noreturn"),
|
||||
string_literal("nounwind"),
|
||||
string_literal("signext"),
|
||||
string_literal("sret"),
|
||||
string_literal("writable"),
|
||||
string_literal("zeroext"),
|
||||
};
|
||||
|
||||
static_assert(array_length(attribute_names) == (u64)AttributeIndex::count);
|
||||
|
||||
struct ModuleLLVM
|
||||
{
|
||||
LLVMContextRef context;
|
||||
@ -1193,6 +1265,7 @@ struct ModuleLLVM
|
||||
LLVMTypeRef pointer_type;
|
||||
LLVMTypeRef void_type;
|
||||
LLVMIntrinsicId intrinsic_table[(u64)IntrinsicIndex::count];
|
||||
LLVMAttributeId attribute_table[(u64)AttributeIndex::count];
|
||||
LLVMValueRef memcmp;
|
||||
LLVMMetadataRef inlined_at;
|
||||
LLVMBasicBlockRef continue_block;
|
||||
@ -1213,6 +1286,7 @@ struct Module
|
||||
Type* first_pair_struct_type;
|
||||
Type* first_array_type;
|
||||
Type* first_enum_array_type;
|
||||
Type* first_function_type;
|
||||
|
||||
Type* va_list_type;
|
||||
|
||||
|
1805
src/emitter.cpp
1805
src/emitter.cpp
File diff suppressed because it is too large
Load Diff
243
src/parser.cpp
243
src/parser.cpp
@ -10,6 +10,8 @@ enum class ValueIntrinsic
|
||||
integer_max,
|
||||
int_from_enum,
|
||||
int_from_pointer,
|
||||
max,
|
||||
min,
|
||||
pointer_cast,
|
||||
pointer_from_int,
|
||||
select,
|
||||
@ -668,7 +670,7 @@ fn Type* parse_type(Module* module, Scope* scope)
|
||||
{
|
||||
case TypeIntrinsic::return_type:
|
||||
{
|
||||
auto return_type = module->current_function->variable.type->function.semantic_return_type;
|
||||
auto return_type = module->current_function->variable.type->function.base.semantic_return_type;
|
||||
return return_type;
|
||||
} break;
|
||||
case TypeIntrinsic::count: report_error();
|
||||
@ -862,6 +864,8 @@ fn Token tokenize(Module* module)
|
||||
string_literal("integer_max"),
|
||||
string_literal("int_from_enum"),
|
||||
string_literal("int_from_pointer"),
|
||||
string_literal("max"),
|
||||
string_literal("min"),
|
||||
string_literal("pointer_cast"),
|
||||
string_literal("pointer_from_int"),
|
||||
string_literal("select"),
|
||||
@ -1241,6 +1245,7 @@ fn Value* parse_aggregate_initialization(Module* module, Scope* scope, ValueBuil
|
||||
}
|
||||
|
||||
auto field_index = field_count;
|
||||
auto checkpoint = get_checkpoint(module);
|
||||
if (consume_character_if_match(module, '.'))
|
||||
{
|
||||
auto name = parse_identifier(module);
|
||||
@ -1530,6 +1535,37 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
|
||||
{
|
||||
trap();
|
||||
} break;
|
||||
case ValueIntrinsic::min:
|
||||
case ValueIntrinsic::max:
|
||||
{
|
||||
skip_space(module);
|
||||
expect_character(module, left_parenthesis);
|
||||
skip_space(module);
|
||||
auto left = parse_value(module, scope, {});
|
||||
skip_space(module);
|
||||
expect_character(module, ',');
|
||||
skip_space(module);
|
||||
auto right = parse_value(module, scope, {});
|
||||
skip_space(module);
|
||||
expect_character(module, right_parenthesis);
|
||||
|
||||
BinaryId binary_id;
|
||||
switch (intrinsic)
|
||||
{
|
||||
case ValueIntrinsic::max: binary_id = BinaryId::max; break;
|
||||
case ValueIntrinsic::min: binary_id = BinaryId::min; break;
|
||||
default: unreachable();
|
||||
}
|
||||
|
||||
*result = {
|
||||
.binary = {
|
||||
.left = left,
|
||||
.right = right,
|
||||
.id = binary_id,
|
||||
},
|
||||
.id = ValueId::binary,
|
||||
};
|
||||
} break;
|
||||
case ValueIntrinsic::count: unreachable();
|
||||
}
|
||||
} break;
|
||||
@ -1540,7 +1576,26 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
|
||||
|
||||
skip_space(module);
|
||||
|
||||
if (module->content[module->offset] == '.')
|
||||
auto checkpoint = get_checkpoint(module);
|
||||
bool is_aggregate_initialization = false;
|
||||
if (consume_character_if_match(module, '.'))
|
||||
{
|
||||
auto identifier = parse_identifier(module);
|
||||
|
||||
skip_space(module);
|
||||
is_aggregate_initialization = consume_character_if_match(module, '=');
|
||||
if (!is_aggregate_initialization)
|
||||
{
|
||||
if (!consume_character_if_match(module, ','))
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_checkpoint(module, checkpoint);
|
||||
|
||||
if (is_aggregate_initialization)
|
||||
{
|
||||
result = parse_aggregate_initialization(module, scope, builder, right_bracket);
|
||||
}
|
||||
@ -2746,6 +2801,75 @@ fn String parse_name(Module* module)
|
||||
return result;
|
||||
}
|
||||
|
||||
fn bool type_function_base_compare(TypeFunctionBase& a, TypeFunctionBase& b)
|
||||
{
|
||||
auto same_return_type = a.semantic_return_type == b.semantic_return_type;
|
||||
auto same_calling_convention = a.calling_convention == b.calling_convention;
|
||||
auto same_is_variable_arguments = a.is_variable_arguments == b.is_variable_arguments;
|
||||
|
||||
auto same_argument_length = a.semantic_argument_types.length == b.semantic_argument_types.length;
|
||||
auto same_argument_types = same_argument_length;
|
||||
|
||||
if (same_argument_length)
|
||||
{
|
||||
for (u64 i = 0; i < a.semantic_argument_types.length; i += 1)
|
||||
{
|
||||
auto a_type = a.semantic_argument_types[i];
|
||||
auto b_type = b.semantic_argument_types[i];
|
||||
|
||||
auto is_same_argument_type = a_type == b_type;
|
||||
|
||||
same_argument_types = same_argument_types && is_same_argument_type;
|
||||
}
|
||||
}
|
||||
|
||||
return same_return_type && same_calling_convention && same_is_variable_arguments && same_argument_types;
|
||||
}
|
||||
|
||||
fn Type* get_function_type(Module* module, TypeFunctionBase base)
|
||||
{
|
||||
Type* last_function_type = module->first_function_type;
|
||||
|
||||
while (last_function_type)
|
||||
{
|
||||
assert(last_function_type->id == TypeId::function);
|
||||
if (type_function_base_compare(base, last_function_type->function.base))
|
||||
{
|
||||
return last_function_type;
|
||||
}
|
||||
|
||||
auto next = last_function_type->function.next;
|
||||
if (!next)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
last_function_type = next;
|
||||
}
|
||||
|
||||
auto result = type_allocate_init(module, Type{
|
||||
.function = {
|
||||
.base = base,
|
||||
},
|
||||
.id = TypeId::function,
|
||||
.name = string_literal(""),
|
||||
.scope = &module->scope,
|
||||
});
|
||||
|
||||
if (last_function_type)
|
||||
{
|
||||
assert(module->first_function_type);
|
||||
last_function_type->function.next = result;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(!module->first_function_type);
|
||||
module->first_function_type = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void parse(Module* module)
|
||||
{
|
||||
auto scope = &module->scope;
|
||||
@ -2823,16 +2947,23 @@ void parse(Module* module)
|
||||
|
||||
auto global_name = parse_identifier(module);
|
||||
|
||||
Global* global_forward_declaration = 0;
|
||||
Global* last_global = module->first_global;
|
||||
while (last_global)
|
||||
{
|
||||
if (global_name.equal(last_global->variable.name))
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
global_forward_declaration = last_global;
|
||||
if (last_global->variable.storage->id != ValueId::forward_declared_function)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
if (last_global->linkage == Linkage::external)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
if (!last_global->next)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2840,14 +2971,14 @@ void parse(Module* module)
|
||||
}
|
||||
|
||||
Type* type_it = module->scope.types.first;
|
||||
Type* forward_declaration = 0;
|
||||
Type* type_forward_declaration = 0;
|
||||
while (type_it)
|
||||
{
|
||||
if (global_name.equal(type_it->name))
|
||||
{
|
||||
if (type_it->id == TypeId::forward_declaration)
|
||||
{
|
||||
forward_declaration = type_it;
|
||||
type_forward_declaration = type_it;
|
||||
break;
|
||||
}
|
||||
else
|
||||
@ -2924,6 +3055,12 @@ void parse(Module* module)
|
||||
}
|
||||
|
||||
auto global_keyword = (GlobalKeyword)i;
|
||||
|
||||
if (global_forward_declaration && global_keyword != GlobalKeyword::function)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
switch (global_keyword)
|
||||
{
|
||||
case GlobalKeyword::bits:
|
||||
@ -3292,37 +3429,53 @@ void parse(Module* module)
|
||||
|
||||
auto is_declaration = consume_character_if_match(module, ';');
|
||||
|
||||
auto function_type = type_allocate_init(module, {
|
||||
.function = {
|
||||
.semantic_return_type = return_type,
|
||||
.semantic_argument_types = argument_types,
|
||||
.calling_convention = calling_convention,
|
||||
.is_variable_arguments = is_variable_arguments,
|
||||
},
|
||||
.id = TypeId::function,
|
||||
.name = string_literal(""),
|
||||
.scope = &module->scope,
|
||||
auto function_type = get_function_type(module, {
|
||||
.semantic_return_type = return_type,
|
||||
.semantic_argument_types = argument_types,
|
||||
.calling_convention = calling_convention,
|
||||
.is_variable_arguments = is_variable_arguments,
|
||||
});
|
||||
|
||||
auto storage = new_value(module);
|
||||
*storage = {
|
||||
.type = get_pointer_type(module, function_type),
|
||||
.id = ValueId::external_function,
|
||||
// TODO? .kind = ValueKind::left,
|
||||
};
|
||||
auto global = new_global(module);
|
||||
*global = {
|
||||
.variable = {
|
||||
.storage = storage,
|
||||
.initial_value = 0,
|
||||
.type = function_type,
|
||||
.scope = scope,
|
||||
.name = global_name,
|
||||
.line = global_line,
|
||||
.column = global_column,
|
||||
},
|
||||
.linkage = (is_export | is_extern) ? Linkage::external : Linkage::internal,
|
||||
};
|
||||
auto pointer_to_function_type = get_pointer_type(module, function_type);
|
||||
|
||||
Global* global = 0;
|
||||
if (global_forward_declaration)
|
||||
{
|
||||
global = global_forward_declaration;
|
||||
if (global_forward_declaration->variable.type != function_type)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
assert(global_forward_declaration->variable.storage->type == pointer_to_function_type);
|
||||
|
||||
global->variable.name = global_name;
|
||||
global->variable.line = global_line;
|
||||
global->variable.column = global_column;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto storage = new_value(module);
|
||||
*storage = {
|
||||
.type = pointer_to_function_type,
|
||||
.id = ValueId::forward_declared_function,
|
||||
// TODO? .kind = ValueKind::left,
|
||||
};
|
||||
|
||||
global = new_global(module);
|
||||
*global = {
|
||||
.variable = {
|
||||
.storage = storage,
|
||||
.initial_value = 0,
|
||||
.type = function_type,
|
||||
.scope = scope,
|
||||
.name = global_name,
|
||||
.line = global_line,
|
||||
.column = global_column,
|
||||
},
|
||||
.linkage = (is_export | is_extern) ? Linkage::external : Linkage::internal,
|
||||
};
|
||||
}
|
||||
|
||||
if (!is_declaration)
|
||||
{
|
||||
@ -3340,7 +3493,7 @@ void parse(Module* module)
|
||||
.storage = 0,
|
||||
.initial_value = 0,
|
||||
.type = type,
|
||||
.scope = &storage->function.scope,
|
||||
.scope = &global->variable.storage->function.scope,
|
||||
.name = name,
|
||||
.line = line,
|
||||
.column = 0,
|
||||
@ -3349,7 +3502,7 @@ void parse(Module* module)
|
||||
};
|
||||
}
|
||||
|
||||
storage->function = {
|
||||
global->variable.storage->function = {
|
||||
.arguments = arguments,
|
||||
.scope = {
|
||||
.parent = scope,
|
||||
@ -3360,9 +3513,9 @@ void parse(Module* module)
|
||||
.block = 0,
|
||||
.attributes = function_attributes,
|
||||
};
|
||||
storage->id = ValueId::function;
|
||||
global->variable.storage->id = ValueId::function;
|
||||
|
||||
storage->function.block = parse_block(module, &storage->function.scope);
|
||||
global->variable.storage->function.block = parse_block(module, &global->variable.storage->function.scope);
|
||||
module->current_function = 0;
|
||||
}
|
||||
} break;
|
||||
@ -3532,9 +3685,9 @@ void parse(Module* module)
|
||||
skip_space(module);
|
||||
|
||||
Type* struct_type;
|
||||
if (forward_declaration)
|
||||
if (type_forward_declaration)
|
||||
{
|
||||
struct_type = forward_declaration;
|
||||
struct_type = type_forward_declaration;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3646,9 +3799,9 @@ void parse(Module* module)
|
||||
expect_character(module, left_brace);
|
||||
|
||||
Type* union_type;
|
||||
if (forward_declaration)
|
||||
if (type_forward_declaration)
|
||||
{
|
||||
union_type = forward_declaration;
|
||||
union_type = type_forward_declaration;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
18
tests/bool_pair.bbb
Normal file
18
tests/bool_pair.bbb
Normal file
@ -0,0 +1,18 @@
|
||||
BoolPair = struct
|
||||
{
|
||||
a: u1,
|
||||
b: u1,
|
||||
}
|
||||
|
||||
bool_pair = fn () BoolPair
|
||||
{
|
||||
return { .a = 0, .b = 1 };
|
||||
}
|
||||
|
||||
[export] main = fn [cc(c)] () s32
|
||||
{
|
||||
>result = bool_pair();
|
||||
if (result.a) #trap();
|
||||
if (!result.b) #trap();
|
||||
return 0;
|
||||
}
|
38
tests/enum_debug_info.bbb
Normal file
38
tests/enum_debug_info.bbb
Normal file
@ -0,0 +1,38 @@
|
||||
TypeId = enum
|
||||
{
|
||||
void,
|
||||
noreturn,
|
||||
forward_declaration,
|
||||
integer,
|
||||
function,
|
||||
pointer,
|
||||
array,
|
||||
enum,
|
||||
struct,
|
||||
bits,
|
||||
alias,
|
||||
union,
|
||||
unresolved,
|
||||
vector,
|
||||
floating_point,
|
||||
enum_array,
|
||||
opaque,
|
||||
}
|
||||
|
||||
Type = struct
|
||||
{
|
||||
arr: [5]u32,
|
||||
id: TypeId,
|
||||
a: [2]u64,
|
||||
b: u64,
|
||||
}
|
||||
|
||||
[export] main = fn [cc(c)] () s32
|
||||
{
|
||||
>t: Type = {
|
||||
.id = .integer,
|
||||
zero,
|
||||
};
|
||||
t.arr[0] = 1;
|
||||
return 0;
|
||||
}
|
10
tests/min_max.bbb
Normal file
10
tests/min_max.bbb
Normal file
@ -0,0 +1,10 @@
|
||||
[export] main = fn [cc(c)] () s32
|
||||
{
|
||||
>a: u32 = 1;
|
||||
>b: u32 = 2;
|
||||
>min = #min(a, b);
|
||||
>max = #max(a, b);
|
||||
if (min != a) #trap();
|
||||
if (max != b) #trap();
|
||||
return 0;
|
||||
}
|
22
tests/return_array.bbb
Normal file
22
tests/return_array.bbb
Normal file
@ -0,0 +1,22 @@
|
||||
SomeEnum = enum
|
||||
{
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
d,
|
||||
e,
|
||||
f,
|
||||
}
|
||||
|
||||
foo = fn () [2]SomeEnum
|
||||
{
|
||||
return [ .f, .e ];
|
||||
}
|
||||
|
||||
[export] main = fn [cc(c)] () s32
|
||||
{
|
||||
>result = foo();
|
||||
if (result[0] != .f) #trap();
|
||||
if (result[1] != .e) #trap();
|
||||
return 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user