compiler: Copy initialized edge and allocator values

05d04410c6e6905414e8cf64300c83b94ab4eb6b0d2b276ccc5b1e1c207ea2a4
Alexis Sellier committed ago 1 parent 97ee3683
lib/std/lang/lower.rad +11 -11
2814 2814
    let block = try createBlock(self, "step");
2815 2815
    set self.loopStack[self.loopDepth - 1].continueTarget = block;
2816 2816
    return block;
2817 2817
}
2818 2818
2819 -
/// Allocate a slice of values in the lowering arena.
2820 -
unsafe fn allocVals 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, len: u32) -> *unsafe mut [il::Val] throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2821 -
    return try! alloc::allocRawSlice(self.arena, @sizeOf(il::Val), @alignOf(il::Val), len) as *unsafe mut [il::Val];
2819 +
/// Copy complete values into the lowering arena.
2820 +
unsafe fn copyVals 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, values: &[il::Val]) -> *unsafe mut [il::Val] throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2821 +
    let mut result: *mut [il::Val] = &mut [];
2822 +
    for value in values {
2823 +
        result.append(value, alloc::arenaAllocator(self.arena));
2824 +
    }
2825 +
    return (&mut result[..]) as *unsafe mut [il::Val];
2822 2826
}
2823 2827
2824 2828
/// Allocate a single-value slice in the lowering arena.
2825 2829
unsafe fn allocVal 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, val: il::Val) -> *unsafe mut [il::Val] throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2826 -
    let args = try allocVals(self, 1);
2827 -
    set args[0] = val;
2828 -
    return args;
2830 +
    let values = [val];
2831 +
    return try copyVals(self, &values[..]);
2829 2832
}
2830 2833
2831 2834
////////////////////////
2832 2835
// SSA Var Management //
2833 2836
////////////////////////
6780 6783
6781 6784
    let allocCtxReg = nextReg(self);
6782 6785
    emitLoadW64At(self, allocCtxReg, allocReg, 8);
6783 6786
6784 6787
    let byteSize = emitTypedBinOp(self, il::BinOp::Mul, il::Type::W32, newCapVal, il::Val::Imm(stride as i64));
6785 -
    let args = try allocVals(self, 3);
6786 -
6787 -
    set args[0] = il::Val::Reg(allocCtxReg);
6788 -
    set args[1] = byteSize;
6789 -
    set args[2] = il::Val::Imm(alignment as i64);
6788 +
    let values = [il::Val::Reg(allocCtxReg), byteSize, il::Val::Imm(alignment as i64)];
6789 +
    let args = try copyVals(self, &values[..]);
6790 6790
6791 6791
    let newPtrReg = nextReg(self);
6792 6792
    emit(self, il::Instr::Call {
6793 6793
        retTy: il::Type::W64,
6794 6794
        dst: newPtrReg,
test/tests/slice.append.growth.rad added +62 -0
1 +
//! returns: 0
2 +
3 +
/// Backing bytes for the test allocator.
4 +
static STORAGE: [u8; 8192] = [0; 8192];
5 +
6 +
/// Allocation cursor and call count.
7 +
record State {
8 +
    /// First unused byte.
9 +
    offset: u32,
10 +
    /// Number of growth allocations.
11 +
    calls: u32,
12 +
}
13 +
14 +
/// Persistent allocator state.
15 +
static STATE: State = State { offset: 0, calls: 0 };
16 +
17 +
/// Allocator function and context used by slice append.
18 +
record Allocator: Copy {
19 +
    /// Allocation callback.
20 +
    func: unsafe fn(*unsafe mut opaque, u32, u32) -> *mut opaque,
21 +
    /// Callback state.
22 +
    ctx: *unsafe mut opaque,
23 +
}
24 +
25 +
/// Check the allocator call operands and reserve aligned storage.
26 +
unsafe fn allocate(context: *unsafe mut opaque, size: u32, alignment: u32) -> *mut opaque {
27 +
    let state = context as *unsafe mut State;
28 +
    assert size > 0;
29 +
    assert alignment == 8;
30 +
    assert size % 8 == 0;
31 +
    let offset = (state.offset + alignment - 1) / alignment * alignment;
32 +
    assert offset + size <= STORAGE.len;
33 +
    set state.offset = offset + size;
34 +
    set state.calls += 1;
35 +
    let result: *mut u8 = &mut STORAGE[offset];
36 +
    return result as *mut opaque;
37 +
}
38 +
39 +
/// Check every retained element across repeated capacity transitions.
40 +
@default unsafe fn main() -> u32 {
41 +
    let allocator = Allocator {
42 +
        func: allocate,
43 +
        ctx: (&mut STATE as *unsafe mut State) as *unsafe mut opaque,
44 +
    };
45 +
    let mut values: *mut [u64] = &mut [];
46 +
    let mut expectedCalls: u32 = 0;
47 +
    for index in 0..64 {
48 +
        let capacity = values.cap;
49 +
        if values.len == capacity {
50 +
            set expectedCalls += 1;
51 +
        }
52 +
        values.append(0x100000000 + index as u64, allocator);
53 +
        assert STATE.calls == expectedCalls;
54 +
        assert values.len == index + 1;
55 +
        assert values.cap == ((capacity * 2 | 1) if index == capacity else capacity);
56 +
        for previous in 0..values.len {
57 +
            assert values[previous] == 0x100000000 + previous as u64;
58 +
        }
59 +
    }
60 +
    assert expectedCalls == 7;
61 +
    return 0;
62 +
}