Merge branch 'master' into fix/field-first-writes-into-dynamic-soa

This commit is contained in:
k-nrd
2026-04-09 15:19:11 -03:00
committed by GitHub
263 changed files with 64048 additions and 17378 deletions

View File

@@ -1,4 +1,14 @@
gb_internal lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise);
gb_internal lbValue lb_build_slice_expr_value(lbProcedure *p, Ast *expr);
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;
@@ -242,25 +252,18 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x,
LLVMValueRef v0 = LLVMBuildFNeg(p->builder, LLVMBuildExtractValue(p->builder, x.value, 0, ""), "");
LLVMValueRef v1 = LLVMBuildFNeg(p->builder, LLVMBuildExtractValue(p->builder, x.value, 1, ""), "");
lbAddr addr = lb_add_local_generated(p, x.type, false);
LLVMTypeRef type = llvm_addr_type(p->module, addr.addr);
LLVMBuildStore(p->builder, v0, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 0, ""));
LLVMBuildStore(p->builder, v1, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 1, ""));
return lb_addr_load(p, addr);
Type *et = base_complex_elem_type(x.type);
lbValue fields[2] = {{v0, et}, {v1, et}};
return lb_build_struct_value(p, x.type, fields, gb_count_of(fields));
} else if (is_type_quaternion(x.type)) {
LLVMValueRef v0 = LLVMBuildFNeg(p->builder, LLVMBuildExtractValue(p->builder, x.value, 0, ""), "");
LLVMValueRef v1 = LLVMBuildFNeg(p->builder, LLVMBuildExtractValue(p->builder, x.value, 1, ""), "");
LLVMValueRef v2 = LLVMBuildFNeg(p->builder, LLVMBuildExtractValue(p->builder, x.value, 2, ""), "");
LLVMValueRef v3 = LLVMBuildFNeg(p->builder, LLVMBuildExtractValue(p->builder, x.value, 3, ""), "");
lbAddr addr = lb_add_local_generated(p, x.type, false);
LLVMTypeRef type = llvm_addr_type(p->module, addr.addr);
LLVMBuildStore(p->builder, v0, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 0, ""));
LLVMBuildStore(p->builder, v1, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 1, ""));
LLVMBuildStore(p->builder, v2, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 2, ""));
LLVMBuildStore(p->builder, v3, LLVMBuildStructGEP2(p->builder, type, addr.addr.value, 3, ""));
return lb_addr_load(p, addr);
Type *et = base_complex_elem_type(x.type);
lbValue fields[4] = {{v0, et}, {v1, et}, {v2, et}, {v3, et}};
return lb_build_struct_value(p, x.type, fields, gb_count_of(fields));
} else if (is_type_simd_vector(x.type)) {
Type *elem = base_array_type(x.type);
if (is_type_float(elem)) {
@@ -964,7 +967,7 @@ gb_internal lbValue lb_emit_matrix_mul_vector(lbProcedure *p, lbValue lhs, lbVal
}
for (unsigned row_index = 0; row_index < column_count; row_index++) {
LLVMValueRef value = lb_emit_struct_ev(p, rhs, row_index).value;
LLVMValueRef value = LLVMBuildExtractValue(p->builder, rhs.value, row_index, "");
LLVMValueRef row = llvm_vector_broadcast(p, value, row_count);
v_rows[row_index] = row;
}
@@ -985,13 +988,19 @@ gb_internal lbValue lb_emit_matrix_mul_vector(lbProcedure *p, lbValue lhs, lbVal
lbAddr res = lb_add_local_generated(p, type, true);
Type *vector_elem_type = base_array_type(rhs.type);
for (i64 i = 0; i < mt->Matrix.row_count; i++) {
for (i64 j = 0; j < mt->Matrix.column_count; j++) {
lbValue dst = lb_emit_matrix_epi(p, res.addr, i, 0);
lbValue d0 = lb_emit_load(p, dst);
lbValue a = lb_emit_matrix_ev(p, lhs, i, j);
lbValue b = lb_emit_struct_ev(p, rhs, cast(i32)j);
LLVMValueRef b_value = LLVMBuildExtractValue(p->builder, rhs.value, cast(unsigned)j, "");
lbValue b = {b_value, vector_elem_type};
lbValue c = lb_emit_mul_add(p, a, b, d0, elem);
lb_emit_store(p, dst, c);
}
@@ -1040,7 +1049,7 @@ gb_internal lbValue lb_emit_vector_mul_matrix(lbProcedure *p, lbValue lhs, lbVal
}
for (unsigned column_index = 0; column_index < row_count; column_index++) {
LLVMValueRef value = lb_emit_struct_ev(p, lhs, column_index).value;
LLVMValueRef value = LLVMBuildExtractValue(p->builder, lhs.value, column_index, "");
LLVMValueRef row = llvm_vector_broadcast(p, value, column_count);
v_rows[column_index] = row;
}
@@ -1069,12 +1078,16 @@ gb_internal lbValue lb_emit_vector_mul_matrix(lbProcedure *p, lbValue lhs, lbVal
lbAddr res = lb_add_local_generated(p, type, true);
Type *vector_elem_type = base_array_type(rhs.type);
for (i64 j = 0; j < mt->Matrix.column_count; j++) {
for (i64 k = 0; k < mt->Matrix.row_count; k++) {
lbValue dst = lb_emit_matrix_epi(p, res.addr, 0, j);
lbValue d0 = lb_emit_load(p, dst);
lbValue a = lb_emit_struct_ev(p, lhs, cast(i32)k);
LLVMValueRef a_value = LLVMBuildExtractValue(p->builder, lhs.value, cast(unsigned)k, "");
lbValue a = {a_value, vector_elem_type};
lbValue b = lb_emit_matrix_ev(p, rhs, k, j);
lbValue c = lb_emit_mul_add(p, a, b, d0, elem);
lb_emit_store(p, dst, c);
@@ -1415,8 +1428,8 @@ gb_internal LLVMValueRef lb_integer_modulo(lbProcedure *p, LLVMValueRef lhs, LLV
truncated: r = a - b*trunc(a/b)
floored: r = a - b*floor(a/b)
IFF a/0 == 0, then (a%0 == a) or (a%%0 == a)
IFF a/0 == a, then (a%0 == 0) or (a%%0 == 0)
If and only if (⟺) a/0 == 0, then (a%0 == a) or (a%%0 == a)
If and only if (⟺) a/0 == a, then (a%0 == 0) or (a%%0 == 0)
*/
switch (behaviour) {
@@ -1485,7 +1498,6 @@ gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbV
}
}
lbAddr res = lb_add_local_generated(p, type, false); // NOTE: initialized in full later
lbValue a = lb_emit_struct_ev(p, lhs, 0);
lbValue b = lb_emit_struct_ev(p, lhs, 1);
lbValue c = lb_emit_struct_ev(p, rhs, 0);
@@ -1523,10 +1535,9 @@ gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbV
}
}
lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 0), real);
lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 1), imag);
return lb_addr_load(p, res);
lbValue fields[2] = {real, imag};
return lb_build_struct_value(p, type, fields, gb_count_of(fields));
} else if (is_type_quaternion(type)) {
lhs = lb_emit_conv(p, lhs, type);
rhs = lb_emit_conv(p, rhs, type);
@@ -1539,7 +1550,6 @@ gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbV
immediate_type = t_f32;
}
lbAddr res = lb_add_local_generated(p, type, false); // NOTE: initialized in full later
lbValue x0 = lb_emit_struct_ev(p, lhs, 0);
lbValue x1 = lb_emit_struct_ev(p, lhs, 1);
lbValue x2 = lb_emit_struct_ev(p, lhs, 2);
@@ -1567,10 +1577,6 @@ gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbV
lbValue z2 = lb_emit_arith(p, op, x2, y2, immediate_type);
lbValue z3 = lb_emit_arith(p, op, x3, y3, immediate_type);
lbValue d0 = lb_emit_struct_ep(p, res.addr, 0);
lbValue d1 = lb_emit_struct_ep(p, res.addr, 1);
lbValue d2 = lb_emit_struct_ep(p, res.addr, 2);
lbValue d3 = lb_emit_struct_ep(p, res.addr, 3);
if (immediate_type != ft) {
z0 = lb_emit_conv(p, z0, ft);
@@ -1579,12 +1585,8 @@ gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbV
z3 = lb_emit_conv(p, z3, ft);
}
lb_emit_store(p, d0, z0);
lb_emit_store(p, d1, z1);
lb_emit_store(p, d2, z2);
lb_emit_store(p, d3, z3);
return lb_addr_load(p, res);
lbValue fields[4] = {z0, z1, z2, z3};
return lb_build_struct_value(p, type, fields, gb_count_of(fields));
} else if (op == Token_Mul) {
TEMPORARY_ALLOCATOR_GUARD();
@@ -2148,7 +2150,7 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
if (is_type_cstring16(src) && is_type_u16_multi_ptr(dst)) {
return lb_emit_transmute(p, value, dst);
}
if (is_type_u8_multi_ptr(src) && is_type_cstring16(dst)) {
if (is_type_u16_multi_ptr(src) && is_type_cstring16(dst)) {
return lb_emit_transmute(p, value, dst);
}
if (is_type_cstring16(src) && is_type_rawptr(dst)) {
@@ -2222,77 +2224,63 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
if (is_type_complex(src) && is_type_complex(dst)) {
Type *ft = base_complex_elem_type(dst);
lbAddr gen = lb_add_local_generated(p, t, false);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, lb_emit_struct_ev(p, value, 0), ft);
lbValue imag = lb_emit_conv(p, lb_emit_struct_ev(p, value, 1), ft);
lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real);
lb_emit_store(p, lb_emit_struct_ep(p, gp, 1), imag);
return lb_addr_load(p, gen);
lbValue fields[2] = {real, imag};
return lb_build_struct_value(p, t, fields, gb_count_of(fields));
}
if (is_type_quaternion(src) && is_type_quaternion(dst)) {
// @QuaternionLayout
Type *ft = base_complex_elem_type(dst);
lbAddr gen = lb_add_local_generated(p, t, false);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue q0 = lb_emit_conv(p, lb_emit_struct_ev(p, value, 0), ft);
lbValue q1 = lb_emit_conv(p, lb_emit_struct_ev(p, value, 1), ft);
lbValue q2 = lb_emit_conv(p, lb_emit_struct_ev(p, value, 2), ft);
lbValue q3 = lb_emit_conv(p, lb_emit_struct_ev(p, value, 3), ft);
lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), q0);
lb_emit_store(p, lb_emit_struct_ep(p, gp, 1), q1);
lb_emit_store(p, lb_emit_struct_ep(p, gp, 2), q2);
lb_emit_store(p, lb_emit_struct_ep(p, gp, 3), q3);
return lb_addr_load(p, gen);
lbValue fields[4] = {q0, q1, q2, q3};
return lb_build_struct_value(p, t, fields, gb_count_of(fields));
}
if (is_type_integer(src) && is_type_complex(dst)) {
Type *ft = base_complex_elem_type(dst);
lbAddr gen = lb_add_local_generated(p, t, true);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, value, ft);
lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real);
return lb_addr_load(p, gen);
lbValue fields[2] = {real, lb_const_nil(m, ft)};
return lb_build_struct_value(p, t, fields, gb_count_of(fields));
}
if (is_type_float(src) && is_type_complex(dst)) {
Type *ft = base_complex_elem_type(dst);
lbAddr gen = lb_add_local_generated(p, t, true);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, value, ft);
lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real);
return lb_addr_load(p, gen);
lbValue fields[2] = {real, lb_const_nil(m, ft)};
return lb_build_struct_value(p, t, fields, gb_count_of(fields));
}
if (is_type_integer(src) && is_type_quaternion(dst)) {
Type *ft = base_complex_elem_type(dst);
lbAddr gen = lb_add_local_generated(p, t, true);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, value, ft);
lbValue zero = lb_const_nil(m, ft);
// @QuaternionLayout
lb_emit_store(p, lb_emit_struct_ep(p, gp, 3), real);
return lb_addr_load(p, gen);
lbValue fields[4] = {zero, zero, zero, real};
return lb_build_struct_value(p, t, fields, gb_count_of(fields));
}
if (is_type_float(src) && is_type_quaternion(dst)) {
Type *ft = base_complex_elem_type(dst);
lbAddr gen = lb_add_local_generated(p, t, true);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, value, ft);
lbValue zero = lb_const_nil(m, ft);
// @QuaternionLayout
lb_emit_store(p, lb_emit_struct_ep(p, gp, 3), real);
return lb_addr_load(p, gen);
lbValue fields[4] = {zero, zero, zero, real};
return lb_build_struct_value(p, t, fields, gb_count_of(fields));
}
if (is_type_complex(src) && is_type_quaternion(dst)) {
Type *ft = base_complex_elem_type(dst);
lbAddr gen = lb_add_local_generated(p, t, true);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, lb_emit_struct_ev(p, value, 0), ft);
lbValue imag = lb_emit_conv(p, lb_emit_struct_ev(p, value, 1), ft);
lbValue zero = lb_const_nil(m, ft);
// @QuaternionLayout
lb_emit_store(p, lb_emit_struct_ep(p, gp, 3), real);
lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), imag);
return lb_addr_load(p, gen);
lbValue fields[4] = {imag, zero, zero, real};
return lb_build_struct_value(p, t, fields, gb_count_of(fields));
}
// float <-> integer
@@ -2591,7 +2579,7 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
sel.index.allocator = temporary_allocator();
if (lookup_subtype_polymorphic_selection(t, src_type, &sel)) {
if (sel.entity == nullptr) {
GB_PANIC("invalid subtype cast %s -> ", type_to_string(src_type), type_to_string(t));
GB_PANIC("invalid subtype cast %s -> %s", type_to_string(src_type), type_to_string(t));
}
if (st_is_ptr) {
lbValue res = lb_emit_deep_field_gep(p, value, sel);
@@ -2834,8 +2822,6 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
return lb_const_nil(p->module, t);
}
lbAddr result = lb_add_local_generated(p, t, true);
Type *st = default_type(src_type);
lbValue data = lb_address_from_load_or_generate_local(p, value);
@@ -2844,13 +2830,8 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
data = lb_emit_conv(p, data, t_rawptr);
lbValue id = lb_typeid(p->module, st);
lbValue any_data = lb_emit_struct_ep(p, result.addr, 0);
lbValue any_id = lb_emit_struct_ep(p, result.addr, 1);
lb_emit_store(p, any_data, data);
lb_emit_store(p, any_id, id);
return lb_addr_load(p, result);
lbValue fields[2] = {data, id};
return lb_build_struct_value(p, t, fields, gb_count_of(fields));
}
@@ -2917,10 +2898,13 @@ gb_internal lbValue lb_emit_c_vararg(lbProcedure *p, lbValue arg, Type *type) {
gb_internal lbValue lb_compare_records(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right, Type *type) {
GB_ASSERT((is_type_struct(type) || is_type_soa_pointer(type) || is_type_union(type)) && is_type_comparable(type));
lbValue left_ptr = lb_address_from_load_or_generate_local(p, left);
lbValue right_ptr = lb_address_from_load_or_generate_local(p, right);
i64 size = type_size_of(type);
lbValue res = {};
if (type_size_of(type) == 0) {
if (size == 0) {
switch (op_kind) {
case Token_CmpEq:
return lb_const_bool(p->module, t_bool, true);
@@ -2929,8 +2913,23 @@ gb_internal lbValue lb_compare_records(lbProcedure *p, TokenKind op_kind, lbValu
}
GB_PANIC("invalid operator");
}
TEMPORARY_ALLOCATOR_GUARD();
if (is_type_simple_compare(type)) {
// if (size <= 8) {
// LLVMTypeRef int_type = LLVMIntTypeInContext(p->module->ctx, cast(unsigned)(size*8));
// LLVMValueRef l = OdinLLVMBuildLoad(p, int_type, LLVMBuildPointerCast(p->builder, left_ptr.value, LLVMPointerType(int_type, 0), ""));
// LLVMValueRef r = OdinLLVMBuildLoad(p, int_type, LLVMBuildPointerCast(p->builder, right_ptr.value, LLVMPointerType(int_type, 0), ""));
// LLVMIntPredicate pred = (op_kind == Token_CmpEq) ? LLVMIntEQ : LLVMIntNE;
// LLVMValueRef cmp = LLVMBuildICmp(p->builder, pred, l, r, "");
// res.value = cmp;
// res.type = t_bool;
// return res;
// }
// TODO(bill): Test to see if this is actually faster!!!!
auto args = array_make<lbValue>(temporary_allocator(), 3);
args[0] = lb_emit_conv(p, left_ptr, t_rawptr);
@@ -2944,6 +2943,7 @@ gb_internal lbValue lb_compare_records(lbProcedure *p, TokenKind op_kind, lbValu
args[1] = lb_emit_conv(p, right_ptr, t_rawptr);
res = lb_emit_call(p, value, args);
}
if (op_kind == Token_NotEq) {
res = lb_emit_unary_arith(p, Token_Not, res, res.type);
}
@@ -4104,10 +4104,18 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
case_end;
case_ast_node(te, TernaryIfExpr, expr);
GB_ASSERT(te->y != nullptr);
Type *type = default_type(type_of_expr(expr));
if (lb_is_expr_trivial(te->x) && lb_is_expr_trivial(te->y)) {
lbValue cond = lb_build_expr(p, te->cond);
lbValue x = lb_emit_conv(p, lb_build_expr(p, te->x), type);
lbValue y = lb_emit_conv(p, lb_build_expr(p, te->y), type);
return lb_emit_select(p, cond, x, y);
}
LLVMValueRef incoming_values[2] = {};
LLVMBasicBlockRef incoming_blocks[2] = {};
GB_ASSERT(te->y != nullptr);
lbBlock *then = lb_create_block(p, "if.then");
lbBlock *done = lb_create_block(p, "if.done"); // NOTE(bill): Append later
lbBlock *else_ = lb_create_block(p, "if.else");
@@ -4115,7 +4123,6 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
lb_build_cond(p, te->cond, then, else_);
lb_start_block(p, then);
Type *type = default_type(type_of_expr(expr));
LLVMTypeRef llvm_type = lb_type(p->module, type);
incoming_values[0] = lb_emit_conv(p, lb_build_expr(p, te->x), type).value;
@@ -4282,6 +4289,24 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
return lb_build_expr(p, se->expr);
}
}
{
Type *src_type = base_type(type_of_expr(se->expr));
if (is_type_pointer(src_type)) {
src_type = base_type(type_deref(src_type));
}
if (is_type_slice(src_type) ||
is_type_dynamic_array(src_type) ||
is_type_string(src_type) ||
is_type_string16(src_type)) {
return lb_build_slice_expr_value(p, expr);
}
if (is_type_multi_pointer(src_type) && se->high != nullptr) {
return lb_build_slice_expr_value(p, expr);
}
}
return lb_addr_load(p, lb_build_addr(p, expr));
case_end;
@@ -4337,9 +4362,9 @@ gb_internal lbAddr lb_get_soa_variable_addr(lbProcedure *p, Entity *e) {
}
gb_internal lbValue lb_get_using_variable(lbProcedure *p, Entity *e) {
GB_ASSERT(e->kind == Entity_Variable && e->flags & EntityFlag_Using);
String name = e->token.string;
InternedString interned = entity_interned_name(e);
Entity *parent = e->using_parent;
Selection sel = lookup_field(parent->type, name, false);
Selection sel = lookup_field(parent->type, interned, false);
GB_ASSERT(sel.entity != nullptr);
lbValue *pv = map_get(&p->module->values, parent);
@@ -4354,7 +4379,7 @@ gb_internal lbValue lb_get_using_variable(lbProcedure *p, Entity *e) {
} else if (pv != nullptr) {
v = *pv;
} else {
GB_ASSERT_MSG(e->using_expr != nullptr, "%.*s", LIT(name));
GB_ASSERT_MSG(e->using_expr != nullptr, "%.*s", LIT(e->token.string));
v = lb_build_addr_ptr(p, e->using_expr);
}
GB_ASSERT(v.value != nullptr);
@@ -4486,6 +4511,7 @@ gb_internal void lb_build_addr_compound_lit_populate(lbProcedure *p, Slice<Ast *
case Type_DynamicArray: et = bt->DynamicArray.elem; break;
case Type_SimdVector: et = bt->SimdVector.elem; break;
case Type_Matrix: et = bt->Matrix.elem; break;
case Type_FixedCapacityDynamicArray: et = bt->FixedCapacityDynamicArray.elem; break;
}
GB_ASSERT(et != nullptr);
@@ -4693,6 +4719,25 @@ gb_internal lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) {
return lb_addr(elem);
}
case Type_FixedCapacityDynamicArray: {
lbValue array = {};
array = lb_build_addr_ptr(p, ie->expr);
if (deref) {
array = lb_emit_load(p, array);
}
lbValue index = lb_build_expr(p, ie->index);
index = lb_emit_conv(p, index, t_int);
lbValue array_ptr = lb_emit_struct_ep(p, array, 0);
lbValue elem = lb_emit_array_ep(p, array_ptr, index);
auto index_tv = type_and_value_of_expr(ie->index);
lbValue len = lb_emit_struct_ep(p, array, 1);
len = lb_emit_load(p, len);
lb_emit_bounds_check(p, ast_token(ie->index), index, len);
return lb_addr(elem);
}
case Type_EnumeratedArray: {
lbValue array = {};
array = lb_build_addr_ptr(p, ie->expr);
@@ -4826,6 +4871,216 @@ gb_internal lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) {
}
gb_internal lbValue lb_build_slice_expr_value(lbProcedure *p, Ast *expr) {
ast_node(se, SliceExpr, expr);
if (true) {
// NOTE(bill): Fused nested slice chain: src[a:b][c:d][e:f]...
// Walk a chain of nested SliceExprs down to the root source, then apply
// each slice's bounds check and offset on raw (ptr, len) SSA values,
// building a single aggregate at the end.
Ast *inner_ast = unparen_expr(se->expr);
if (inner_ast->kind == Ast_SliceExpr) {
enum {MAX_CHAIN = 8}; // this is more than enough in practice
// Collect the chain: slices[0] is innermost, slices[n-1] is outer-most `se`.
Ast *chain[MAX_CHAIN] = {}; // bounded depth
isize chain_count = 0;
chain[chain_count++] = expr; // outer-most
Ast *root = inner_ast;
while (root->kind == Ast_SliceExpr && chain_count < MAX_CHAIN) {
chain[chain_count++] = root;
root = unparen_expr(root->SliceExpr.expr);
}
Type *root_type = base_type(type_of_expr(root));
if (is_type_pointer(root_type)) {
root_type = base_type(type_deref(root_type));
}
if (is_type_slice(root_type) ||
is_type_dynamic_array(root_type) ||
is_type_string(root_type) ||
is_type_string16(root_type)) {
lbModule *m = p->module;
// Evaluate the root source and extract raw (ptr, len).
lbValue src = lb_build_expr(p, root);
Type *src_type = base_type(src.type);
if (is_type_pointer(src_type)) {
src_type = base_type(type_deref(src_type));
src = lb_emit_load(p, src);
}
lbValue cur_ptr = {};
lbValue cur_len = {};
if (is_type_slice(src_type)) {
cur_ptr = lb_slice_elem(p, src);
cur_len = lb_slice_len(p, src);
} else if (is_type_dynamic_array(src_type)) {
cur_ptr = lb_dynamic_array_elem(p, src);
cur_len = lb_dynamic_array_len(p, src);
} else {
GB_ASSERT(is_type_string(src_type) || is_type_string16(src_type));
cur_ptr = lb_string_elem(p, src);
cur_len = lb_string_len(p, src);
}
// Apply each slice innermost-first (reverse of collection order).
for (isize i = chain_count - 1; i >= 0; i--) {
AstSliceExpr *s = &chain[i]->SliceExpr;
lbValue lo = lb_const_int(m, t_int, 0);
lbValue hi = {};
if (s->low != nullptr) {
lo = lb_correct_endianness(p, lb_build_expr(p, s->low));
}
if (s->high != nullptr) {
hi = lb_correct_endianness(p, lb_build_expr(p, s->high));
}
if (hi.value == nullptr) {
hi = cur_len;
}
bool no_indices = s->low == nullptr && s->high == nullptr;
if (!no_indices) {
lb_emit_slice_bounds_check(p, s->open, lo, hi, cur_len, s->low != nullptr);
}
cur_ptr = (s->low == nullptr) ? cur_ptr : lb_emit_ptr_offset(p, cur_ptr, lo);
cur_len = (s->low == nullptr) ? lb_emit_conv(p, hi, t_int) : lb_emit_arith(p, Token_Sub, hi, lo, t_int);
}
// Single aggregate construction.
Type *result_type = type_of_expr(expr);
if (is_type_string(result_type)) {
return lb_make_string_value(p, result_type, cur_ptr, cur_len);
}
return lb_make_slice_value(p, result_type, cur_ptr, cur_len);
}
}
}
lbValue base = lb_build_expr(p, se->expr);
Type *type = base_type(base.type);
if (is_type_pointer(type)) {
type = base_type(type_deref(type));
base = lb_emit_load(p, base);
}
lbValue low = lb_const_int(p->module, t_int, 0);
lbValue high = {};
if (se->low != nullptr) {
low = lb_correct_endianness(p, lb_build_expr(p, se->low));
}
if (se->high != nullptr) {
high = lb_correct_endianness(p, lb_build_expr(p, se->high));
}
bool no_indices = se->low == nullptr && se->high == nullptr;
switch (type->kind) {
case Type_Slice: {
Type *slice_type = type;
lbValue len = lb_slice_len(p, base);
if (high.value == nullptr) high = len;
if (!no_indices) {
lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
}
lbValue elem = lb_emit_ptr_offset(p, lb_slice_elem(p, base), low);
lbValue new_len = (se->low == nullptr)
? lb_emit_conv(p, high, t_int)
: lb_emit_arith(p, Token_Sub, high, low, t_int);
return lb_make_slice_value(p, slice_type, elem, new_len);
}
case Type_DynamicArray: {
Type *elem_type = type->DynamicArray.elem;
Type *slice_type = alloc_type_slice(elem_type);
lbValue len = lb_dynamic_array_len(p, base);
if (high.value == nullptr) high = len;
if (!no_indices) {
lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
}
lbValue elem = lb_emit_ptr_offset(p, lb_dynamic_array_elem(p, base), low);
lbValue new_len = (se->low == nullptr)
? lb_emit_conv(p, high, t_int)
: lb_emit_arith(p, Token_Sub, high, low, t_int);
return lb_make_slice_value(p, slice_type, elem, new_len);
}
case Type_Basic: {
if (is_type_string16(type)) {
GB_ASSERT_MSG(are_types_identical(type, t_string16), "got %s", type_to_string(type));
lbValue len = lb_string_len(p, base);
if (high.value == nullptr) high = len;
if (!no_indices) {
lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
}
lbValue elem = lb_emit_ptr_offset(p, lb_string_elem(p, base), low);
lbValue new_len = (se->low == nullptr)
? lb_emit_conv(p, high, t_int)
: lb_emit_arith(p, Token_Sub, high, low, t_int);
return lb_make_string_value(p, t_string16, elem, new_len);
}
GB_ASSERT_MSG(are_types_identical(type, t_string), "got %s", type_to_string(type));
lbValue len = lb_string_len(p, base);
if (high.value == nullptr) high = len;
if (!no_indices) {
lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
}
lbValue elem = lb_emit_ptr_offset(p, lb_string_elem(p, base), low);
lbValue new_len = (se->low == nullptr)
? lb_emit_conv(p, high, t_int)
: lb_emit_arith(p, Token_Sub, high, low, t_int);
return lb_make_string_value(p, t_string, elem, new_len);
}
case Type_MultiPointer: {
GB_ASSERT(se->high != nullptr);
Type *elem_type = type->MultiPointer.elem;
Type *slice_type = alloc_type_slice(elem_type);
low = lb_emit_conv(p, low, t_int);
high = lb_emit_conv(p, high, t_int);
lb_emit_multi_pointer_slice_bounds_check(p, se->open, low, high);
LLVMValueRef indices[1] = {low.value};
lbValue ptr = {};
ptr.value = LLVMBuildGEP2(p->builder, lb_type(p->module, elem_type), base.value, indices, 1, "");
ptr.type = alloc_type_pointer(elem_type);
lbValue new_len = (se->low == nullptr)
? lb_emit_conv(p, high, t_int)
: lb_emit_arith(p, Token_Sub, high, low, t_int);
return lb_make_slice_value(p, slice_type, ptr, new_len);
}
default:
GB_PANIC("lb_build_slice_expr_value: unexpected type %s", type_to_string(base.type));
return {};
}
}
gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
ast_node(se, SliceExpr, expr);
@@ -4936,6 +5191,31 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
return slice;
}
case Type_FixedCapacityDynamicArray: {
Type *elem_type = type->FixedCapacityDynamicArray.elem;
Type *slice_type = alloc_type_slice(elem_type);
lbValue len = lb_fixed_capacity_dynamic_array_len(p, base);
if (high.value == nullptr) high = len;
bool low_const = type_and_value_of_expr(se->low).mode == Addressing_Constant;
bool high_const = type_and_value_of_expr(se->high).mode == Addressing_Constant;
if (!low_const || !high_const) {
if (!no_indices) {
lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
}
}
lbValue data_ptr = lb_addr_get_ptr(p, addr);
lbValue array_ptr = lb_emit_struct_ep(p, data_ptr, 0);
lbValue elem = lb_emit_ptr_offset(p, lb_array_elem(p, array_ptr), low);
lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
lbAddr slice = lb_add_local_generated(p, slice_type, false);
lb_fill_slice(p, slice, elem, new_len);
return slice;
}
case Type_Basic: {
if (is_type_string16(type)) {
GB_ASSERT_MSG(are_types_identical(type, t_string16), "got %s", type_to_string(type));
@@ -5051,12 +5331,13 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
Type *et = nullptr;
switch (bt->kind) {
case Type_Array: et = bt->Array.elem; break;
case Type_EnumeratedArray: et = bt->EnumeratedArray.elem; break;
case Type_Slice: et = bt->Slice.elem; break;
case Type_BitSet: et = bt->BitSet.elem; break;
case Type_SimdVector: et = bt->SimdVector.elem; break;
case Type_Matrix: et = bt->Matrix.elem; break;
case Type_Array: et = bt->Array.elem; break;
case Type_EnumeratedArray: et = bt->EnumeratedArray.elem; break;
case Type_Slice: et = bt->Slice.elem; break;
case Type_BitSet: et = bt->BitSet.elem; break;
case Type_SimdVector: et = bt->SimdVector.elem; break;
case Type_Matrix: et = bt->Matrix.elem; break;
case Type_FixedCapacityDynamicArray: et = bt->FixedCapacityDynamicArray.elem; break;
}
String proc_name = {};
@@ -5083,8 +5364,8 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
for (Ast *elem : cl->elems) {
ast_node(fv, FieldValue, elem);
String name = fv->field->Ident.token.string;
Selection sel = lookup_field(bt, name, false);
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);
@@ -5150,13 +5431,7 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
for (isize i = 0; i < fields.count; i++) {
auto const &f = fields[i];
LLVMValueRef mask = LLVMConstInt(lit, 1, false);
#if LLVM_VERSION_MAJOR >= 19
mask = LLVMBuildShl(p->builder, mask, LLVMConstInt(lit, f.bit_size, false), "");
#else
mask = LLVMConstShl(mask, LLVMConstInt(lit, f.bit_size, false));
#endif
mask = LLVMConstSub(mask, LLVMConstInt(lit, 1, false));
LLVMValueRef mask = lb_const_low_bits_mask(lit, f.bit_size);
LLVMValueRef elem = values[i].value;
if (lb_sizeof(lit) < lb_sizeof(LLVMTypeOf(elem))) {
@@ -5202,13 +5477,7 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
GB_ASSERT(mask_width > 0);
bits_to_set -= mask_width;
LLVMValueRef mask = LLVMConstInt(vt, 1, false);
#if LLVM_VERSION_MAJOR >= 19
mask = LLVMBuildShl(p->builder, mask, LLVMConstInt(vt, mask_width, false), "");
#else
mask = LLVMConstShl(mask, LLVMConstInt(vt, mask_width, false));
#endif
mask = LLVMConstSub(mask, LLVMConstInt(vt, 1, false));
LLVMValueRef mask = lb_const_low_bits_mask(vt, mask_width);
LLVMValueRef to_set = LLVMBuildAnd(p->builder, val, mask, "");
@@ -5289,8 +5558,8 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
if (elem->kind == Ast_FieldValue) {
ast_node(fv, FieldValue, elem);
String name = fv->field->Ident.token.string;
Selection sel = lookup_field(bt, name, false);
InternedString interned = fv->field->Ident.interned;
Selection sel = lookup_field(bt, interned, false);
GB_ASSERT(!sel.indirect);
elem = fv->value;
@@ -5347,11 +5616,20 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
Type *fet = field_expr.type;
GB_ASSERT(fet->kind != Type_Tuple);
// HACK TODO(bill): THIS IS A MASSIVE HACK!!!!
if (is_type_union(ft) && !are_types_identical(fet, ft) && !is_type_untyped(fet)) {
GB_ASSERT_MSG(union_variant_index(ft, fet) >= 0, "%s", type_to_string(fet));
if (union_is_variant_of(ft, fet)) {
// NOTE(bill, 2026-03-15): If it's a direct assignment for a variant to the
// direct union we can just assign it directly to minimize wasted code generation
//
// TODO(bill): Allow this for deeply nested unions too e.g. union{union{T}}
GB_ASSERT_MSG(union_variant_index_checked(ft, fet) >= 0, "%s", type_to_string(fet));
GB_ASSERT(are_types_identical(type_deref(gep.type), ft));
lb_emit_store_union_variant(p, gep, field_expr, fet);
lb_emit_store_union_variant(p, gep, field_expr, fet);
} else {
lbValue fv = lb_emit_conv(p, field_expr, ft);
lb_emit_store(p, gep, fv);
}
} else {
lbValue fv = lb_emit_conv(p, field_expr, ft);
lb_emit_store(p, gep, fv);
@@ -5450,6 +5728,25 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
break;
}
case Type_FixedCapacityDynamicArray: {
if (cl->elems.count > 0) {
lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr)));
auto temp_data = array_make<lbCompoundLitElemTempData>(temporary_allocator(), 0, cl->elems.count);
lb_build_addr_compound_lit_populate(p, cl->elems, &temp_data, type);
lbValue dst_ptr = lb_addr_get_ptr(p, v);
for_array(i, temp_data) {
i32 index = cast(i32)(temp_data[i].elem_index);
temp_data[i].gep = lb_emit_array_epi(p, dst_ptr, index);
}
lb_build_addr_compound_lit_assign_array(p, temp_data);
}
break;
}
case Type_DynamicArray: {
if (cl->elems.count == 0) {
break;
@@ -5516,12 +5813,12 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
if (elem->kind == Ast_FieldValue) {
ast_node(fv, FieldValue, elem);
Selection sel = lookup_field(bt, fv->field->Ident.token.string, false);
Selection sel = lookup_field(bt, fv->field->Ident.interned, false);
index = sel.index[0];
elem = fv->value;
} else {
TypeAndValue tav = type_and_value_of_expr(elem);
Selection sel = lookup_field(bt, field_names[field_index], false);
Selection sel = lookup_field(bt, string_interner_insert(field_names[field_index]), false);
index = sel.index[0];
}
@@ -5663,7 +5960,7 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
case_ast_node(se, SelectorExpr, expr);
Ast *sel_node = unparen_expr(se->selector);
if (sel_node->kind == Ast_Ident) {
String selector = sel_node->Ident.token.string;
InternedString selector = sel_node->Ident.interned;
TypeAndValue tav = type_and_value_of_expr(se->expr);
if (tav.mode == Addressing_Invalid) {
@@ -5683,7 +5980,7 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
GB_ASSERT(e->kind == Entity_Procedure);
return lb_addr(lb_find_value_from_entity(p->module, e));
}
GB_PANIC("Unreachable %.*s", LIT(selector));
GB_PANIC("Unreachable %s", selector.cstring());
}
if (se->swizzle_count > 0) {
@@ -6078,4 +6375,3 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
return {};
}