wip
This commit is contained in:
parent
9b1c5ce7b0
commit
4ac50bebab
390
src/compiler.bbb
390
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
|
||||
@ -746,7 +763,7 @@ TypeId = enum
|
||||
|
||||
TypeInteger = struct
|
||||
{
|
||||
bit_count: u32,
|
||||
bit_count: u64,
|
||||
signed: u1,
|
||||
}
|
||||
|
||||
@ -802,12 +819,77 @@ 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ValueId = enum
|
||||
{
|
||||
infer_or_ignore,
|
||||
external_function,
|
||||
function,
|
||||
constant_integer,
|
||||
global,
|
||||
}
|
||||
|
||||
ValueConstantInteger = struct
|
||||
@ -1223,7 +1305,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 +1767,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 +2866,225 @@ 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
#trap();
|
||||
}
|
||||
|
||||
ResolvedCallingConvention = enum
|
||||
{
|
||||
system_v,
|
||||
win64,
|
||||
}
|
||||
|
||||
emit = fn (module: &Module) void
|
||||
{
|
||||
assert(!module.current_function);
|
||||
@ -2889,6 +3190,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 +3293,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)
|
||||
|
@ -629,6 +629,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();
|
||||
}
|
||||
}
|
||||
@ -905,6 +910,8 @@ enum class BinaryId
|
||||
logical_or,
|
||||
logical_and_shortcircuit,
|
||||
logical_or_shortcircuit,
|
||||
max,
|
||||
min,
|
||||
};
|
||||
|
||||
struct ValueBinary
|
||||
@ -1078,6 +1085,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 +1172,11 @@ struct LLVMIntrinsicId
|
||||
|
||||
enum class IntrinsicIndex
|
||||
{
|
||||
smax,
|
||||
smin,
|
||||
trap,
|
||||
umax,
|
||||
umin,
|
||||
va_start,
|
||||
va_end,
|
||||
va_copy,
|
||||
@ -1172,7 +1184,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"),
|
||||
|
564
src/emitter.cpp
564
src/emitter.cpp
@ -153,6 +153,7 @@ fn bool is_integral_or_enumeration_type(Type* type)
|
||||
case TypeId::bits:
|
||||
case TypeId::enumerator:
|
||||
return true;
|
||||
case TypeId::array:
|
||||
case TypeId::structure:
|
||||
return false;
|
||||
default: unreachable();
|
||||
@ -667,31 +668,25 @@ fn Type* abi_system_v_get_integer_type_at_offset(Module* module, Type* type, u32
|
||||
{
|
||||
case TypeId::integer:
|
||||
{
|
||||
auto bit_count = type->integer.bit_count;
|
||||
switch (bit_count)
|
||||
if (offset == 0)
|
||||
{
|
||||
case 64: return type;
|
||||
case 32: case 16: case 8:
|
||||
{
|
||||
assert(offset == 0);
|
||||
auto start = source_offset + get_byte_size(type);
|
||||
auto end = source_offset + 8;
|
||||
auto bit_count = type->integer.bit_count;
|
||||
auto start = source_offset + get_byte_size(type);
|
||||
auto end = source_offset + 8;
|
||||
|
||||
if (contains_no_user_data(source_type, start, end))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
{
|
||||
auto original_byte_count = get_byte_size(type);
|
||||
assert(original_byte_count != source_offset);
|
||||
auto byte_count = MIN(original_byte_count - source_offset, 8);
|
||||
auto bit_count = byte_count * 8;
|
||||
|
||||
auto result_type = integer_type(module, { .bit_count = (u32)bit_count, .is_signed = false });
|
||||
return result_type;
|
||||
} break;
|
||||
bool type_contains_no_user_data = contains_no_user_data(source_type, start, end);
|
||||
switch (bit_count)
|
||||
{
|
||||
case 64: return type;
|
||||
case 32: case 16: case 8:
|
||||
{
|
||||
if (type_contains_no_user_data)
|
||||
{
|
||||
return type;
|
||||
}
|
||||
} break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case TypeId::pointer:
|
||||
@ -738,10 +733,18 @@ fn Type* abi_system_v_get_integer_type_at_offset(Module* module, Type* type, u32
|
||||
auto backing_type = type->enumerator.backing_type;
|
||||
return abi_system_v_get_integer_type_at_offset(module, backing_type, offset, source_type == type ? backing_type : source_type, source_offset);
|
||||
} break;
|
||||
case TypeId::array:
|
||||
{
|
||||
auto element_type = type->array.element_type;
|
||||
auto element_size = get_byte_size(element_type);
|
||||
auto element_offset = (offset / element_size) * element_size;
|
||||
return abi_system_v_get_integer_type_at_offset(module, element_type, offset - element_offset, source_type, source_offset);
|
||||
} break;
|
||||
default: unreachable();
|
||||
}
|
||||
|
||||
auto source_size = get_byte_size(source_type);
|
||||
assert(source_size != source_offset);
|
||||
auto byte_count = source_size - source_offset;
|
||||
u32 bit_count = byte_count > 8 ? 64 : byte_count * 8;
|
||||
auto result = integer_type(module, { .bit_count = bit_count, .is_signed = false });
|
||||
@ -1103,7 +1106,7 @@ fn void resolve_type_in_place_debug(Module* module, Type* type)
|
||||
assert(array_element_count);
|
||||
resolve_type_in_place_debug(module, array_element_type);
|
||||
auto bit_alignment = get_byte_alignment(type) * 8;
|
||||
auto array_type = LLVMDIBuilderCreateArrayType(module->llvm.di_builder, array_element_count, bit_alignment, array_element_type->llvm.debug, 0, 0);
|
||||
auto array_type = LLVMDIBuilderCreateArrayType(module->llvm.di_builder, get_bit_size(type), bit_alignment, array_element_type->llvm.debug, 0, 0);
|
||||
result = array_type;
|
||||
} break;
|
||||
case TypeId::enumerator:
|
||||
@ -1119,7 +1122,10 @@ fn void resolve_type_in_place_debug(Module* module, Type* type)
|
||||
field_buffer[i] = enum_field;
|
||||
}
|
||||
|
||||
result = LLVMDIBuilderCreateEnumerationType(module->llvm.di_builder, module->scope.llvm, (char*)type->name.pointer, type->name.length, module->llvm.file, type->enumerator.line, get_bit_size(type), get_byte_alignment(type) * 8, field_buffer, type->enumerator.fields.length, backing_type->llvm.debug);
|
||||
auto debug_aligned_type = align_integer_type(module, backing_type);
|
||||
resolve_type_in_place_debug(module, debug_aligned_type);
|
||||
|
||||
result = LLVMDIBuilderCreateEnumerationType(module->llvm.di_builder, module->scope.llvm, (char*)type->name.pointer, type->name.length, module->llvm.file, type->enumerator.line, get_bit_size(type), get_byte_alignment(type) * 8, field_buffer, type->enumerator.fields.length, debug_aligned_type->llvm.debug);
|
||||
} break;
|
||||
case TypeId::structure:
|
||||
{
|
||||
@ -1136,7 +1142,7 @@ fn void resolve_type_in_place_debug(Module* module, Type* type)
|
||||
auto& field = fields[i];
|
||||
auto field_type = field.type;
|
||||
resolve_type_in_place_debug(module, field_type);
|
||||
auto member_type = LLVMDIBuilderCreateMemberType(module->llvm.di_builder, module->scope.llvm, (char*)field.name.pointer, field.name.length, module->llvm.file, field.line, get_byte_size(field_type) * 8, get_byte_alignment(field_type) * 8, field.offset * 8, flags, field_type->llvm.debug);
|
||||
auto member_type = LLVMDIBuilderCreateMemberType(module->llvm.di_builder, module->scope.llvm, (char*)field.name.pointer, field.name.length, module->llvm.file, field.line, get_bit_size(field_type), get_byte_alignment(field_type) * 8, field.offset * 8, flags, field_type->llvm.debug);
|
||||
llvm_type_buffer[i] = member_type;
|
||||
}
|
||||
|
||||
@ -1762,7 +1768,7 @@ fn AbiInformation abi_system_v_get_indirect_return_result(Type* type)
|
||||
}
|
||||
}
|
||||
|
||||
fn AbiInformation abi_system_classify_return_type(Module* module, Type* semantic_return_type)
|
||||
fn AbiInformation abi_system_v_classify_return_type(Module* module, Type* semantic_return_type)
|
||||
{
|
||||
auto type_classes = abi_system_v_classify_type(semantic_return_type, {});
|
||||
auto low_class = type_classes.r[0];
|
||||
@ -2242,6 +2248,8 @@ fn bool binary_is_boolean(BinaryId id)
|
||||
case BinaryId::bitwise_xor:
|
||||
case BinaryId::shift_left:
|
||||
case BinaryId::shift_right:
|
||||
case BinaryId::max:
|
||||
case BinaryId::min:
|
||||
return false;
|
||||
case BinaryId::compare_equal:
|
||||
case BinaryId::compare_not_equal:
|
||||
@ -2278,8 +2286,8 @@ enum class IndexType
|
||||
|
||||
struct TypeAnalysis
|
||||
{
|
||||
Type* indexing_type;
|
||||
bool must_be_constant;
|
||||
bool is_index;
|
||||
};
|
||||
|
||||
fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnalysis analysis);
|
||||
@ -2692,7 +2700,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal
|
||||
{
|
||||
if (!expected_type)
|
||||
{
|
||||
if (analysis.is_index)
|
||||
if (analysis.indexing_type)
|
||||
{
|
||||
expected_type = uint64(module);
|
||||
}
|
||||
@ -3268,7 +3276,9 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal
|
||||
}
|
||||
auto pointer_element_type = array_like_type->pointer.element_type;
|
||||
|
||||
analyze_type(module, value->array_expression.index, 0, { .must_be_constant = analysis.must_be_constant, .is_index = true });
|
||||
auto indexing_type = pointer_element_type->id == TypeId::enum_array ? pointer_element_type->enum_array.enum_type : uint64(module);
|
||||
|
||||
analyze_type(module, value->array_expression.index, 0, { .indexing_type = indexing_type, .must_be_constant = analysis.must_be_constant });
|
||||
|
||||
Type* element_type = 0;
|
||||
switch (pointer_element_type->id)
|
||||
@ -3311,6 +3321,11 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal
|
||||
} break;
|
||||
case ValueId::enum_literal:
|
||||
{
|
||||
if (!expected_type)
|
||||
{
|
||||
expected_type = analysis.indexing_type;
|
||||
}
|
||||
|
||||
if (!expected_type)
|
||||
{
|
||||
report_error();
|
||||
@ -3572,12 +3587,31 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal
|
||||
// Right now 0, 1, 2, 3 => constant values, rest zeroed is constant because `declaration_index == initialization_index`
|
||||
// With constant initialization values 2, 3, 4 and rest zeroed, the aggregate initialization because `declaration_index != initialization_index`, that is, the first initialization index (0) does not match the declaration index (2). The same case can be applied for cases (1, 3) and (2, 4)
|
||||
|
||||
switch (resolved_type->id)
|
||||
Type* aggregate_type = 0;
|
||||
switch (value->kind)
|
||||
{
|
||||
case ValueKind::left:
|
||||
{
|
||||
if (resolved_type->id != TypeId::pointer)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
aggregate_type = resolved_type->pointer.element_type;
|
||||
} break;
|
||||
case ValueKind::right:
|
||||
{
|
||||
aggregate_type = resolved_type;
|
||||
} break;
|
||||
default:
|
||||
}
|
||||
|
||||
switch (aggregate_type->id)
|
||||
{
|
||||
case TypeId::structure:
|
||||
{
|
||||
bool is_ordered = true;
|
||||
auto fields = resolved_type->structure.fields;
|
||||
auto fields = aggregate_type->structure.fields;
|
||||
assert(fields.length <= 64);
|
||||
|
||||
auto same_values_as_field = fields.length == elements.length;
|
||||
@ -3637,7 +3671,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal
|
||||
} break;
|
||||
case TypeId::bits:
|
||||
{
|
||||
auto fields = resolved_type->bits.fields;
|
||||
auto fields = aggregate_type->bits.fields;
|
||||
assert(fields.length <= 64);
|
||||
|
||||
auto same_values_as_field = fields.length == elements.length;
|
||||
@ -3704,7 +3738,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal
|
||||
auto initialization_name = elements[0].name;
|
||||
|
||||
u64 i;
|
||||
auto fields = resolved_type->union_type.fields;
|
||||
auto fields = aggregate_type->union_type.fields;
|
||||
for (i = 0; i < fields.length; i += 1)
|
||||
{
|
||||
auto& field = fields[i];
|
||||
@ -3725,8 +3759,8 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal
|
||||
case TypeId::enum_array:
|
||||
{
|
||||
bool is_ordered = true;
|
||||
auto enum_type = resolved_type->enum_array.enum_type;
|
||||
auto element_type = resolved_type->enum_array.element_type;
|
||||
auto enum_type = aggregate_type->enum_array.enum_type;
|
||||
auto element_type = aggregate_type->enum_array.element_type;
|
||||
assert(enum_type->id == TypeId::enumerator);
|
||||
auto fields = enum_type->enumerator.fields;
|
||||
|
||||
@ -4379,10 +4413,20 @@ fn ValueTypePair enter_struct_pointer_for_coerced_access(Module* module, LLVMVal
|
||||
|
||||
if (!(first_field_size < destination_size && first_field_size < source_size))
|
||||
{
|
||||
trap();
|
||||
auto gep = LLVMBuildStructGEP2(module->llvm.builder, source_type->llvm.abi, source_value, 0, "coerce.dive");
|
||||
if (first_field_type->id == TypeId::structure)
|
||||
{
|
||||
trap();
|
||||
}
|
||||
else
|
||||
{
|
||||
return { gep, first_field_type };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return { source_value, source_type };
|
||||
}
|
||||
|
||||
return { source_value, source_type };
|
||||
}
|
||||
|
||||
fn LLVMValueRef coerce_integer_or_pointer_to_integer_or_pointer(Module* module, LLVMValueRef source, Type* source_type, Type* destination_type)
|
||||
@ -4418,7 +4462,12 @@ fn LLVMValueRef create_coerced_load(Module* module, LLVMValueRef source, Type* s
|
||||
|
||||
if (type_is_integer_backing(source_type) && type_is_integer_backing(destination_type))
|
||||
{
|
||||
trap();
|
||||
auto load = create_load(module, {
|
||||
.type = source_type,
|
||||
.pointer = source,
|
||||
});
|
||||
auto result = coerce_integer_or_pointer_to_integer_or_pointer(module, load, source_type, destination_type);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -4749,6 +4798,42 @@ fn SliceEmitResult emit_string_literal(Module* module, Value* value)
|
||||
}
|
||||
}
|
||||
|
||||
fn void invalidate_analysis(Module* module, Value* value)
|
||||
{
|
||||
switch (value->id)
|
||||
{
|
||||
case ValueId::variable_reference:
|
||||
case ValueId::constant_integer:
|
||||
break;
|
||||
case ValueId::aggregate_initialization:
|
||||
{
|
||||
auto elements = value->aggregate_initialization.elements;
|
||||
for (auto& element : elements)
|
||||
{
|
||||
invalidate_analysis(module, element.value);
|
||||
}
|
||||
} break;
|
||||
case ValueId::field_access:
|
||||
{
|
||||
invalidate_analysis(module, value->field_access.aggregate);
|
||||
} break;
|
||||
default: trap();
|
||||
}
|
||||
|
||||
value->type = 0;
|
||||
}
|
||||
|
||||
fn void reanalyze_type_as_left_value(Module* module, Value* value)
|
||||
{
|
||||
assert(value->type);
|
||||
assert(value->kind == ValueKind::right);
|
||||
auto original_type = value->type;
|
||||
invalidate_analysis(module, value);
|
||||
value->kind = ValueKind::left;
|
||||
auto expected_type = value->id == ValueId::aggregate_initialization ? get_pointer_type(module, original_type) : 0;
|
||||
analyze_type(module, value, expected_type, {});
|
||||
}
|
||||
|
||||
fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, Type* left_type)
|
||||
{
|
||||
switch (value->id)
|
||||
@ -4972,17 +5057,27 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm,
|
||||
}
|
||||
else
|
||||
{
|
||||
if (src->kind == ValueKind::right)
|
||||
if (src->kind == ValueKind::right && !src->is_constant())
|
||||
{
|
||||
if (src->id == ValueId::variable_reference)
|
||||
if (!type_is_slice(src->type))
|
||||
{
|
||||
src->type = 0;
|
||||
src->kind = ValueKind::left;
|
||||
analyze_type(module, src, 0, {});
|
||||
switch (src->id)
|
||||
{
|
||||
case ValueId::aggregate_initialization:
|
||||
case ValueId::variable_reference:
|
||||
case ValueId::field_access:
|
||||
{
|
||||
reanalyze_type_as_left_value(module, src);
|
||||
} break;
|
||||
default:
|
||||
{
|
||||
trap();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emit_value(module, semantic_call_argument_value, TypeKind::memory, false);
|
||||
emit_value(module, src, TypeKind::memory, false);
|
||||
auto destination_size = get_byte_size(coerce_to_type);
|
||||
auto source_size = get_byte_size(argument_abi.semantic_type);
|
||||
auto alignment = get_byte_alignment(argument_abi.semantic_type);
|
||||
@ -5083,6 +5178,16 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm,
|
||||
trap();
|
||||
}
|
||||
} break;
|
||||
case ValueId::zero:
|
||||
{
|
||||
for (u32 i = 0; i < coerce_fields.length; i += 1)
|
||||
{
|
||||
auto& field = coerce_fields[i];
|
||||
auto field_type = field.type;
|
||||
llvm_abi_argument_value_buffer[abi_argument_count] = LLVMConstNull(field_type->llvm.abi);
|
||||
abi_argument_count += 1;
|
||||
}
|
||||
} break;
|
||||
default: trap();
|
||||
}
|
||||
}
|
||||
@ -5124,9 +5229,7 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm,
|
||||
assert(src->type->id == TypeId::structure);
|
||||
assert(src->id == ValueId::variable_reference);
|
||||
assert(src->kind == ValueKind::right);
|
||||
src->type = 0;
|
||||
src->kind = ValueKind::left;
|
||||
analyze_type(module, src, pointer_type, {});
|
||||
reanalyze_type_as_left_value(module, src);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -5222,9 +5325,8 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm,
|
||||
{
|
||||
case ValueId::variable_reference:
|
||||
{
|
||||
semantic_call_argument_value->type = 0;
|
||||
semantic_call_argument_value->kind = ValueKind::left;
|
||||
analyze_value(module, semantic_call_argument_value, pointer_type, TypeKind::memory, false);
|
||||
reanalyze_type_as_left_value(module, semantic_call_argument_value);
|
||||
emit_value(module, semantic_call_argument_value, TypeKind::memory, false);
|
||||
llvm_abi_argument_value_buffer[abi_argument_count] = semantic_call_argument_value->llvm;
|
||||
abi_argument_count += 1;
|
||||
} break;
|
||||
@ -5342,19 +5444,42 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm,
|
||||
|
||||
auto destination_type = return_abi.semantic_type;
|
||||
|
||||
assert(return_abi.semantic_type->id == TypeId::structure);
|
||||
if (return_abi.semantic_type->structure.fields.length > 0)
|
||||
|
||||
auto source_value = llvm_call;
|
||||
auto source_type = raw_function_type->function.abi_return_type;
|
||||
auto destination_size = get_byte_size(destination_type);
|
||||
auto left_destination_size = destination_size - return_abi.attributes.direct.offset;
|
||||
auto is_destination_volatile = false;
|
||||
|
||||
switch (return_abi.semantic_type->id)
|
||||
{
|
||||
auto source_value = llvm_call;
|
||||
auto source_type = raw_function_type->function.abi_return_type;
|
||||
auto destination_size = get_byte_size(destination_type);
|
||||
auto left_destination_size = destination_size - return_abi.attributes.direct.offset;
|
||||
auto is_destination_volatile = false;
|
||||
create_coerced_store(module, source_value, source_type, destination_pointer, destination_type, left_destination_size, is_destination_volatile);
|
||||
}
|
||||
else
|
||||
{
|
||||
trap();
|
||||
case TypeId::structure:
|
||||
{
|
||||
if (return_abi.semantic_type->structure.fields.length > 0)
|
||||
{
|
||||
create_coerced_store(module, source_value, source_type, destination_pointer, destination_type, left_destination_size, is_destination_volatile);
|
||||
}
|
||||
else
|
||||
{
|
||||
trap();
|
||||
}
|
||||
} break;
|
||||
case TypeId::array:
|
||||
{
|
||||
if (get_byte_size(return_abi.semantic_type) <= 8)
|
||||
{
|
||||
create_store(module, {
|
||||
.source = source_value,
|
||||
.destination = destination_pointer,
|
||||
.type = source_type,
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
trap();
|
||||
}
|
||||
} break;
|
||||
default: unreachable();
|
||||
}
|
||||
|
||||
assert(coerce_alloca);
|
||||
@ -6229,6 +6354,35 @@ fn LLVMValueRef emit_binary(Module* module, LLVMValueRef left, Type* left_type,
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case BinaryId::max:
|
||||
case BinaryId::min:
|
||||
{
|
||||
IntrinsicIndex intrinsic;
|
||||
switch (resolved_value_type->id)
|
||||
{
|
||||
case TypeId::integer:
|
||||
{
|
||||
auto is_signed = resolved_value_type->integer.is_signed;
|
||||
switch (id)
|
||||
{
|
||||
case BinaryId::max:
|
||||
{
|
||||
intrinsic = is_signed ? IntrinsicIndex::smax : IntrinsicIndex::umax;
|
||||
} break;
|
||||
case BinaryId::min:
|
||||
{
|
||||
intrinsic = is_signed ? IntrinsicIndex::smin : IntrinsicIndex::umin;
|
||||
} break;
|
||||
default: unreachable();
|
||||
}
|
||||
} break;
|
||||
default: report_error();
|
||||
}
|
||||
LLVMTypeRef argument_types[] = { resolved_value_type->llvm.abi };
|
||||
LLVMValueRef argument_values[] = { left, right };
|
||||
auto llvm_value = emit_intrinsic_call(module, intrinsic, array_to_slice(argument_types), array_to_slice(argument_values));
|
||||
return llvm_value;
|
||||
} break;
|
||||
case BinaryId::shift_right:
|
||||
if (resolved_value_type->integer.is_signed)
|
||||
{
|
||||
@ -7206,156 +7360,184 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind, bool expect
|
||||
auto is_constant = value->aggregate_initialization.is_constant;
|
||||
auto zero = value->aggregate_initialization.zero;
|
||||
|
||||
switch (resolved_value_type->id)
|
||||
switch (value->kind)
|
||||
{
|
||||
case TypeId::structure:
|
||||
case ValueKind::left:
|
||||
{
|
||||
auto fields = resolved_value_type->structure.fields;
|
||||
|
||||
if (is_constant)
|
||||
if (resolved_value_type->id != TypeId::pointer)
|
||||
{
|
||||
LLVMValueRef constant_buffer[64];
|
||||
u32 constant_count = (u32)elements.length;
|
||||
|
||||
for (u64 i = 0; i < elements.length; i += 1)
|
||||
{
|
||||
auto* value = elements[i].value;
|
||||
emit_value(module, value, TypeKind::memory, must_be_constant);
|
||||
auto llvm_value = value->llvm;
|
||||
assert(llvm_value);
|
||||
assert(LLVMIsAConstant(llvm_value));
|
||||
constant_buffer[i] = llvm_value;
|
||||
}
|
||||
|
||||
if (zero)
|
||||
{
|
||||
if (elements.length == fields.length)
|
||||
{
|
||||
unreachable();
|
||||
}
|
||||
|
||||
for (u64 i = elements.length; i < fields.length; i += 1)
|
||||
{
|
||||
auto& field = fields[i];
|
||||
auto field_type = field.type;
|
||||
resolve_type_in_place(module, field_type);
|
||||
constant_buffer[i] = LLVMConstNull(field_type->llvm.memory);
|
||||
constant_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
assert(constant_count == fields.length);
|
||||
|
||||
llvm_value = LLVMConstNamedStruct(get_llvm_type(resolved_value_type, type_kind), constant_buffer, constant_count);
|
||||
}
|
||||
else
|
||||
{
|
||||
trap();
|
||||
report_error();
|
||||
}
|
||||
|
||||
auto aggregate_type = resolved_value_type->pointer.element_type;
|
||||
|
||||
auto alloca = create_alloca(module, {
|
||||
.type = aggregate_type,
|
||||
});
|
||||
auto resolved_pointer_type = resolved_value_type;
|
||||
auto old_type = value->type;
|
||||
// Overwrite type so asserts are not triggered
|
||||
value->type = aggregate_type;
|
||||
emit_assignment(module, alloca, resolved_pointer_type, value);
|
||||
value->type = old_type;
|
||||
llvm_value = alloca;
|
||||
} break;
|
||||
case TypeId::union_type:
|
||||
case ValueKind::right:
|
||||
{
|
||||
trap();
|
||||
} break;
|
||||
case TypeId::bits:
|
||||
{
|
||||
auto fields = resolved_value_type->bits.fields;
|
||||
Type* backing_type = resolved_value_type->bits.backing_type;
|
||||
resolve_type_in_place(module, backing_type);
|
||||
auto abi_type = get_llvm_type(backing_type, type_kind);
|
||||
|
||||
if (is_constant)
|
||||
switch (resolved_value_type->id)
|
||||
{
|
||||
u64 bits_value = 0;
|
||||
|
||||
for (u32 initialization_index = 0; initialization_index < elements.length; initialization_index += 1)
|
||||
{
|
||||
auto value = elements[initialization_index].value;
|
||||
auto name = elements[initialization_index].name;
|
||||
|
||||
u32 declaration_index;
|
||||
for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1)
|
||||
case TypeId::structure:
|
||||
{
|
||||
auto& field = fields[declaration_index];
|
||||
auto fields = resolved_value_type->structure.fields;
|
||||
|
||||
if (name.equal(field.name))
|
||||
if (is_constant)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
LLVMValueRef constant_buffer[64];
|
||||
u32 constant_count = (u32)elements.length;
|
||||
|
||||
if (declaration_index == fields.length)
|
||||
{
|
||||
unreachable();
|
||||
}
|
||||
|
||||
const auto& field = fields[declaration_index];
|
||||
u64 field_value;
|
||||
switch (value->id)
|
||||
{
|
||||
case ValueId::constant_integer:
|
||||
for (u64 i = 0; i < elements.length; i += 1)
|
||||
{
|
||||
field_value = value->constant_integer.value;
|
||||
} break;
|
||||
default: unreachable();
|
||||
}
|
||||
auto* value = elements[i].value;
|
||||
emit_value(module, value, TypeKind::memory, must_be_constant);
|
||||
auto llvm_value = value->llvm;
|
||||
assert(llvm_value);
|
||||
assert(LLVMIsAConstant(llvm_value));
|
||||
constant_buffer[i] = llvm_value;
|
||||
}
|
||||
|
||||
bits_value |= field_value << field.offset;
|
||||
}
|
||||
if (zero)
|
||||
{
|
||||
if (elements.length == fields.length)
|
||||
{
|
||||
unreachable();
|
||||
}
|
||||
|
||||
llvm_value = LLVMConstInt(abi_type, bits_value, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
llvm_value = LLVMConstNull(abi_type);
|
||||
for (u64 i = elements.length; i < fields.length; i += 1)
|
||||
{
|
||||
auto& field = fields[i];
|
||||
auto field_type = field.type;
|
||||
resolve_type_in_place(module, field_type);
|
||||
constant_buffer[i] = LLVMConstNull(field_type->llvm.memory);
|
||||
constant_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 initialization_index = 0; initialization_index < elements.length; initialization_index += 1)
|
||||
{
|
||||
auto value = elements[initialization_index].value;
|
||||
auto name = elements[initialization_index].name;
|
||||
assert(constant_count == fields.length);
|
||||
|
||||
u32 declaration_index;
|
||||
for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1)
|
||||
{
|
||||
auto& field = fields[declaration_index];
|
||||
|
||||
if (name.equal(field.name))
|
||||
{
|
||||
break;
|
||||
llvm_value = LLVMConstNamedStruct(get_llvm_type(resolved_value_type, type_kind), constant_buffer, constant_count);
|
||||
}
|
||||
}
|
||||
|
||||
if (declaration_index == fields.length)
|
||||
else
|
||||
{
|
||||
// TODO: shouldn't this be a left value?
|
||||
unreachable();
|
||||
}
|
||||
} break;
|
||||
case TypeId::union_type:
|
||||
{
|
||||
unreachable();
|
||||
}
|
||||
trap();
|
||||
} break;
|
||||
case TypeId::bits:
|
||||
{
|
||||
auto fields = resolved_value_type->bits.fields;
|
||||
Type* backing_type = resolved_value_type->bits.backing_type;
|
||||
resolve_type_in_place(module, backing_type);
|
||||
auto abi_type = get_llvm_type(backing_type, type_kind);
|
||||
|
||||
const auto& field = fields[declaration_index];
|
||||
if (is_constant)
|
||||
{
|
||||
u64 bits_value = 0;
|
||||
|
||||
emit_value(module, value, TypeKind::memory, must_be_constant);
|
||||
for (u32 initialization_index = 0; initialization_index < elements.length; initialization_index += 1)
|
||||
{
|
||||
auto value = elements[initialization_index].value;
|
||||
auto name = elements[initialization_index].name;
|
||||
|
||||
auto extended = LLVMBuildZExt(module->llvm.builder, value->llvm, abi_type, "");
|
||||
auto shl = LLVMBuildShl(module->llvm.builder, extended, LLVMConstInt(abi_type, field.offset, false), "");
|
||||
auto or_value = LLVMBuildOr(module->llvm.builder, llvm_value, shl, "");
|
||||
llvm_value = or_value;
|
||||
}
|
||||
u32 declaration_index;
|
||||
for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1)
|
||||
{
|
||||
auto& field = fields[declaration_index];
|
||||
|
||||
if (name.equal(field.name))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (declaration_index == fields.length)
|
||||
{
|
||||
unreachable();
|
||||
}
|
||||
|
||||
const auto& field = fields[declaration_index];
|
||||
u64 field_value;
|
||||
switch (value->id)
|
||||
{
|
||||
case ValueId::constant_integer:
|
||||
{
|
||||
field_value = value->constant_integer.value;
|
||||
} break;
|
||||
default: unreachable();
|
||||
}
|
||||
|
||||
bits_value |= field_value << field.offset;
|
||||
}
|
||||
|
||||
llvm_value = LLVMConstInt(abi_type, bits_value, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
llvm_value = LLVMConstNull(abi_type);
|
||||
|
||||
for (u32 initialization_index = 0; initialization_index < elements.length; initialization_index += 1)
|
||||
{
|
||||
auto value = elements[initialization_index].value;
|
||||
auto name = elements[initialization_index].name;
|
||||
|
||||
u32 declaration_index;
|
||||
for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1)
|
||||
{
|
||||
auto& field = fields[declaration_index];
|
||||
|
||||
if (name.equal(field.name))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (declaration_index == fields.length)
|
||||
{
|
||||
unreachable();
|
||||
}
|
||||
|
||||
const auto& field = fields[declaration_index];
|
||||
|
||||
emit_value(module, value, TypeKind::memory, must_be_constant);
|
||||
|
||||
auto extended = LLVMBuildZExt(module->llvm.builder, value->llvm, abi_type, "");
|
||||
auto shl = LLVMBuildShl(module->llvm.builder, extended, LLVMConstInt(abi_type, field.offset, false), "");
|
||||
auto or_value = LLVMBuildOr(module->llvm.builder, llvm_value, shl, "");
|
||||
llvm_value = or_value;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case TypeId::enum_array:
|
||||
{
|
||||
assert(is_constant);
|
||||
assert(elements.length <= 64);
|
||||
Value* value_buffer[64];
|
||||
for (u64 i = 0; i < elements.length; i += 1)
|
||||
{
|
||||
value_buffer[i] = elements[i].value;
|
||||
}
|
||||
Slice<Value*> values = { value_buffer, elements.length };
|
||||
auto element_type = resolved_value_type->enum_array.element_type;
|
||||
llvm_value = emit_constant_array(module, values, element_type);
|
||||
} break;
|
||||
default: unreachable();
|
||||
}
|
||||
} break;
|
||||
case TypeId::enum_array:
|
||||
{
|
||||
assert(is_constant);
|
||||
assert(elements.length <= 64);
|
||||
Value* value_buffer[64];
|
||||
for (u64 i = 0; i < elements.length; i += 1)
|
||||
{
|
||||
value_buffer[i] = elements[i].value;
|
||||
}
|
||||
Slice<Value*> values = { value_buffer, elements.length };
|
||||
auto element_type = resolved_value_type->enum_array.element_type;
|
||||
llvm_value = emit_constant_array(module, values, element_type);
|
||||
} break;
|
||||
default: unreachable();
|
||||
}
|
||||
|
||||
} break;
|
||||
case ValueId::zero:
|
||||
{
|
||||
@ -8586,7 +8768,7 @@ void emit(Module* module)
|
||||
},
|
||||
};
|
||||
auto semantic_return_type = function_type->semantic_return_type;
|
||||
function_type->return_abi = abi_system_classify_return_type(module, resolve_alias(module, semantic_return_type));
|
||||
function_type->return_abi = abi_system_v_classify_return_type(module, resolve_alias(module, semantic_return_type));
|
||||
auto return_abi_kind = function_type->return_abi.flags.kind;
|
||||
|
||||
Type* abi_argument_type_buffer[64];
|
||||
|
@ -10,6 +10,8 @@ enum class ValueIntrinsic
|
||||
integer_max,
|
||||
int_from_enum,
|
||||
int_from_pointer,
|
||||
max,
|
||||
min,
|
||||
pointer_cast,
|
||||
pointer_from_int,
|
||||
select,
|
||||
@ -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);
|
||||
}
|
||||
|
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