compiler: Copy initialized edge and allocator values
05d04410c6e6905414e8cf64300c83b94ab4eb6b0d2b276ccc5b1e1c207ea2a4
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 | + | } |