compiler: Calculate optional payload offsets from resolved layouts
560d04a37c182be151ab65d24eaecd213db4bb9a0aad151d29e35c44d3617ef2
1 parent
415fdcf8
lib/std/lang/lower.rad
+5 -5
| 1511 | 1511 | if resolver::typesEqual(target, ty) { |
|
| 1512 | 1512 | if resolver::isNullableType(*inner) { |
|
| 1513 | 1513 | try lowerConstDataPayloadInto(self, node, *inner, dataPrefix, b); |
|
| 1514 | 1514 | } else { |
|
| 1515 | 1515 | let layout = resolver::getTypeLayout(ty); |
|
| 1516 | - | let offset = resolver::getOptionalValOffset(*inner); |
|
| 1516 | + | let offset = resolver::getOptionalValOffset(resolver::getTypeLayout(*inner)); |
|
| 1517 | 1517 | dataBuilderPush(b, il::DataValue { |
|
| 1518 | 1518 | item: il::DataItem::Val { typ: il::Type::W8, val: 1 }, count: 1, |
|
| 1519 | 1519 | }); |
|
| 1520 | 1520 | if offset > 1 { |
|
| 1521 | 1521 | dataBuilderPush(b, il::DataValue { item: il::DataItem::Undef, count: offset - 1 }); |
| 3391 | 3391 | return nil; |
|
| 3392 | 3392 | }; |
|
| 3393 | 3393 | // For optional aggregates, extract the payload from the tagged value. |
|
| 3394 | 3394 | // The tag check already passed, so we know the payload is valid. |
|
| 3395 | 3395 | if let case MatchSubjectKind::OptionalAggregate = subject.kind { |
|
| 3396 | - | let valOffset = resolver::getOptionalValOffset(subject.bindType) as i32; |
|
| 3396 | + | let valOffset = resolver::getOptionalValOffset(resolver::getTypeLayout(subject.bindType)) as i32; |
|
| 3397 | 3397 | return try bindPayloadVariable(self, name, subject.val, subject.bindType, subject.by, valOffset, mutable); |
|
| 3398 | 3398 | } |
|
| 3399 | 3399 | // Declare the variable in the current block's scope. |
|
| 3400 | 3400 | return newVar(self, name, ilType(self.low, subject.bindType), mutable, subject.val); |
|
| 3401 | 3401 | } |
| 4393 | 4393 | // Null-pointer-optimized (NPO) types are used as-is -- valid values are never null. |
|
| 4394 | 4394 | if resolver::isNullableType(*inner) { |
|
| 4395 | 4395 | return val; |
|
| 4396 | 4396 | } |
|
| 4397 | 4397 | let layout = resolver::getTypeLayout(optType); |
|
| 4398 | - | let valOffset = resolver::getOptionalValOffset(*inner) as i32; |
|
| 4398 | + | let valOffset = resolver::getOptionalValOffset(resolver::getTypeLayout(*inner)) as i32; |
|
| 4399 | 4399 | ||
| 4400 | 4400 | return try buildTagged(self, layout, 1, val, *inner, 1, valOffset); |
|
| 4401 | 4401 | } |
|
| 4402 | 4402 | ||
| 4403 | 4403 | /// Build a `nil` value for an optional type. |
| 4413 | 4413 | if let case resolver::Type::Slice { item, mutable, .. } = *inner { |
|
| 4414 | 4414 | return try buildSliceValue( |
|
| 4415 | 4415 | self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0) |
|
| 4416 | 4416 | ); |
|
| 4417 | 4417 | } |
|
| 4418 | - | let valOffset = resolver::getOptionalValOffset(*inner) as i32; |
|
| 4418 | + | let valOffset = resolver::getOptionalValOffset(resolver::getTypeLayout(*inner)) as i32; |
|
| 4419 | 4419 | return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, valOffset); |
|
| 4420 | 4420 | } |
|
| 4421 | 4421 | ||
| 4422 | 4422 | /// Build a result value for throwing functions. |
|
| 4423 | 4423 | unsafe fn buildResult 'arena 'phase 'function ( |
| 4667 | 4667 | inner: resolver::Type, |
|
| 4668 | 4668 | a: il::Reg, |
|
| 4669 | 4669 | b: il::Reg, |
|
| 4670 | 4670 | offset: i32 |
|
| 4671 | 4671 | ) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 4672 | - | let valOffset = resolver::getOptionalValOffset(inner) as i32; |
|
| 4672 | + | let valOffset = resolver::getOptionalValOffset(resolver::getTypeLayout(inner)) as i32; |
|
| 4673 | 4673 | ||
| 4674 | 4674 | // Load tags. |
|
| 4675 | 4675 | let tagA = loadTag(self, a, offset + TVAL_TAG_OFFSET, il::Type::W8); |
|
| 4676 | 4676 | let tagB = loadTag(self, b, offset + TVAL_TAG_OFFSET, il::Type::W8); |
|
| 4677 | 4677 |
lib/std/lang/resolver.rad
+3 -5
| 2059 | 2059 | // Nullable types use null pointer optimization -- no tag byte needed. |
|
| 2060 | 2060 | if isNullableType(inner) { |
|
| 2061 | 2061 | return getTypeLayout(inner); |
|
| 2062 | 2062 | } |
|
| 2063 | 2063 | let innerLayout = getTypeLayout(inner); |
|
| 2064 | - | let tagSize: u32 = 1; |
|
| 2065 | - | let valOffset = mem::alignUp(tagSize, innerLayout.alignment); |
|
| 2064 | + | let valOffset = getOptionalValOffset(innerLayout); |
|
| 2066 | 2065 | let alignment = max(innerLayout.alignment, 1); |
|
| 2067 | 2066 | ||
| 2068 | 2067 | return Layout { |
|
| 2069 | 2068 | size: mem::alignUp(valOffset + innerLayout.size, alignment), |
|
| 2070 | 2069 | alignment, |
|
| 2071 | 2070 | }; |
|
| 2072 | 2071 | } |
|
| 2073 | 2072 | ||
| 2074 | 2073 | /// Get the payload offset within an optional aggregate. |
|
| 2075 | - | export unsafe fn getOptionalValOffset(inner: Type) -> u32 { |
|
| 2076 | - | let innerLayout = getTypeLayout(inner); |
|
| 2077 | - | return mem::alignUp(1, innerLayout.alignment); |
|
| 2074 | + | export fn getOptionalValOffset(inner: Layout) -> u32 { |
|
| 2075 | + | return mem::alignUp(1, inner.alignment); |
|
| 2078 | 2076 | } |
|
| 2079 | 2077 | ||
| 2080 | 2078 | /// Check if a type is optional. |
|
| 2081 | 2079 | export fn isOptionalType(ty: Type) -> bool { |
|
| 2082 | 2080 | match ty { |
lib/std/lang/resolver/tests/regions.rad
+8 -0
| 4 | 4 | use std::testing; |
|
| 5 | 5 | use std::lang::types; |
|
| 6 | 6 | use std::lang::resolver; |
|
| 7 | 7 | use std::lang::ast; |
|
| 8 | 8 | ||
| 9 | + | /// Optional aggregate payloads start at their required alignment after the tag. |
|
| 10 | + | @test fn testOptionalPayloadOffsets() throws (testing::TestError) { |
|
| 11 | + | for alignment in [1 as u32, 2, 4, 8, 16] { |
|
| 12 | + | let layout = resolver::Layout { size: 32, alignment }; |
|
| 13 | + | assert resolver::getOptionalValOffset(layout) == alignment; |
|
| 14 | + | } |
|
| 15 | + | } |
|
| 16 | + | ||
| 9 | 17 | /// Each declaration has a distinct identity within its resolver. |
|
| 10 | 18 | @test unsafe fn testSymbolIdentities() throws (testing::TestError) { |
|
| 11 | 19 | let mut arena = super::testArena(); |
|
| 12 | 20 | let storage: 'test = &mut arena in { |
|
| 13 | 21 | let mut res = super::testResolver(storage); |