diff --git a/LANGUAGE.md b/LANGUAGE.md index 348283b..258aa5e 100644 --- a/LANGUAGE.md +++ b/LANGUAGE.md @@ -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 diff --git a/compiler/llvm/llvm.odin b/compiler/llvm/llvm.odin index 4c5182f..29f55ea 100644 --- a/compiler/llvm/llvm.odin +++ b/compiler/llvm/llvm.odin @@ -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..= 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") } diff --git a/compiler/types/types.odin b/compiler/types/types.odin index 81ac2b3..d1e4789 100644 --- a/compiler/types/types.odin +++ b/compiler/types/types.odin @@ -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,11 +1746,18 @@ size :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 { } offset: u64 max_align: u64 = 1 - 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) + 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: diff --git a/compiler_tests.odin b/compiler_tests.odin index de349b0..4520e2e 100644 --- a/compiler_tests.odin +++ b/compiler_tests.odin @@ -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 } `