wip
This commit is contained in:
parent
9b1c5ce7b0
commit
1395f1926e
515
src/compiler.bbb
515
src/compiler.bbb
@ -97,6 +97,19 @@ align_forward = fn (value: u64, alignment: u64) u64
|
||||
return result;
|
||||
}
|
||||
|
||||
next_power_of_two = fn (n: u64) u64
|
||||
{
|
||||
n -= 1;
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
n |= n >> 32;
|
||||
n += 1;
|
||||
return n;
|
||||
}
|
||||
|
||||
string_no_match = #integer_max(u64);
|
||||
|
||||
c_string_length = fn (c_string: &u8) u64
|
||||
@ -551,10 +564,14 @@ LLVMTargetMachineOptions = opaque;
|
||||
|
||||
LLVMIntrinsicIndex = enum u32
|
||||
{
|
||||
trap,
|
||||
va_start,
|
||||
va_end,
|
||||
va_copy,
|
||||
"llvm.smax",
|
||||
"llvm.smin",
|
||||
"llvm.trap",
|
||||
"llvm.umax",
|
||||
"llvm.umin",
|
||||
"llvm.va_start",
|
||||
"llvm.va_end",
|
||||
"llvm.va_copy",
|
||||
}
|
||||
|
||||
CompilerCommand = enum
|
||||
@ -658,6 +675,7 @@ Type = struct;
|
||||
Value = struct;
|
||||
Local = struct;
|
||||
Block = struct;
|
||||
Module = struct;
|
||||
|
||||
ScopeKind = enum
|
||||
{
|
||||
@ -746,7 +764,7 @@ TypeId = enum
|
||||
|
||||
TypeInteger = struct
|
||||
{
|
||||
bit_count: u32,
|
||||
bit_count: u64,
|
||||
signed: u1,
|
||||
}
|
||||
|
||||
@ -767,9 +785,67 @@ AbiRegisterCount = union
|
||||
system_v: AbiRegisterCountSystemV,
|
||||
};
|
||||
|
||||
AbiInformationPadding = union
|
||||
{
|
||||
type: &Type,
|
||||
unpadded_coerce_and_expand_type: &Type,
|
||||
}
|
||||
|
||||
AbiInformationDirectAttributes = struct
|
||||
{
|
||||
offset: u32,
|
||||
alignment: u32,
|
||||
}
|
||||
|
||||
AbiInformationIndirectAttributes = struct
|
||||
{
|
||||
alignment: u32,
|
||||
address_space: u32,
|
||||
}
|
||||
|
||||
AbiInformationAttributes = union
|
||||
{
|
||||
direct: AbiInformationDirectAttributes,
|
||||
indirect: AbiInformationIndirectAttributes,
|
||||
alloca_field_index: u32,
|
||||
}
|
||||
|
||||
AbiKind = enum
|
||||
{
|
||||
ignore,
|
||||
direct,
|
||||
extend,
|
||||
indirect,
|
||||
indirect_aliased,
|
||||
expand,
|
||||
coerce_and_expand,
|
||||
in_alloca,
|
||||
}
|
||||
|
||||
AbiFlags = struct
|
||||
{
|
||||
kind: AbiKind,
|
||||
padding_in_reg: u1,
|
||||
in_alloca_sret: u1,
|
||||
in_alloca_indirect: u1,
|
||||
in_alloca_by_value: u1,
|
||||
in_alloca_realign: u1,
|
||||
sret_after_this: u1,
|
||||
in_reg: u1,
|
||||
can_be_flattened: u1,
|
||||
sign_extension: u1,
|
||||
}
|
||||
|
||||
AbiInformation = struct
|
||||
{
|
||||
foo: u32,
|
||||
semantic_type: &Type,
|
||||
coerce_to_type: &Type,
|
||||
padding: AbiInformationPadding,
|
||||
padding_argument_index: u16,
|
||||
attributes: AbiInformationAttributes,
|
||||
flags: AbiFlags,
|
||||
abi_start: u16,
|
||||
abi_count: u16,
|
||||
}
|
||||
|
||||
TypeFunction = struct
|
||||
@ -802,12 +878,88 @@ Type = struct
|
||||
scope: &Scope,
|
||||
}
|
||||
|
||||
align_bit_count = fn (bit_count: u64) u64
|
||||
{
|
||||
>aligned_bit_count = #max(next_power_of_two(bit_count), 8);
|
||||
assert(aligned_bit_count % 8 == 0);
|
||||
return aligned_bit_count;
|
||||
}
|
||||
|
||||
aligned_byte_count_from_bit_count = fn (bit_count: u64) u64
|
||||
{
|
||||
>aligned_bit_count = align_bit_count(bit_count);
|
||||
return aligned_bit_count / 8;
|
||||
}
|
||||
|
||||
get_byte_size = fn (type: &Type) u64
|
||||
{
|
||||
switch (type.id)
|
||||
{
|
||||
.integer =>
|
||||
{
|
||||
>byte_count = aligned_byte_count_from_bit_count(type.content.integer.bit_count);
|
||||
assert(byte_count == 1 or byte_count == 2 or byte_count == 4 or byte_count == 8 or byte_count == 16);
|
||||
return byte_count;
|
||||
},
|
||||
else =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
is_integral_or_enumeration_type = fn (type: &Type) u1
|
||||
{
|
||||
switch (type.id)
|
||||
{
|
||||
.integer, .bits, .enum =>
|
||||
{
|
||||
return 1;
|
||||
},
|
||||
.array, .struct =>
|
||||
{
|
||||
return 0;
|
||||
},
|
||||
else =>
|
||||
{
|
||||
unreachable;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
is_promotable_integer_type_for_abi = fn (type: &Type) u1
|
||||
{
|
||||
switch (type.id)
|
||||
{
|
||||
.integer =>
|
||||
{
|
||||
return type.content.integer.bit_count < 32;
|
||||
},
|
||||
else =>
|
||||
{
|
||||
#trap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resolve_type_in_place_abi = fn (module: &Module, type: &Type) void;
|
||||
resolve_type_in_place_memory = fn (module: &Module, type: &Type) void;
|
||||
resolve_type_in_place_debug = fn (module: &Module, type: &Type) void;
|
||||
|
||||
resolve_type_in_place = fn (module: &Module, type: &Type) void
|
||||
{
|
||||
resolve_type_in_place_abi(module, type);
|
||||
resolve_type_in_place_memory(module, type);
|
||||
resolve_type_in_place_debug(module, type);
|
||||
}
|
||||
|
||||
ValueId = enum
|
||||
{
|
||||
infer_or_ignore,
|
||||
external_function,
|
||||
function,
|
||||
constant_integer,
|
||||
global,
|
||||
}
|
||||
|
||||
ValueConstantInteger = struct
|
||||
@ -1223,7 +1375,7 @@ integer_type = fn (module: &Module, integer: TypeInteger) &Type
|
||||
{
|
||||
assert(integer.bit_count != 0);
|
||||
assert(integer.bit_count <= 64);
|
||||
>index = #select(integer.bit_count == 128, i128_offset + #extend(integer.signed), #extend(integer.bit_count) - 1 + 64 * #extend(integer.signed));
|
||||
>index = #select(integer.bit_count == 128, i128_offset + #extend(integer.signed), integer.bit_count - 1 + 64 * #extend(integer.signed));
|
||||
>result = module.scope.types.first + index;
|
||||
assert(result.id == .integer);
|
||||
assert(result.content.integer.bit_count == integer.bit_count);
|
||||
@ -1685,7 +1837,7 @@ parse_type = fn (module: &Module, scope: &Scope) &Type
|
||||
}
|
||||
}
|
||||
|
||||
>result = integer_type(module, { .bit_count = #truncate(bit_count), .signed = is_signed });
|
||||
>result = integer_type(module, { .bit_count = bit_count, .signed = is_signed });
|
||||
return result;
|
||||
}
|
||||
else
|
||||
@ -2784,6 +2936,278 @@ parse = fn (module: &Module) void
|
||||
}
|
||||
}
|
||||
|
||||
resolve_alias = fn (module: &Module, type: &Type) &Type
|
||||
{
|
||||
>result: &Type = zero;
|
||||
|
||||
switch (type.id)
|
||||
{
|
||||
.void,
|
||||
.integer,
|
||||
=>
|
||||
{
|
||||
result = type;
|
||||
},
|
||||
else =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
}
|
||||
|
||||
assert(result != zero);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
resolve_type_in_place_abi = fn (module: &Module, type: &Type) void
|
||||
{
|
||||
#trap();
|
||||
}
|
||||
|
||||
resolve_type_in_place_memory = fn (module: &Module, type: &Type) void
|
||||
{
|
||||
#trap();
|
||||
}
|
||||
|
||||
resolve_type_in_place_debug = fn (module: &Module, type: &Type) void
|
||||
{
|
||||
#trap();
|
||||
}
|
||||
|
||||
AbiSystemVClass = enum
|
||||
{
|
||||
none,
|
||||
integer,
|
||||
sse,
|
||||
sse_up,
|
||||
x87,
|
||||
x87_up,
|
||||
complex_x87,
|
||||
memory,
|
||||
}
|
||||
|
||||
AbiSystemVClassifyArgument = struct
|
||||
{
|
||||
base_offset: u64,
|
||||
is_variable_argument: u1,
|
||||
is_register_call: u1,
|
||||
}
|
||||
|
||||
abi_system_v_classify_type = fn (type: &Type, options: AbiSystemVClassifyArgument) [2]AbiSystemVClass
|
||||
{
|
||||
>result: [2]AbiSystemVClass = zero;
|
||||
|
||||
>is_memory = options.base_offset >= 8;
|
||||
>current_index: u64 = #extend(is_memory);
|
||||
>not_current_index: u64 = #extend(!is_memory);
|
||||
assert(current_index != not_current_index);
|
||||
result[current_index] = .memory;
|
||||
|
||||
switch (type.id)
|
||||
{
|
||||
.void, .noreturn =>
|
||||
{
|
||||
result[current_index] = .none;
|
||||
},
|
||||
.integer =>
|
||||
{
|
||||
>bit_count = type.content.integer.bit_count;
|
||||
if (bit_count <= 64)
|
||||
{
|
||||
result[current_index] = .integer;
|
||||
}
|
||||
else if (bit_count == 128)
|
||||
{
|
||||
#trap();
|
||||
}
|
||||
else
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
},
|
||||
else =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
contains_no_user_data = fn (type: &Type, start: u64, end: u64) u1
|
||||
{
|
||||
>byte_size = get_byte_size(type);
|
||||
>result = byte_size <= start;
|
||||
|
||||
if (!result)
|
||||
{
|
||||
switch (type.id)
|
||||
{
|
||||
.struct =>
|
||||
{
|
||||
result = 1;
|
||||
#trap();
|
||||
},
|
||||
.array, .enum_array =>
|
||||
{
|
||||
result = 1;
|
||||
#trap();
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
abi_system_v_get_integer_type_at_offset = fn (module: &Module, type: &Type, offset: u64, source_type: &Type, source_offset: u64) &Type
|
||||
{
|
||||
switch (type.id)
|
||||
{
|
||||
.integer =>
|
||||
{
|
||||
if (offset == 0)
|
||||
{
|
||||
>bit_count = type.content.integer.bit_count;
|
||||
>start = source_offset + get_byte_size(type);
|
||||
>end = source_offset + 8;
|
||||
|
||||
if (bit_count == 64)
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
if (bit_count == 32 or bit_count == 16 or bit_count == 8)
|
||||
{
|
||||
if (contains_no_user_data(source_type, start, end))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
>source_size = get_byte_size(source_type);
|
||||
assert(source_size != source_offset);
|
||||
>byte_count = source_size - source_offset;
|
||||
>bit_count = #select(byte_count > 8, 64, byte_count * 8);
|
||||
>result = integer_type(module, { .bit_count = bit_count, .signed = 0 });
|
||||
return result;
|
||||
}
|
||||
|
||||
AbiSystemVDirect = struct
|
||||
{
|
||||
semantic_type: &Type,
|
||||
type: &Type,
|
||||
padding: &Type,
|
||||
offset: u32,
|
||||
alignment: u32,
|
||||
cannot_be_flattened: u1,
|
||||
}
|
||||
|
||||
abi_system_v_get_direct = fn (module: &Module, direct: AbiSystemVDirect) AbiInformation
|
||||
{
|
||||
>result: AbiInformation = {
|
||||
.semantic_type = direct.semantic_type,
|
||||
.flags = {
|
||||
.kind = .direct,
|
||||
zero,
|
||||
},
|
||||
zero,
|
||||
};
|
||||
|
||||
resolve_type_in_place(module, direct.semantic_type);
|
||||
resolve_type_in_place(module, direct.type);
|
||||
|
||||
if (direct.padding)
|
||||
{
|
||||
resolve_type_in_place(module, direct.padding);
|
||||
}
|
||||
|
||||
#trap();
|
||||
}
|
||||
|
||||
abi_system_v_classify_return_type = fn (module: &Module, semantic_return_type: &Type) AbiInformation
|
||||
{
|
||||
>classes = abi_system_v_classify_type(semantic_return_type, zero);
|
||||
assert(classes[1] != .memory or classes[0] == .memory);
|
||||
assert(classes[1] != .sse_up or classes[0] == .sse);
|
||||
|
||||
>low_type: &Type = zero;
|
||||
|
||||
switch (classes[0])
|
||||
{
|
||||
.none =>
|
||||
{
|
||||
if (classes[1] == .none)
|
||||
{
|
||||
#trap();
|
||||
}
|
||||
else
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
},
|
||||
.integer =>
|
||||
{
|
||||
low_type = abi_system_v_get_integer_type_at_offset(module, semantic_return_type, 0, semantic_return_type, 0);
|
||||
|
||||
if (classes[1] == .none and low_type.id == .integer)
|
||||
{
|
||||
if (is_integral_or_enumeration_type(semantic_return_type) and? is_promotable_integer_type_for_abi(semantic_return_type))
|
||||
{
|
||||
#trap();
|
||||
}
|
||||
}
|
||||
},
|
||||
else =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
}
|
||||
|
||||
>high_type: &Type = zero;
|
||||
|
||||
switch (classes[1])
|
||||
{
|
||||
.none => {},
|
||||
.integer =>
|
||||
{
|
||||
>high_offset: u64 = 8;
|
||||
high_type = abi_system_v_get_integer_type_at_offset(module, semantic_return_type, high_offset, semantic_return_type, high_offset);
|
||||
|
||||
if (classes[0] == .none)
|
||||
{
|
||||
#trap();
|
||||
}
|
||||
},
|
||||
else =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
}
|
||||
|
||||
if (high_type)
|
||||
{
|
||||
#trap();
|
||||
}
|
||||
|
||||
>result = abi_system_v_get_direct(module, {
|
||||
.semantic_type = semantic_return_type,
|
||||
.type = low_type,
|
||||
zero,
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ResolvedCallingConvention = enum
|
||||
{
|
||||
system_v,
|
||||
win64,
|
||||
}
|
||||
|
||||
emit = fn (module: &Module) void
|
||||
{
|
||||
assert(!module.current_function);
|
||||
@ -2889,6 +3313,79 @@ emit = fn (module: &Module) void
|
||||
>name = #enum_name(e);
|
||||
module.llvm.intrinsic_table[i] = LLVMLookupIntrinsicID(name.pointer, name.length);
|
||||
}
|
||||
|
||||
>global = module.first_global;
|
||||
|
||||
while (global)
|
||||
{
|
||||
assert(!module.current_function);
|
||||
assert(!module.current_macro_instantiation);
|
||||
assert(!module.current_macro_declaration);
|
||||
|
||||
if (global.emitted)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
>global_pointer_type = global.variable.storage.type;
|
||||
assert(global_pointer_type.id == .pointer);
|
||||
>global_value_type = global_pointer_type.content.pointer.element_type;
|
||||
|
||||
switch (global.variable.storage.id)
|
||||
{
|
||||
.function, .external_function =>
|
||||
{
|
||||
>function_type = &global_value_type.content.function;
|
||||
>semantic_argument_types = function_type.semantic_argument_types;
|
||||
>semantic_return_type = function_type.semantic_return_type;
|
||||
function_type.argument_abis = arena_allocate_slice[AbiInformation](module.arena, semantic_argument_types.length);
|
||||
>llvm_abi_argument_type_buffer: [64]&LLVMType = undefined;
|
||||
|
||||
>resolved_calling_convention: ResolvedCallingConvention = undefined;
|
||||
switch (function_type.calling_convention)
|
||||
{
|
||||
.c =>
|
||||
{
|
||||
// TODO: switch on platform
|
||||
resolved_calling_convention = .system_v;
|
||||
},
|
||||
}
|
||||
|
||||
>is_reg_call = resolved_calling_convention == .system_v and 0; // TODO: regcall calling convention
|
||||
|
||||
switch (resolved_calling_convention)
|
||||
{
|
||||
.system_v =>
|
||||
{
|
||||
function_type.available_registers = {
|
||||
.system_v = {
|
||||
.gpr = #select(is_reg_call, 11, 6),
|
||||
.sse = #select(is_reg_call, 16, 8),
|
||||
},
|
||||
};
|
||||
function_type.return_abi = abi_system_v_classify_return_type(module, resolve_alias(module, semantic_return_type));
|
||||
#trap();
|
||||
},
|
||||
.win64 =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
}
|
||||
},
|
||||
.global =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
else =>
|
||||
{
|
||||
report_error();
|
||||
},
|
||||
}
|
||||
|
||||
global = global.next;
|
||||
}
|
||||
|
||||
#trap();
|
||||
}
|
||||
|
||||
compile = fn (arena: &Arena, options: CompileOptions) void
|
||||
@ -2919,7 +3416,7 @@ compile = fn (arena: &Arena, options: CompileOptions) void
|
||||
type_it.& = {
|
||||
.content = {
|
||||
.integer = {
|
||||
.bit_count = #truncate(bit_count),
|
||||
.bit_count = bit_count,
|
||||
.signed = sign,
|
||||
},
|
||||
},
|
||||
|
@ -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"),
|
||||
@ -1213,6 +1239,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;
|
||||
|
||||
|
689
src/emitter.cpp
689
src/emitter.cpp
File diff suppressed because it is too large
Load Diff
233
src/parser.cpp
233
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,43 @@ 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)
|
||||
{
|
||||
trap();
|
||||
}
|
||||
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 +3483,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 +3492,7 @@ void parse(Module* module)
|
||||
};
|
||||
}
|
||||
|
||||
storage->function = {
|
||||
global->variable.storage->function = {
|
||||
.arguments = arguments,
|
||||
.scope = {
|
||||
.parent = scope,
|
||||
@ -3360,9 +3503,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 +3675,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 +3789,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