//! Machine-only regression: build with aggregate.alignment.start.ras and run
//! with `emulator -machine`. Success writes 0x5555 to the finish device.
//! Typed aggregates may have alignment 1, 2 or 4 even when larger than a word.

record Bytes: Copy { data: [u8; 2057] }
record ByteBox: Copy { anchor: u64, pad: u8, value: Bytes }
record B3: Copy { a: u8, b: u8, c: u8 }
record SmallByteBox: Copy { anchor: u64, pad: u8, value: B3 }
record H3: Copy { a: u16, b: u16, c: u16 }
record SmallHalfBox: Copy { anchor: u64, pad: u16, value: H3 }
record Halves: Copy { data: [u16; 129] }
record HalfBox: Copy { anchor: u64, pad: u16, value: Halves }
record W2: Copy { a: u32, b: u32 }
record SmallWordBox: Copy { anchor: u64, pad: u32, value: W2 }
record Hart: Copy { a: u32, b: u32, c: u32, d: u32 }
record HartBox: Copy { anchor: u64, pad: u32, harts: [Hart; 2] }
union Short: Copy { Some(H3), None }

fn byteReturn(src: *B3) -> B3 { return *src; }
fn halfReturn(src: *H3) -> H3 { return *src; }
fn wordReturn(src: *W2, choose: bool) -> W2 {
    return *src if choose else W2 { a: 9, b: 10 };
}
fn shortReturn(src: *H3) -> Short { return Short::Some(*src); }
fn optionalReturn(src: *H3) -> ?H3 { return *src; }
fn bytesReturn(src: *Bytes, choose: bool) -> Bytes {
    // Both sides must copy into the conditional temporary before returning.
    return *src if choose else *src;
}
fn halvesReturn(src: *Halves) -> Halves { return *src; }
fn hartReturn(src: *Hart) -> Hart { return *src; }
fn sliceReturn(left: *[Hart], right: *[Hart], choose: bool) -> *[Hart] {
    return left if choose else right;
}
fn fallible(src: *Bytes, value: *H3, fail: bool) -> H3 throws (Bytes) {
    if fail { throw *src; }
    return *value;
}
fn propagate(src: *Bytes, value: *H3, fail: bool) -> H3 throws (Bytes) {
    return try fallible(src, value, fail);
}

// Keep more values live than the 23 allocatable registers. Source and
// destination remain live longest, including across this >2047-byte copy.
fn spilledCopy(dst: *mut Bytes, src: *Bytes, values: *[u32]) {
    let v0 = values[0];
    let v1 = values[1];
    let v2 = values[2];
    let v3 = values[3];
    let v4 = values[4];
    let v5 = values[5];
    let v6 = values[6];
    let v7 = values[7];
    let v8 = values[8];
    let v9 = values[9];
    let v10 = values[10];
    let v11 = values[11];
    let v12 = values[12];
    let v13 = values[13];
    let v14 = values[14];
    let v15 = values[15];
    let v16 = values[16];
    let v17 = values[17];
    let v18 = values[18];
    let v19 = values[19];
    let v20 = values[20];
    let v21 = values[21];
    let v22 = values[22];
    let v23 = values[23];
    set *dst = *src;
    assert v0 + v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11
        + v12 + v13 + v14 + v15 + v16 + v17 + v18 + v19 + v20 + v21 + v22 + v23 == 276;
    for i in 0..2057 {
        assert dst.data[i] == src.data[i];
    }
}

fn smallCopies() {
    // The enclosing locals are aligned, but the fields deliberately aren't
    // aligned to eight. Returning these must neither overread nor issue LD.
    let mut bytes: SmallByteBox = undefined;
    let mut halves: SmallHalfBox = undefined;
    let mut words: SmallWordBox = undefined;
    set bytes.pad = 71;
    set halves.pad = 72;
    set words.pad = 73;
    set bytes.value = B3 { a: 0x81, b: 0xFE, c: 0xA3 };
    set halves.value = H3 { a: 0x8123, b: 0xFEDC, c: 0xA345 };
    set words.value = W2 { a: 0x81234567, b: 0xFEDCBA98 };
    let b = byteReturn(&bytes.value);
    let h = halfReturn(&halves.value);
    let w = wordReturn(&words.value, true);
    assert b.a == 0x81 and b.b == 0xFE and b.c == 0xA3;
    assert h.a == 0x8123 and h.b == 0xFEDC and h.c == 0xA345;
    assert w.a == 0x81234567 and w.b == 0xFEDCBA98;
    set words.value = wordReturn(&words.value, false);
    assert words.value.a == 9 and words.value.b == 10;
    let wrapped = shortReturn(&halves.value);
    match wrapped {
        case Short::Some(value) => { assert value == h; },
        case Short::None => panic "lost halfword payload",
    }
    let present = optionalReturn(&halves.value) else panic "lost optional payload";
    assert present == h;
    assert bytes.pad == 71 and halves.pad == 72 and words.pad == 73;
}

fn largeCopies() {
    let mut source: ByteBox = undefined;
    let mut target: ByteBox = undefined;
    set source.pad = 81;
    set target.pad = 82;
    for i in 0..2057 { set source.value.data[i] = i as u8; }
    set target.value = bytesReturn(&source.value, true);
    set target.value = bytesReturn(&target.value, false);
    let mut values: [u32; 24] = undefined;
    for i in 0..24 { set values[i] = i as u32; }
    spilledCopy(&mut target.value, &source.value, &values[..]);
    assert source.pad == 81 and target.pad == 82;
    set target.value.data[0] = 255;
    assert source.value.data[0] == 0;

    let mut halves: HalfBox = undefined;
    let mut copied: HalfBox = undefined;
    set copied.pad = 83;
    for i in 0..129 { set halves.value.data[i] = (i + 0x8000) as u16; }
    set copied.value = halvesReturn(&halves.value);
    for i in 0..129 { assert copied.value.data[i] == halves.value.data[i]; }
    assert copied.pad == 83;

    let value = H3 { a: 11, b: 22, c: 33 };
    let success = try! propagate(&source.value, &value, false);
    assert success == value;
    let mut caught = false;
    let _ = try propagate(&source.value, &value, true) catch error {
        assert error.data[0] == 0 and error.data[1023] == 255 and error.data[2056] == 8;
        set caught = true;
    };
    assert caught;
}

fn hartCopies() {
    let mut platform: HartBox = undefined;
    set platform.pad = 91;
    set platform.harts[0] = Hart { a: 1, b: 2, c: 3, d: 4 };
    set platform.harts[1] = hartReturn(&platform.harts[0]);
    let first = sliceReturn(&platform.harts[..1], &platform.harts[1..], true);
    let second = sliceReturn(&platform.harts[..1], &platform.harts[1..], false);
    assert first.len == 1 and second.len == 1;
    assert first[0] == second[0] and second[0].d == 4;
    set platform.harts[1].d = 99;
    assert platform.harts[0].d == 4 and platform.pad == 91;
}

@default fn main() -> i32 {
    smallCopies();
    largeCopies();
    hartCopies();
    return 0;
}
