Lower references to array repeat literals

1ecd58a716ef28bcd7fe2f5f7e356049a32e851da42c2ff72178206e9eaaa493
The resolver accepted references to array repeat literals and exposed
them as slices, but `lowerAddressOf` handled only ordinary array
literals. Valid `&[value; count]` and `&mut [value; count]`
expressions consequently reached `UnexpectedNodeValue` during
lowering.

Generalize the existing array-literal-to-slice path to include repeat
literals and reuse ordinary array lowering for constant and runtime
storage.
Alexis Sellier committed ago 1 parent 177c9234
lib/std/lang/lower.rad +36 -64
5031 5031
    }
5032 5032
    // Handle dereference address: `&(*ptr) = ptr`.
5033 5033
    if let case ast::NodeValue::Deref(target) = addr.target.value {
5034 5034
        return try lowerExpr(self, target);
5035 5035
    }
5036 -
    // Handle slice literal: `&[1, 2, 3]`.
5037 -
    if let case ast::NodeValue::ArrayLit(elements) = addr.target.value {
5038 -
        return try lowerSliceLiteral(self, node, addr.target, elements);
5036 +
    // Array literals become slices when their address is taken.
5037 +
    match addr.target.value {
5038 +
        case ast::NodeValue::ArrayLit(_),
5039 +
             ast::NodeValue::ArrayRepeatLit(_) =>
5040 +
        {
5041 +
            return try lowerArrayLiteralSlice(self, node, addr.target);
5042 +
        }
5043 +
        else => {}
5039 5044
    }
5040 5045
    throw LowerError::UnexpectedNodeValue(addr.target);
5041 5046
}
5042 5047
5043 -
/// Lower a slice literal like `&[1, 2, 3]`.
5044 -
/// If all elements are constants, creates static data. Otherwise, allocates
5045 -
/// stack space and stores elements at runtime.
5046 -
fn lowerSliceLiteral(
5048 +
/// Lower an addressed array literal as a slice.
5049 +
fn lowerArrayLiteralSlice(
5047 5050
    self: *mut FnLowerer,
5048 5051
    sliceNode: *ast::Node,
5049 -
    arrayNode: *ast::Node,
5050 -
    elements: *mut [*ast::Node]
5052 +
    arrayNode: *ast::Node
5051 5053
) -> il::Val throws (LowerError) {
5052 -
    // Get the slice type from the address-of expression.
5053 5054
    let sliceTy = try typeOf(self, sliceNode);
5054 5055
    let case resolver::Type::Slice { item, mutable } = sliceTy else {
5055 5056
        throw LowerError::UnexpectedType(&sliceTy);
5056 5057
    };
5057 -
    if elements.len == 0 { // Empty slices don't need to be stored as data.
5058 -
        return try buildSliceValue(self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0));
5058 +
    let arrayTy = try typeOf(self, arrayNode);
5059 +
    let case resolver::Type::Array(arrayInfo) = arrayTy else {
5060 +
        throw LowerError::ExpectedArray;
5061 +
    };
5062 +
    let length = arrayInfo.length;
5063 +
    if length == 0 {
5064 +
        return try buildSliceValue(
5065 +
            self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0)
5066 +
        );
5059 5067
    }
5060 5068
    if resolver::isConstExpr(self.low.resolver, arrayNode) {
5061 -
        let elemLayout = resolver::getTypeLayout(*item);
5062 -
        return try lowerConstSliceLiteral(self, item, mutable, elements, elemLayout);
5063 -
    } else {
5064 -
        return try lowerRuntimeSliceLiteral(self, item, mutable, elements);
5065 -
    }
5066 -
}
5067 -
5068 -
/// Lower a slice literal with all constant elements to static data.
5069 -
fn lowerConstSliceLiteral(
5070 -
    self: *mut FnLowerer,
5071 -
    elemTy: *resolver::Type,
5072 -
    mutable: bool,
5073 -
    elements: *mut [*ast::Node],
5074 -
    elemLayout: resolver::Layout
5075 -
) -> il::Val throws (LowerError) {
5076 -
    // Build data values for all elements using standard data lowering.
5077 -
    let mut b = dataBuilder(self.low.allocator);
5078 -
    for elem in elements {
5079 -
        try lowerConstDataInto(self.low, elem, *elemTy, elemLayout.size, self.fnName, &mut b);
5080 -
    }
5081 -
    let result = dataBuilderFinish(&b);
5082 -
    let readOnly = not mutable;
5083 -
5084 -
    return try lowerConstDataAsSlice(self, result.values, elemLayout.alignment, readOnly, elemTy, mutable, elements.len);
5085 -
}
5086 -
5087 -
/// Lower a slice literal with non-constant elements.
5088 -
fn lowerRuntimeSliceLiteral(
5089 -
    self: *mut FnLowerer,
5090 -
    elemTy: *resolver::Type,
5091 -
    mutable: bool,
5092 -
    elements: *mut [*ast::Node]
5093 -
) -> il::Val throws (LowerError) {
5094 -
    let elemLayout = resolver::getTypeLayout(*elemTy);
5095 -
    let arraySize = elements.len * elemLayout.size;
5096 -
    let arrayReg = nextReg(self);
5097 -
5098 -
    // Reserve stack space for slice elements.
5099 -
    emit(self, il::Instr::Reserve {
5100 -
        dst: arrayReg,
5101 -
        size: il::Val::Imm(arraySize as i64),
5102 -
        alignment: elemLayout.alignment
5103 -
    });
5104 -
    // Store each element.
5105 -
    for elemNode, i in elements {
5106 -
        let elemVal = try lowerExpr(self, elemNode);
5107 -
        let offset = i * elemLayout.size;
5108 -
5109 -
        try emitStore(self, arrayReg, offset as i32, *elemTy, elemVal);
5069 +
        let mut b = dataBuilder(self.low.allocator);
5070 +
        match arrayNode.value {
5071 +
            case ast::NodeValue::ArrayLit(elements) =>
5072 +
                try lowerConstArrayLitInto(self.low, elements, arrayTy, self.fnName, &mut b),
5073 +
            case ast::NodeValue::ArrayRepeatLit(repeat) =>
5074 +
                try lowerConstArrayRepeatInto(self.low, repeat, arrayTy, self.fnName, &mut b),
5075 +
            else => throw LowerError::UnexpectedNodeValue(arrayNode),
5076 +
        }
5077 +
        let result = dataBuilderFinish(&b);
5078 +
        let alignment = resolver::getTypeLayout(*item).alignment;
5079 +
        return try lowerConstDataAsSlice(
5080 +
            self, result.values, alignment, not mutable, item, mutable, length
5081 +
        );
5110 5082
    }
5111 -
    let lenVal = il::Val::Imm(elements.len as i64);
5112 -
5113 -
    return try buildSliceValue(self, elemTy, mutable, il::Val::Reg(arrayReg), lenVal, lenVal);
5083 +
    let data = try lowerExpr(self, arrayNode);
5084 +
    let count = il::Val::Imm(length as i64);
5085 +
    return try buildSliceValue(self, item, mutable, data, count, count);
5114 5086
}
5115 5087
5116 5088
/// Lower the common element pointer computation for subscript operations.
5117 5089
/// Handles both arrays and slices by resolving the container type, extracting
5118 5090
/// the data pointer (for slices), and emitting an [`il::Instr::Elem`] to compute
test/tests/reference.array.repeat.rad added +4 -0
1 +
/// Taking a reference to an array repeat literal yields a static slice.
2 +
fn repeatedSlice() -> *[u32] {
3 +
    return &[(7 as u32); 3];
4 +
}
test/tests/reference.array.repeat.ril added +14 -0
1 +
data $repeatedSlice$literal$0 align 4 {
2 +
    w32 7 * 3;
3 +
}
4 +
5 +
fn w64 $repeatedSlice(w64 %0) {
6 +
  @entry0
7 +
    copy %1 $repeatedSlice$literal$0;
8 +
    reserve %2 16 8;
9 +
    store w64 %1 %2 0;
10 +
    store w32 3 %2 8;
11 +
    store w32 3 %2 12;
12 +
    blit %0 %2 16;
13 +
    ret %0;
14 +
}