Support constant compound literals

This commit is contained in:
gingerBill
2026-06-14 15:51:27 +01:00
parent ff8274c4f1
commit baef272bbd
4 changed files with 212 additions and 21 deletions

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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: {