mirror of
https://github.com/odin-lang/Odin.git
synced 2026-07-11 02:19:30 +00:00
Support constant compound literals
This commit is contained in:
@@ -10175,7 +10175,7 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
|
||||
|
||||
String assignment_str = str_lit("structure literal");
|
||||
if (bt->kind == Type_BitField) {
|
||||
assignment_str = str_lit("bit_field literal");
|
||||
assignment_str = str_lit("'bit_field' literal");
|
||||
}
|
||||
|
||||
for (Ast *elem : elems) {
|
||||
@@ -10187,14 +10187,14 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
|
||||
Ast *ident = fv->field;
|
||||
if (ident->kind == Ast_ImplicitSelectorExpr) {
|
||||
gbString expr_str = expr_to_string(ident);
|
||||
error(ident, "Field names do not start with a '.', remove the '.' in structure literal", expr_str);
|
||||
error(ident, "Field names do not start with a '.', remove the '.' in %.*s", expr_str, LIT(assignment_str));
|
||||
gb_string_free(expr_str);
|
||||
|
||||
ident = ident->ImplicitSelectorExpr.selector;
|
||||
}
|
||||
if (ident->kind != Ast_Ident) {
|
||||
gbString expr_str = expr_to_string(ident);
|
||||
error(elem, "Invalid field name '%s' in structure literal", expr_str);
|
||||
error(elem, "Invalid field name '%s' in %.*s", expr_str, LIT(assignment_str));
|
||||
gb_string_free(expr_str);
|
||||
continue;
|
||||
}
|
||||
@@ -10204,7 +10204,9 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
|
||||
Selection sel = lookup_field(type, interned, o->mode == Addressing_Type);
|
||||
bool is_unknown = sel.entity == nullptr;
|
||||
if (is_unknown) {
|
||||
error(ident, "Unknown field '%.*s' in structure literal", LIT(name));
|
||||
gbString s = type_to_string(type);
|
||||
error(ident, "Unknown field '%.*s' in %.*s", LIT(name), LIT(assignment_str));
|
||||
gb_string_free(s);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -10258,7 +10260,7 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
|
||||
ft = bt->Array.elem;
|
||||
break;
|
||||
case Type_BitField:
|
||||
is_constant = false;
|
||||
// is_constant = false;
|
||||
ft = bt->BitField.fields[index]->type;
|
||||
break;
|
||||
default:
|
||||
@@ -10307,6 +10309,12 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
|
||||
if (is_constant) {
|
||||
is_constant = check_is_operand_compound_lit_constant(c, &o, field->type);
|
||||
}
|
||||
if (bt->kind == Type_BitField) {
|
||||
if (is_type_different_to_arch_endianness(field->type)) {
|
||||
is_constant = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
u8 prev_bit_field_bit_size = c->bit_field_bit_size;
|
||||
if (field->kind == Entity_Variable && field->Variable.bit_field_bit_size) {
|
||||
@@ -11358,7 +11366,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
|
||||
if (cl->elems.count == 0) {
|
||||
break; // NOTE(bill): No need to init
|
||||
}
|
||||
is_constant = false;
|
||||
// is_constant = false;
|
||||
if (cl->elems[0]->kind != Ast_FieldValue) {
|
||||
gbString type_str = type_to_string(type);
|
||||
error(node, "%s ('bit_field') compound literals are only allowed to contain 'field = value' elements", type_str);
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
gb_internal LLVMValueRef lb_const_low_bits_mask(LLVMTypeRef type, u64 bit_count) {
|
||||
GB_ASSERT(bit_count <= 64);
|
||||
if (bit_count == 0) {
|
||||
return LLVMConstInt(type, 0, false);
|
||||
}
|
||||
u64 mask = bit_count == 64 ? ~0ull : (1ull<<bit_count)-1;
|
||||
return LLVMConstInt(type, mask, false);
|
||||
}
|
||||
|
||||
gb_internal bool lb_is_const(lbValue value) {
|
||||
LLVMValueRef v = value.value;
|
||||
if (is_type_untyped_nil(value.type)) {
|
||||
@@ -716,6 +725,173 @@ gb_internal LLVMValueRef lb_fill_fixed_capacity_dynamic_array(lbModule *m, i64 e
|
||||
return llvm_const_named_struct(m, original_type, svalues, svalue_count);
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_const_value_bit_field(lbModule *m, Type *type, Ast *value_compound) {
|
||||
ast_node(cl, CompoundLit, value_compound);
|
||||
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
Type *bt = base_type(type);
|
||||
|
||||
// Type *backing_type = core_type(bt->BitField.backing_type);
|
||||
|
||||
struct FieldData {
|
||||
Type *field_type;
|
||||
u64 bit_offset;
|
||||
u64 bit_size;
|
||||
};
|
||||
auto values = array_make<lbValue>(temporary_allocator(), 0, cl->elems.count);
|
||||
auto fields = array_make<FieldData>(temporary_allocator(), 0, cl->elems.count);
|
||||
|
||||
for (Ast *elem : cl->elems) {
|
||||
ast_node(fv, FieldValue, elem);
|
||||
InternedString interned = fv->field->Ident.interned;
|
||||
Selection sel = lookup_field(bt, interned, false);
|
||||
GB_ASSERT(sel.is_bit_field);
|
||||
GB_ASSERT(!sel.indirect);
|
||||
GB_ASSERT(sel.index.count == 1);
|
||||
GB_ASSERT(sel.entity != nullptr);
|
||||
|
||||
i64 index = sel.index[0];
|
||||
Entity *f = bt->BitField.fields[index];
|
||||
GB_ASSERT(f == sel.entity);
|
||||
i64 bit_offset = bt->BitField.bit_offsets[index];
|
||||
i64 bit_size = bt->BitField.bit_sizes[index];
|
||||
GB_ASSERT(bit_size > 0);
|
||||
|
||||
Type *field_type = sel.entity->type;
|
||||
if (fv->value->tav.mode != Addressing_Constant) {
|
||||
continue;
|
||||
}
|
||||
lbValue field_expr = lb_const_value(m, field_type, fv->value->tav.value, field_type);
|
||||
array_add(&values, field_expr);
|
||||
array_add(&fields, FieldData{field_type, cast(u64)bit_offset, cast(u64)bit_size});
|
||||
}
|
||||
|
||||
// NOTE(bill): inline insertion sort should be good enough, right?
|
||||
for (isize i = 1; i < values.count; i++) {
|
||||
for (isize j = i;
|
||||
j > 0 && fields[i].bit_offset < fields[j].bit_offset;
|
||||
j--) {
|
||||
auto vtmp = values[j];
|
||||
values[j] = values[j-1];
|
||||
values[j-1] = vtmp;
|
||||
|
||||
auto ftmp = fields[j];
|
||||
fields[j] = fields[j-1];
|
||||
fields[j-1] = ftmp;
|
||||
}
|
||||
}
|
||||
|
||||
bool any_fields_different_endian = false;
|
||||
for (auto const &f : fields) {
|
||||
if (is_type_different_to_arch_endianness(f.field_type)) {
|
||||
// NOTE(bill): Just be slow for this, to be correct
|
||||
any_fields_different_endian = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
GB_ASSERT(!any_fields_different_endian);
|
||||
|
||||
|
||||
Type *backing_type = core_type(bt->BitField.backing_type);
|
||||
GB_ASSERT(is_type_integer(backing_type) ||
|
||||
(is_type_array(backing_type) && is_type_integer(backing_type->Array.elem)));
|
||||
|
||||
if (is_type_integer(backing_type)) {
|
||||
// SINGLE INTEGER BACKING ONLY
|
||||
|
||||
LLVMTypeRef lit = lb_type(m, backing_type);
|
||||
|
||||
LLVMValueRef res = LLVMConstInt(lit, 0, false);
|
||||
|
||||
for (isize i = 0; i < fields.count; i++) {
|
||||
auto const &f = fields[i];
|
||||
|
||||
LLVMValueRef mask = lb_const_low_bits_mask(lit, f.bit_size);
|
||||
|
||||
LLVMValueRef elem = values[i].value;
|
||||
if (lb_sizeof(lit) < lb_sizeof(LLVMTypeOf(elem))) {
|
||||
elem = LLVMBuildTrunc(m->const_dummy_builder, elem, lit, "");
|
||||
} else {
|
||||
elem = LLVMBuildZExt(m->const_dummy_builder, elem, lit, "");
|
||||
}
|
||||
elem = LLVMBuildAnd(m->const_dummy_builder, elem, mask, "");
|
||||
|
||||
elem = LLVMBuildShl(m->const_dummy_builder, elem, LLVMConstInt(lit, f.bit_offset, false), "");
|
||||
|
||||
res = LLVMBuildOr(m->const_dummy_builder, res, elem, "");
|
||||
}
|
||||
|
||||
return {res, type};
|
||||
} else if (is_type_array(backing_type)) {
|
||||
// ARRAY OF INTEGER BACKING
|
||||
|
||||
i64 array_count = backing_type->Array.count;
|
||||
LLVMTypeRef lit = lb_type(m, core_type(backing_type->Array.elem));
|
||||
|
||||
LLVMValueRef *elems = gb_alloc_array(temporary_allocator(), LLVMValueRef, array_count);
|
||||
for (i64 i = 0; i < array_count; i++) {
|
||||
elems[i] = LLVMConstInt(lit, 0, false);
|
||||
}
|
||||
|
||||
u64 elem_bit_size = cast(u64)(8*type_size_of(backing_type->Array.elem));
|
||||
u64 curr_bit_offset = 0;
|
||||
for (isize i = 0; i < fields.count; i++) {
|
||||
auto const &f = fields[i];
|
||||
|
||||
LLVMValueRef val = values[i].value;
|
||||
LLVMTypeRef vt = lb_type(m, values[i].type);
|
||||
|
||||
curr_bit_offset = f.bit_offset;
|
||||
|
||||
for (u64 bits_to_set = f.bit_size;
|
||||
bits_to_set > 0;
|
||||
/**/) {
|
||||
i64 elem_idx = curr_bit_offset/elem_bit_size;
|
||||
u64 elem_bit_offset = curr_bit_offset%elem_bit_size;
|
||||
|
||||
u64 mask_width = gb_min(bits_to_set, elem_bit_size-elem_bit_offset);
|
||||
GB_ASSERT(mask_width > 0);
|
||||
bits_to_set -= mask_width;
|
||||
|
||||
LLVMValueRef mask = lb_const_low_bits_mask(vt, mask_width);
|
||||
|
||||
LLVMValueRef to_set = LLVMBuildAnd(m->const_dummy_builder, val, mask, "");
|
||||
|
||||
if (elem_bit_offset != 0) {
|
||||
to_set = LLVMBuildShl(m->const_dummy_builder, to_set, LLVMConstInt(vt, elem_bit_offset, false), "");
|
||||
}
|
||||
to_set = LLVMBuildTrunc(m->const_dummy_builder, to_set, lit, "");
|
||||
|
||||
if (LLVMIsNull(elems[elem_idx])) {
|
||||
elems[elem_idx] = to_set; // don't even bother doing `0 | to_set`
|
||||
} else {
|
||||
elems[elem_idx] = LLVMBuildOr(m->const_dummy_builder, elems[elem_idx], to_set, "");
|
||||
}
|
||||
|
||||
if (mask_width != 0) {
|
||||
val = LLVMBuildLShr(m->const_dummy_builder, val, LLVMConstInt(vt, mask_width, false), "");
|
||||
}
|
||||
curr_bit_offset += mask_width;
|
||||
}
|
||||
|
||||
GB_ASSERT_MSG(curr_bit_offset == f.bit_offset + f.bit_size, "%llu == %llu + %llu",
|
||||
cast(unsigned long long)curr_bit_offset,
|
||||
cast(unsigned long long)f.bit_offset,
|
||||
cast(unsigned long long)f.bit_size
|
||||
);
|
||||
}
|
||||
|
||||
LLVMValueRef res = LLVMConstArray(lit, elems, cast(unsigned)array_count);
|
||||
return {res, type};
|
||||
} else {
|
||||
// SLOW STORAGE
|
||||
GB_PANIC("TODO(bill): bit_field storage of an unknown kind");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Type *value_type, lbConstContext cc) {
|
||||
if (cc.allow_local) {
|
||||
cc.is_rodata = false;
|
||||
@@ -728,8 +904,10 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
|
||||
|
||||
lbValue res = {};
|
||||
res.type = original_type;
|
||||
type = core_type(type);
|
||||
value = convert_exact_value_for_type(value, type);
|
||||
if (!is_type_bit_field(original_type)) {
|
||||
type = core_type(type);
|
||||
value = convert_exact_value_for_type(value, type);
|
||||
}
|
||||
|
||||
bool is_local = cc.allow_local && m->curr_procedure != nullptr;
|
||||
|
||||
@@ -1278,7 +1456,9 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
|
||||
return res;
|
||||
|
||||
case ExactValue_Compound:
|
||||
if (is_type_slice(type)) {
|
||||
if (is_type_bit_field(original_type)) {
|
||||
return lb_const_value_bit_field(m, original_type, value.value_compound);
|
||||
} else if (is_type_slice(type)) {
|
||||
return lb_const_value(m, type, value, value_type, cc);
|
||||
} else if (is_type_soa_struct(type)) {
|
||||
GB_ASSERT(type->kind == Type_Struct);
|
||||
|
||||
@@ -2,15 +2,6 @@ gb_internal lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue l
|
||||
gb_internal lbValue lb_build_slice_expr_value(lbProcedure *p, Ast *expr);
|
||||
gb_internal lbValue lb_expand_values(lbProcedure *p, lbValue val, Type *type);
|
||||
|
||||
gb_internal LLVMValueRef lb_const_low_bits_mask(LLVMTypeRef type, u64 bit_count) {
|
||||
GB_ASSERT(bit_count <= 64);
|
||||
if (bit_count == 0) {
|
||||
return LLVMConstInt(type, 0, false);
|
||||
}
|
||||
u64 mask = bit_count == 64 ? ~0ull : (1ull<<bit_count)-1;
|
||||
return LLVMConstInt(type, mask, false);
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast *right, Type *final_type) {
|
||||
lbModule *m = p->module;
|
||||
|
||||
@@ -4422,7 +4413,6 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
|
||||
GB_ASSERT_MSG(tv.mode != Addressing_Invalid, "invalid expression '%s' (tv.mode = %d, tv.type = %s) @ %s\n Current Proc: %.*s : %s", expr_to_string(expr), tv.mode, type_to_string(tv.type), token_pos_to_string(expr_pos), LIT(p->name), type_to_string(p->type));
|
||||
|
||||
|
||||
|
||||
if (tv.value.kind != ExactValue_Invalid) {
|
||||
Type *original_type = lb_build_expr_original_const_type(expr);
|
||||
// NOTE(bill): Short on constant values
|
||||
|
||||
@@ -3829,8 +3829,22 @@ gb_internal Selection lookup_field_with_selection(Type *type_, InternedString fi
|
||||
}
|
||||
} else if (type->kind == Type_BitSet) {
|
||||
return lookup_field_with_selection(type->BitSet.elem, field_name, true, sel, allow_blank_ident);
|
||||
}
|
||||
} else if (type->kind == Type_BitField) {
|
||||
for_array(i, type->BitField.fields) {
|
||||
Entity *f = type->BitField.fields[i];
|
||||
if (f->kind != Entity_Variable || (f->flags & EntityFlag_Field) == 0) {
|
||||
continue;
|
||||
}
|
||||
auto str = entity_interned_name(f);
|
||||
if (field_name == str) {
|
||||
selection_add_index(&sel, i); // HACK(bill): Leaky memory
|
||||
sel.entity = f;
|
||||
sel.is_bit_field = true;
|
||||
return sel;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (type->kind == Type_Generic && type->Generic.specialized != nullptr) {
|
||||
Type *specialized = type->Generic.specialized;
|
||||
@@ -3929,7 +3943,6 @@ gb_internal Selection lookup_field_with_selection(Type *type_, InternedString fi
|
||||
return sel;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (type->kind == Type_Basic) {
|
||||
switch (type->Basic.kind) {
|
||||
case Basic_any: {
|
||||
|
||||
Reference in New Issue
Block a user