struct field reordering (minimizing padding)
This commit is contained in:
+1
-1
@@ -40,7 +40,7 @@ roadmap and milestone history.
|
||||
- narrow immutable zero-terminated byte pointer/slice conversion to `*c_char` / `?*c_char` without general `u8`/`c_char` interchange
|
||||
- optionals with `null`, `orelse`, postfix `?`, conditional unwraps, guarded unwraps, and left-to-right short-circuiting multi-unwraps
|
||||
- nominal distinct types with explicit scalar backing conversion during construction and explicit scalar backing extraction, native enums with optional explicit integer backing and explicit backing-to-scalar casts, contextual enum literals, and imported C enums as target-backed integer aliases
|
||||
- source-order native structs, opaque nominal records with `Name :: opaque`, complete `c_struct { ... }`, keyed record literals, native untagged unions, and native tagged unions `union(Enum)` / `union(enum)`
|
||||
- compiler-reordered native structs with fields laid out by decreasing alignment (declaration order breaks ties and remains the reflection/diagnostic order), opaque nominal records with `Name :: opaque`, complete source-order `c_struct { ... }`, keyed record literals, native untagged unions, and native tagged unions `union(Enum)` / `union(enum)`
|
||||
- named native struct fields may declare defaults with `field T = expression`; keyed literals use defaults for omitted fields and explicit initializers override them
|
||||
- void-payload tagged-union variants, anonymous struct payloads, contextual `.variant`, `.variant{payload}`, and `.variant{field = value}` construction
|
||||
- native sum composition with `A | B` for unbacked enums and tagged unions, optionally grouped as `(A | B)`, using program-global `u16` variant ids
|
||||
|
||||
+38
-5
@@ -1117,6 +1117,15 @@ emit_instruction_stream :: proc(
|
||||
} else if item.kind == .Range && arg_index == 2 {
|
||||
element_type = types.BOOL
|
||||
}
|
||||
aggregate_index := arg_index
|
||||
if item.kind == .Struct {
|
||||
aggregate_index = types.physical_field_index(
|
||||
&emitter.module.types,
|
||||
instruction.type,
|
||||
arg_index,
|
||||
emitter.module.target,
|
||||
)
|
||||
}
|
||||
final := arg_index == total-1
|
||||
if final {
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = insertvalue %s ", instruction_index, type_name)
|
||||
@@ -1134,7 +1143,7 @@ emit_instruction_stream :: proc(
|
||||
} else {
|
||||
write_constant(&emitter.builder, i64(item.sentinel), element_type, &emitter.module.types)
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, ", %d\n", arg_index)
|
||||
fmt.sbprintf(&emitter.builder, ", %d\n", aggregate_index)
|
||||
}
|
||||
case .Null:
|
||||
item, ok := types.node(&emitter.module.types, instruction.type)
|
||||
@@ -1334,13 +1343,19 @@ emit_instruction_stream :: proc(
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid field reference")
|
||||
continue
|
||||
}
|
||||
physical_index := types.physical_field_index(
|
||||
&emitter.module.types,
|
||||
base_type,
|
||||
field_index,
|
||||
emitter.module.target,
|
||||
)
|
||||
if types.is_union(base_type, &emitter.module.types) {
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = getelementptr i8, ptr %%v%d, i64 %d\n", instruction_index, instruction.a, types.union_payload_offset(base_type, &emitter.module.types, emitter.module.target))
|
||||
} else {
|
||||
fmt.sbprintf(
|
||||
&emitter.builder,
|
||||
" %%v%d = getelementptr %s, ptr %%v%d, i32 0, i32 %d\n",
|
||||
instruction_index, llvm_type(base_type, &emitter.module.types), instruction.a, field_index,
|
||||
instruction_index, llvm_type(base_type, &emitter.module.types), instruction.a, physical_index,
|
||||
)
|
||||
}
|
||||
case .Load:
|
||||
@@ -2593,12 +2608,30 @@ emit_types :: proc(emitter: ^Emitter) {
|
||||
strings.write_string(&emitter.builder, " }\n")
|
||||
continue
|
||||
}
|
||||
fields := types.fields_for(&emitter.module.types, id)
|
||||
strings.write_string(&emitter.builder, "{ ")
|
||||
for field, field_index in types.fields_for(&emitter.module.types, id) {
|
||||
if field_index > 0 {
|
||||
previous_index := -1
|
||||
for physical_index in 0..<len(fields) {
|
||||
logical_index := -1
|
||||
for _, candidate_index in fields {
|
||||
if previous_index >= 0 &&
|
||||
!types.field_layout_precedes(
|
||||
&emitter.module.types, id, previous_index, candidate_index, emitter.module.target,
|
||||
) {
|
||||
continue
|
||||
}
|
||||
if logical_index < 0 ||
|
||||
types.field_layout_precedes(
|
||||
&emitter.module.types, id, candidate_index, logical_index, emitter.module.target,
|
||||
) {
|
||||
logical_index = candidate_index
|
||||
}
|
||||
}
|
||||
if physical_index > 0 {
|
||||
strings.write_string(&emitter.builder, ", ")
|
||||
}
|
||||
strings.write_string(&emitter.builder, llvm_type(field.type, &emitter.module.types))
|
||||
strings.write_string(&emitter.builder, llvm_type(fields[logical_index].type, &emitter.module.types))
|
||||
previous_index = logical_index
|
||||
}
|
||||
strings.write_string(&emitter.builder, " }\n")
|
||||
}
|
||||
|
||||
@@ -636,6 +636,43 @@ fields_for :: proc(store: ^Store, value: Type) -> []Field {
|
||||
return store.fields[start:end]
|
||||
}
|
||||
|
||||
field_layout_precedes :: proc(
|
||||
store: ^Store,
|
||||
value: Type,
|
||||
left, right: int,
|
||||
selected := target.DEFAULT,
|
||||
) -> bool {
|
||||
fields := fields_for(store, value)
|
||||
item, ok := node(store, value)
|
||||
if !ok || item.kind != .Struct || item.c_layout ||
|
||||
left < 0 || left >= len(fields) || right < 0 || right >= len(fields) {
|
||||
return left < right
|
||||
}
|
||||
left_alignment := alignment_of(fields[left].type, store, selected)
|
||||
right_alignment := alignment_of(fields[right].type, store, selected)
|
||||
return left_alignment > right_alignment ||
|
||||
(left_alignment == right_alignment && left < right)
|
||||
}
|
||||
|
||||
physical_field_index :: proc(
|
||||
store: ^Store,
|
||||
value: Type,
|
||||
logical_index: int,
|
||||
selected := target.DEFAULT,
|
||||
) -> int {
|
||||
fields := fields_for(store, value)
|
||||
if logical_index < 0 || logical_index >= len(fields) {
|
||||
return logical_index
|
||||
}
|
||||
result := 0
|
||||
for _, other_index in fields {
|
||||
if field_layout_precedes(store, value, other_index, logical_index, selected) {
|
||||
result += 1
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
params_for :: proc(store: ^Store, value: Type) -> []Field {
|
||||
item, ok := node(store, value)
|
||||
if !ok || item.kind != .Function {
|
||||
@@ -1709,12 +1746,19 @@ size :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 {
|
||||
}
|
||||
offset: u64
|
||||
max_align: u64 = 1
|
||||
if !item.c_layout {
|
||||
for field in fields_for(store, value) {
|
||||
offset += size(field.type, store, selected)
|
||||
max_align = max(max_align, u64(alignment_of(field.type, store, selected)))
|
||||
}
|
||||
} else {
|
||||
for field in fields_for(store, value) {
|
||||
field_align := u64(alignment_of(field.type, store, selected))
|
||||
offset = (offset+field_align-1)/field_align*field_align
|
||||
offset += size(field.type, store, selected)
|
||||
max_align = max(max_align, field_align)
|
||||
}
|
||||
}
|
||||
return (offset+max_align-1)/max_align*max_align
|
||||
case .Union:
|
||||
item, _ := node(store, value)
|
||||
|
||||
+23
-4
@@ -1887,6 +1887,18 @@ Bool_Last :: struct {
|
||||
flag bool
|
||||
}
|
||||
|
||||
Optimized :: struct {
|
||||
prefix u8
|
||||
wide u64
|
||||
suffix u8
|
||||
}
|
||||
|
||||
Source_Order_C :: c_struct {
|
||||
prefix c_uchar
|
||||
wide c_ulong
|
||||
suffix c_uchar
|
||||
}
|
||||
|
||||
Opaque :: opaque
|
||||
Color :: enum {
|
||||
red
|
||||
@@ -1937,12 +1949,19 @@ main func() i32 {
|
||||
if (sizeof!(bool) != 1) return 15
|
||||
if (sizeof!(Bool_First) != 2) return 16
|
||||
if (sizeof!(Bool_Last) != 2) return 17
|
||||
optimized Optimized := Optimized{prefix = 40, wide = 72623859790382856, suffix = 41}
|
||||
if (sizeof!(Optimized) != 16) return 18
|
||||
if (alignof!(Optimized) != 8) return 19
|
||||
if (sizeof!(Source_Order_C) != 24) return 20
|
||||
if (optimized.prefix != 40) return 21
|
||||
if (optimized.wide != 72623859790382856) return 22
|
||||
if (optimized.suffix != 41) return 23
|
||||
first :: make_bool_first()
|
||||
if (!first.flag) return 18
|
||||
if (first.byte != 41) return 19
|
||||
if (!first.flag) return 24
|
||||
if (first.byte != 41) return 25
|
||||
last :: make_bool_last()
|
||||
if (!last.flag) return 20
|
||||
if (last.byte != 42) return 21
|
||||
if (!last.flag) return 26
|
||||
if (last.byte != 42) return 27
|
||||
return 0
|
||||
}
|
||||
`
|
||||
|
||||
Reference in New Issue
Block a user