compiler: Build initialized owned call arguments

97ee3683642eb6879a5ae2c1f4ac059fcfcf675904cc0709958da7a2b4d6b1b2
Alexis Sellier committed ago 1 parent 01c24b42
lib/std/lang/lower.rad +31 -20
6909 6909
    }
6910 6910
    let layout = resolver::getTypeLayout(*allocation.item);
6911 6911
    let size = layout.size if layout.size > 0 else 1;
6912 6912
    let alignment = layout.alignment if layout.alignment > 0 else 1;
6913 6913
    let runtime = allocation.traitInfo.methods[allocation.methodIndex].fnType;
6914 -
    let argOffset: u32 = 1 if requiresReturnParam(runtime) else 0;
6915 -
    let args = try allocVals(self, (4 if slice else 3) + argOffset);
6916 -
    set args[argOffset] = il::Val::Reg(dataReg);
6917 -
    set args[argOffset + 1] = il::Val::Imm(size as i64);
6918 -
    set args[argOffset + 2] = il::Val::Imm(alignment as i64);
6914 +
    let mut args = callArgs(self, runtime);
6915 +
    args.append(il::Val::Reg(dataReg), alloc::arenaAllocator(self.arena));
6916 +
    args.append(il::Val::Imm(size as i64), alloc::arenaAllocator(self.arena));
6917 +
    args.append(il::Val::Imm(alignment as i64), alloc::arenaAllocator(self.arena));
6919 6918
    if slice {
6920 -
        set args[argOffset + 3] = count;
6919 +
        args.append(count, alloc::arenaAllocator(self.arena));
6921 6920
    }
6922 6921
    let result = try emitCallValue(self, il::Val::Reg(functionReg), runtime, args);
6923 6922
    return SessionInitialization { allocation, result, value, count };
6924 6923
}
6925 6924
7033 7032
        offset: slotOffset,
7034 7033
    });
7035 7034
    let methodFnType = traitInfo.methods[methodIndex].fnType;
7036 7035
7037 7036
    // Build args: optional return param slot + data pointer (receiver) + user args.
7038 -
    let argOffset: u32 = 1 if requiresReturnParam(methodFnType) else 0;
7039 -
    let args = try allocVals(self, call.args.len + 1 + argOffset);
7040 -
    set args[argOffset] = il::Val::Reg(dataReg);
7037 +
    let mut args = callArgs(self, methodFnType);
7038 +
    args.append(il::Val::Reg(dataReg), alloc::arenaAllocator(self.arena));
7041 7039
7042 7040
    for arg, i in call.args {
7043 -
        set args[i + 1 + argOffset] = try lowerCallArg(
7041 +
        let value = try lowerCallArg(
7044 7042
            self, arg, i + 1 < call.args.len
7045 7043
        );
7044 +
        args.append(value, alloc::arenaAllocator(self.arena));
7046 7045
    }
7047 7046
    return try emitCallValue(self, il::Val::Reg(fnPtrReg), methodFnType, args);
7048 7047
}
7049 7048
7050 7049
/// Lower a call argument, snapshotting aggregate place expressions before
7063 7062
        }
7064 7063
    }
7065 7064
    return val;
7066 7065
}
7067 7066
7067 +
/// Start an argument table with a placeholder for the hidden return pointer.
7068 +
unsafe fn callArgs 'arena 'phase 'function (
7069 +
    self: &mut FnLowerer 'arena 'phase 'function,
7070 +
    fnInfo: *resolver::FnType
7071 +
) -> *mut [il::Val] where 'arena: 'phase, 'phase: 'function {
7072 +
    let mut args: *mut [il::Val] = &mut [];
7073 +
    if requiresReturnParam(fnInfo) {
7074 +
        args.append(il::Val::Undef, alloc::arenaAllocator(self.arena));
7075 +
    }
7076 +
    return args;
7077 +
}
7078 +
7068 7079
/// Emit a function call with return-parameter and small-aggregate handling.
7069 7080
///
7070 7081
/// All call lowering paths (regular, trait method, standalone method) converge
7071 7082
/// here after preparing the callee value, function type, and argument array.
7072 7083
/// The `args` slice must already include a slot at index zero for the hidden
7073 7084
/// return parameter; that slot is filled by this function.
7074 7085
unsafe fn emitCallValue 'arena 'phase 'function (
7075 7086
    self: &mut FnLowerer 'arena 'phase 'function,
7076 7087
    callee: il::Val,
7077 7088
    fnInfo: *resolver::FnType,
7078 -
    args: *unsafe mut [il::Val],
7089 +
    args: *mut [il::Val],
7079 7090
) -> il::Val throws (LowerError) where 'arena: 'phase, 'phase: 'function {
7080 7091
    let retTy = *fnInfo.returnType;
7081 7092
7082 7093
    if requiresReturnParam(fnInfo) {
7083 7094
        if fnInfo.throwList.len > 0 {
7090 7101
7091 7102
        emit(self, il::Instr::Call {
7092 7103
            retTy: il::Type::W64,
7093 7104
            dst,
7094 7105
            func: callee,
7095 -
            args,
7106 +
            args: (&mut args[..]) as *unsafe mut [il::Val],
7096 7107
        });
7097 7108
        return il::Val::Reg(dst);
7098 7109
    }
7099 7110
    let mut dst: ?il::Reg = nil;
7100 7111
    if retTy <> resolver::Type::Void and retTy <> resolver::Type::Never {
7102 7113
    }
7103 7114
    emit(self, il::Instr::Call {
7104 7115
        retTy: ilType(self.low, retTy),
7105 7116
        dst,
7106 7117
        func: callee,
7107 -
        args,
7118 +
        args: (&mut args[..]) as *unsafe mut [il::Val],
7108 7119
    });
7109 7120
7110 7121
    if retTy == resolver::Type::Never {
7111 7122
        emit(self, il::Instr::Unreachable);
7112 7123
    }
7176 7187
7177 7188
    let qualName = instanceMethodName(self.low, nil, method.concreteTypeName, method.name);
7178 7189
    let fnInfo = method.fullFnType;
7179 7190
7180 7191
    // Build args: optional return param slot + receiver + user args.
7181 -
    let argOffset: u32 = 1 if requiresReturnParam(fnInfo) else 0;
7182 -
    let args = try allocVals(self, call.args.len + 1 + argOffset);
7183 -
    set args[argOffset] = receiverVal;
7192 +
    let mut args = callArgs(self, fnInfo);
7193 +
    args.append(receiverVal, alloc::arenaAllocator(self.arena));
7184 7194
    for arg, i in call.args {
7185 -
        set args[i + 1 + argOffset] = try lowerCallArg(
7195 +
        let value = try lowerCallArg(
7186 7196
            self, arg, i + 1 < call.args.len
7187 7197
        );
7198 +
        args.append(value, alloc::arenaAllocator(self.arena));
7188 7199
    }
7189 7200
    return try emitCallValue(self, il::Val::FnAddr(qualName), fnInfo, args);
7190 7201
}
7191 7202
7192 7203
/// Check if a call is to a compiler intrinsic and lower it directly.
7293 7304
    let calleeTy = try typeOf(self, call.callee);
7294 7305
    let case resolver::Type::Fn(fnInfo) = calleeTy else {
7295 7306
        throw LowerError::ExpectedFunction;
7296 7307
    };
7297 7308
    let callee = try lowerCallee(self, call.callee);
7298 -
    let offset: u32 = 1 if requiresReturnParam(fnInfo) else 0;
7299 -
    let args = try allocVals(self, call.args.len + offset);
7309 +
    let mut args = callArgs(self, fnInfo);
7300 7310
    for arg, i in call.args {
7301 -
        set args[i + offset] = try lowerCallArg(
7311 +
        let value = try lowerCallArg(
7302 7312
            self, arg, i + 1 < call.args.len
7303 7313
        );
7314 +
        args.append(value, alloc::arenaAllocator(self.arena));
7304 7315
    }
7305 7316
7306 7317
    return try emitCallValue(self, callee, fnInfo, args);
7307 7318
}
7308 7319
test/tests/call.table.order.rad added +63 -0
1 +
//! returns: 0
2 +
3 +
/// Aggregate call result.
4 +
record Pair: Copy {
5 +
    /// First argument value.
6 +
    first: u32,
7 +
    /// Second argument value.
8 +
    second: u32,
9 +
}
10 +
11 +
/// Record one evaluated argument.
12 +
fn argument(trace: &mut u32, digit: u32) -> u32 {
13 +
    set *trace = *trace * 10 + digit;
14 +
    return digit;
15 +
}
16 +
17 +
/// Return both arguments through a hidden result pointer.
18 +
fn pair(first: u32, second: u32) -> Pair {
19 +
    return Pair { first, second };
20 +
}
21 +
22 +
/// Combine the receiver with explicit arguments.
23 +
fn (value: &Pair) combine(first: u32, second: u32) -> Pair {
24 +
    return Pair { first: value.first + first, second: value.second + second };
25 +
}
26 +
27 +
/// Return an aggregate result or an error after evaluating both arguments.
28 +
fn checked(first: u32, second: u32) -> Pair throws (u32) {
29 +
    if first == 3 {
30 +
        throw second;
31 +
    }
32 +
    return pair(first, second);
33 +
}
34 +
35 +
/// Check direct, indirect, method, and throwing call argument order.
36 +
@default fn main() -> u32 {
37 +
    let mut trace: u32 = 0;
38 +
    let first = pair(argument(&mut trace, 1), argument(&mut trace, 2));
39 +
    assert trace == 12;
40 +
    let second = first.combine(argument(&mut trace, 3), argument(&mut trace, 4));
41 +
    assert trace == 1234;
42 +
    assert second.first == 4;
43 +
    assert second.second == 6;
44 +
    let call: fn(u32, u32) -> Pair = pair;
45 +
    let third = call(argument(&mut trace, 5), argument(&mut trace, 6));
46 +
    assert trace == 123456;
47 +
    assert third.first == 5;
48 +
    assert third.second == 6;
49 +
    let result = try! checked(argument(&mut trace, 1), argument(&mut trace, 2));
50 +
    assert trace == 12345612;
51 +
    assert result.first == 1;
52 +
    assert result.second == 2;
53 +
    set trace = 0;
54 +
    let mut caught = false;
55 +
    try checked(argument(&mut trace, 3), argument(&mut trace, 4)) catch {
56 +
        set caught = true;
57 +
    };
58 +
    assert caught;
59 +
    assert trace == 34;
60 +
    assert first.first == 1;
61 +
    assert first.second == 2;
62 +
    return 0;
63 +
}