From 4b482366c15bd1ecdcf9c913fd583d8c26baadec Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jun 2026 15:51:27 +0100 Subject: [PATCH] Support constant compound literals --- src/check_expr.cpp | 20 ++-- src/llvm_backend_const.cpp | 186 ++++++++++++++++++++++++++++++++++++- src/llvm_backend_expr.cpp | 10 -- src/types.cpp | 17 +++- 4 files changed, 212 insertions(+), 21 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 39c47ae03..337310664 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -10168,7 +10168,7 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slicekind == Type_BitField) { - assignment_str = str_lit("bit_field literal"); + assignment_str = str_lit("'bit_field' literal"); } for (Ast *elem : elems) { @@ -10180,14 +10180,14 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slicefield; 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; } @@ -10197,7 +10197,9 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slicemode == 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; } @@ -10251,7 +10253,7 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, SliceArray.elem; break; case Type_BitField: - is_constant = false; + // is_constant = false; ft = bt->BitField.fields[index]->type; break; default: @@ -10300,6 +10302,12 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slicetype); } + 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) { @@ -11351,7 +11359,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); diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 0f0d458dc..30ff35b1b 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -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<BitField.backing_type); + + struct FieldData { + Type *field_type; + u64 bit_offset; + u64 bit_size; + }; + auto values = array_make(temporary_allocator(), 0, cl->elems.count); + auto fields = array_make(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); diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 554ac79b7..e02f743ca 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -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<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 diff --git a/src/types.cpp b/src/types.cpp index 260dd4918..135461874 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -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: {