lang: Lower linear references

68c6e83abda6caf4a6c956a68c8ec9cc7ff7c940329607cb52aff9099db47ee9
Alexis Sellier committed ago 1 parent d45ecf68
lib/std/lang/lower.rad +75 -46
3255 3255
            set payload = tvalPayloadAddr(self, base, valOffset),
3256 3256
    };
3257 3257
    return newVar(self, name, ilType(self.low, bindType), mutable, payload);
3258 3258
}
3259 3259
3260 +
/// Bind an identifier from a matched subject.
3260 3261
fn bindMatchVariable(
3261 3262
    self: *mut FnLowerer,
3262 3263
    subject: *MatchSubject,
3263 3264
    binding: *ast::Node,
3264 3265
    mutable: bool
3356 3357
        };
3357 3358
        try bindFieldVariable(self, elem, base, fieldInfo, subject.by, failBlock);
3358 3359
    }
3359 3360
}
3360 3361
3362 +
/// Bind fields from a record pattern.
3361 3363
fn bindRecordPatternFields(self: *mut FnLowerer, subject: *MatchSubject, pattern: *ast::Node, lit: ast::RecordLit, failBlock: BlockId) throws (LowerError) {
3362 3364
    // No fields to bind (e.g., `{ .. }`).
3363 3365
    if lit.fields.len == 0 {
3364 3366
        return;
3365 3367
    }
3855 3857
}
3856 3858
3857 3859
/// Lower a `let-else` statement.
3858 3860
fn lowerLetElse(self: *mut FnLowerer, letElse: ast::LetElse) throws (LowerError) {
3859 3861
    let subject = try lowerMatchSubject(self, letElse.pattern.scrutinee);
3860 -
    let mut mergeBlock: BlockId = undefined;
3862 +
    let mut successBlock: BlockId = undefined;
3861 3863
    if letElse.pattern.guard == nil {
3862 -
        set mergeBlock = try createBlock(self, "merge");
3864 +
        set successBlock = try createBlock(self, "success");
3863 3865
    }
3864 -
    // Else branch executes when the pattern fails to match.
3866 +
    // The else branch executes when the pattern fails to match.
3865 3867
    let elseBlock = try createBlock(self, "else");
3866 3868
3867 -
    // Evaluate pattern and jump to @end or @else.
3868 -
    try lowerPatternMatch(self, &subject, &letElse.pattern, &mut mergeBlock, "merge", elseBlock);
3869 +
    // Evaluate the pattern and jump to @success or @else.
3870 +
    try lowerPatternMatch(
3871 +
        self,
3872 +
        &subject,
3873 +
        &letElse.pattern,
3874 +
        &mut successBlock,
3875 +
        "success",
3876 +
        elseBlock,
3877 +
    );
3878 +
    let mut bindingVar: ?Var = nil;
3879 +
    if let case ast::PatternKind::Binding = letElse.pattern.kind {
3880 +
        set bindingVar = lookupLocalVar(self, letElse.pattern.pattern);
3881 +
    }
3882 +
    let mergeBlock = try createBlock(self, "merge");
3883 +
    try emitJmp(self, mergeBlock);
3884 +
3869 3885
    try switchToAndSeal(self, elseBlock);
3870 -
    try lowerNode(self, letElse.elseBranch);
3886 +
    if try typeOf(self, letElse.elseBranch) == resolver::Type::Never {
3887 +
        try lowerNode(self, letElse.elseBranch);
3888 +
    } else {
3889 +
        let fallback = try lowerExpr(self, letElse.elseBranch);
3890 +
        if let variable = bindingVar {
3891 +
            defVar(self, variable, fallback);
3892 +
        }
3893 +
        try emitJmp(self, mergeBlock);
3894 +
    }
3871 3895
3872 -
    // Continue in @merge. The else branch must diverge, so @merge has only
3873 -
    // one predecessor.
3896 +
    // Continue at @merge after a successful match or value-producing fallback.
3874 3897
    try switchToAndSeal(self, mergeBlock);
3875 3898
}
3876 3899
3877 3900
/// Lower a `while let` loop as a match-driven loop.
3878 3901
fn lowerWhileLet(self: *mut FnLowerer, w: ast::WhileLet) throws (LowerError) {
4050 4073
}
4051 4074
4052 4075
/// Check if a resolver type lowers to an aggregate in memory.
4053 4076
fn isAggregateType(typ: resolver::Type) -> bool {
4054 4077
    match typ {
4055 -
        case resolver::Type::Nominal(_) => {
4056 -
            // Void unions are small enough to pass by value.
4057 -
            return not resolver::isVoidUnion(typ);
4058 -
        }
4059 4078
        case resolver::Type::Slice { .. },
4060 -
             resolver::Type::TraitObject { .. },
4061 -
             resolver::Type::Array(_),
4062 -
             resolver::Type::Nil => return true,
4079 +
             resolver::Type::TraitObject { .. } => return true,
4063 4080
        case resolver::Type::Optional(resolver::Type::Pointer { .. }) => {
4064 4081
            // Optional pointers are scalar due to NPO.
4065 4082
            return false;
4066 4083
        }
4067 4084
        case resolver::Type::Optional(_) => {
4068 4085
            // All other optionals, including optional slices, are aggregates.
4069 4086
            return true;
4070 4087
        }
4088 +
        case resolver::Type::Nominal(_) => {
4089 +
            // Void unions are small enough to pass by value.
4090 +
            return not resolver::isVoidUnion(typ);
4091 +
        }
4092 +
        case resolver::Type::Array(_),
4093 +
             resolver::Type::Nil => return true,
4071 4094
        else => return false,
4072 4095
    }
4073 4096
}
4074 4097
4075 4098
/// Check if a resolver type is a small aggregate that can be
4217 4240
/// Build a `nil` value for an optional type.
4218 4241
///
4219 4242
/// For optional pointers (`?*T`), returns an immediate `0` (null pointer).
4220 4243
/// For other optionals, builds a tagged aggregate with tag set to `0` (absent).
4221 4244
fn buildNilOptional(self: *mut FnLowerer, optType: resolver::Type) -> il::Val throws (LowerError) {
4222 -
    match optType {
4223 -
        case resolver::Type::Optional(resolver::Type::Pointer { .. }) => {
4224 -
            return il::Val::Imm(0);
4225 -
        }
4226 -
        case resolver::Type::Optional(resolver::Type::Slice { item, mutable, .. }) => {
4227 -
            return try buildSliceValue(self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0));
4228 -
        }
4229 -
        case resolver::Type::Optional(inner) => {
4230 -
            let valOffset = resolver::getOptionalValOffset(*inner) as i32;
4231 -
            return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, valOffset);
4232 -
        }
4233 -
        else => throw LowerError::ExpectedOptional,
4245 +
    let case resolver::Type::Optional(inner) = optType
4246 +
        else throw LowerError::ExpectedOptional;
4247 +
    if let case resolver::Type::Pointer { .. } = *inner {
4248 +
        return il::Val::Imm(0);
4249 +
    }
4250 +
    if let case resolver::Type::Slice { item, mutable, .. } = *inner {
4251 +
        return try buildSliceValue(
4252 +
            self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0)
4253 +
        );
4234 4254
    }
4255 +
    let valOffset = resolver::getOptionalValOffset(*inner) as i32;
4256 +
    return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, valOffset);
4235 4257
}
4236 4258
4237 4259
/// Build a result value for throwing functions.
4238 4260
fn buildResult(
4239 4261
    self: *mut FnLowerer,
4256 4278
    ptrVal: il::Val,
4257 4279
    lenVal: il::Val,
4258 4280
    capVal: il::Val
4259 4281
) -> il::Val throws (LowerError) {
4260 4282
    let sliceType = resolver::Type::Slice {
4261 -
        class: types::PointerClass::Owned, item: elemTy, mutable,
4283 +
        class: types::PointerClass::Unsafe,
4284 +
        item: elemTy,
4285 +
        mutable,
4262 4286
    };
4263 4287
    let dst = try emitReserve(self, sliceType);
4264 4288
    let ptrTy = resolver::Type::Pointer {
4265 -
        class: types::PointerClass::Owned, target: elemTy, mutable,
4289 +
        class: types::PointerClass::Unsafe,
4290 +
        target: elemTy,
4291 +
        mutable,
4266 4292
    };
4267 4293
4268 4294
    try emitStore(self, dst, SLICE_PTR_OFFSET, ptrTy, ptrVal);
4269 4295
    try emitStore(self, dst, SLICE_LEN_OFFSET, resolver::Type::U32, lenVal);
4270 4296
    try emitStore(self, dst, SLICE_CAP_OFFSET, resolver::Type::U32, capVal);
4450 4476
    a: il::Reg,
4451 4477
    b: il::Reg,
4452 4478
    offset: i32
4453 4479
) -> il::Val throws (LowerError) {
4454 4480
    let ptrTy = resolver::Type::Pointer {
4455 -
        class: types::PointerClass::Owned, target: elemTy, mutable,
4481 +
        class: types::PointerClass::Unsafe,
4482 +
        target: elemTy,
4483 +
        mutable,
4456 4484
    };
4457 4485
    let ptrEq = try emitEqAtOffset(self, a, b, offset + SLICE_PTR_OFFSET, ptrTy);
4458 4486
    let lenEq = try emitEqAtOffset(self, a, b, offset + SLICE_LEN_OFFSET, resolver::Type::U32);
4459 4487
4460 4488
    return emitTypedBinOp(self, il::BinOp::And, il::Type::W32, ptrEq, lenEq);
4700 4728
    a: il::Reg,
4701 4729
    b: il::Reg,
4702 4730
    offset: i32
4703 4731
) -> il::Val throws (LowerError) {
4704 4732
    match typ {
4705 -
        case resolver::Type::Optional(resolver::Type::Slice { item, mutable, .. }) => {
4706 -
            // Optional slices use null pointer optimization.
4707 -
            return try lowerSliceEq(self, item, mutable, a, b, offset);
4708 -
        }
4733 +
        case resolver::Type::Slice { item, mutable, .. } =>
4734 +
            return try lowerSliceEq(self, item, mutable, a, b, offset),
4709 4735
        case resolver::Type::Optional(inner) => {
4736 +
            if let case resolver::Type::Slice { item, mutable, .. } = *inner {
4737 +
                // Optional slices use null pointer optimization.
4738 +
                return try lowerSliceEq(self, item, mutable, a, b, offset);
4739 +
            }
4710 4740
            return try lowerOptionalEq(self, *inner, a, b, offset);
4711 4741
        }
4712 -
        case resolver::Type::Slice { item, mutable, .. } =>
4713 -
            return try lowerSliceEq(self, item, mutable, a, b, offset),
4714 4742
        case resolver::Type::Array(arr) =>
4715 4743
            return try lowerArrayEq(self, arr, a, b, offset),
4716 4744
        case resolver::Type::Nominal(resolver::NominalType::Record(recInfo)) =>
4717 4745
            return try lowerRecordEq(self, recInfo, a, b, offset),
4718 4746
        case resolver::Type::Nominal(resolver::NominalType::Union(unionInfo)) =>
5090 5118
            else => throw LowerError::UnexpectedNodeValue(arrayNode),
5091 5119
        }
5092 5120
        let result = dataBuilderFinish(&b);
5093 5121
        let alignment = resolver::getTypeLayout(*item).alignment;
5094 5122
        return try lowerConstDataAsSlice(
5095 -
            self, result.values, alignment, not mutable, item, mutable, length
5123 +
            self, result.values, alignment, not mutable,
5124 +
            item, mutable, length
5096 5125
        );
5097 5126
    }
5098 5127
    let data = try lowerExpr(self, arrayNode);
5099 5128
    let count = il::Val::Imm(length as i64);
5100 5129
    return try buildSliceValue(self, item, mutable, data, count, count);
6147 6176
/// String literals are stored as global data and the result is a slice
6148 6177
/// pointing to the data with the appropriate length.
6149 6178
fn lowerStringLit(self: *mut FnLowerer, node: *ast::Node, s: *[u8]) -> il::Val throws (LowerError) {
6150 6179
    // Get the slice type from the node.
6151 6180
    let sliceTy = try typeOf(self, node);
6152 -
    let case resolver::Type::Slice { item, mutable, .. } = sliceTy else {
6153 -
        throw LowerError::ExpectedSliceOrArray;
6154 -
    };
6181 +
    let case resolver::Type::Slice { item, mutable, .. } = sliceTy
6182 +
        else throw LowerError::ExpectedSliceOrArray;
6155 6183
    // Build the string data value.
6156 6184
    let ptr = try! alloc::alloc(
6157 6185
        self.low.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue)
6158 6186
    ) as *mut il::DataValue;
6159 6187
6160 6188
    set *ptr = il::DataValue { item: il::DataItem::Str(s), count: 1 };
6161 6189
6162 -
    return try lowerConstDataAsSlice(self, @sliceOf(ptr, 1), 1, true, item, mutable, s.len);
6190 +
    return try lowerConstDataAsSlice(
6191 +
        self, @sliceOf(ptr, 1), 1, true, item, mutable, s.len
6192 +
    );
6163 6193
}
6164 6194
6165 6195
/// Lower a builtin call expression.
6166 6196
fn lowerBuiltinCall(self: *mut FnLowerer, node: *ast::Node, kind: ast::Builtin, args: *mut [*ast::Node]) -> il::Val throws (LowerError) {
6167 6197
    match kind {
6179 6209
fn lowerSliceOf(self: *mut FnLowerer, node: *ast::Node, args: *mut [*ast::Node]) -> il::Val throws (LowerError) {
6180 6210
    if args.len <> 2 and args.len <> 3 {
6181 6211
        throw LowerError::InvalidArgCount;
6182 6212
    }
6183 6213
    let sliceTy = try typeOf(self, node);
6184 -
    let case resolver::Type::Slice { item, mutable, .. } = sliceTy else {
6185 -
        throw LowerError::ExpectedSliceOrArray;
6186 -
    };
6214 +
    let case resolver::Type::Slice { item, mutable, .. } = sliceTy
6215 +
        else throw LowerError::ExpectedSliceOrArray;
6187 6216
    let ptrVal = try lowerExpr(self, args[0]);
6188 6217
    let lenVal = try lowerExpr(self, args[1]);
6189 6218
    let mut capVal = lenVal;
6190 6219
    if args.len == 3 {
6191 6220
        set capVal = try lowerExpr(self, args[2]);
7287 7316
             resolver::Type::U32 => return il::Type::W32,
7288 7317
        case resolver::Type::I64,
7289 7318
             resolver::Type::U64,
7290 7319
             resolver::Type::Pointer { .. },
7291 7320
             resolver::Type::Slice { .. },
7321 +
             resolver::Type::TraitObject { .. },
7292 7322
             resolver::Type::Array(_),
7293 7323
             resolver::Type::Optional(_),
7294 -
             resolver::Type::Fn(_),
7295 -
             resolver::Type::TraitObject { .. } => return il::Type::W64,
7324 +
             resolver::Type::Fn(_) => return il::Type::W64,
7296 7325
        case resolver::Type::Nominal(_) => {
7297 7326
            if resolver::isVoidUnion(typ) {
7298 7327
                return il::Type::W8;
7299 7328
            }
7300 7329
            return il::Type::W64;
test/tests/coercion.implicit.ril +10 -6
4 4
    store w8 1 %0 0;
5 5
    store w32 42 %0 4;
6 6
    reserve %1 8 4;
7 7
    blit %1 %0 8;
8 8
    load w8 %2 %1 0;
9 -
    br.ne w32 %2 0 @merge1 @else2;
10 -
  @merge1
9 +
    br.ne w32 %2 0 @success1 @else2;
10 +
  @success1
11 11
    sload w32 %3 %1 4;
12 -
    ret %3;
12 +
    jmp @merge3;
13 13
  @else2
14 14
    ret 0;
15 +
  @merge3
16 +
    ret %3;
15 17
}
16 18
17 19
fn w32 $optionalLiftVar(w32 %0) {
18 20
  @entry0
19 21
    reserve %1 8 4;
22 24
    reserve %2 8 4;
23 25
    blit %2 %1 8;
24 26
    reserve %3 8 4;
25 27
    blit %3 %2 8;
26 28
    load w8 %4 %3 0;
27 -
    br.ne w32 %4 0 @merge1 @else2;
28 -
  @merge1
29 +
    br.ne w32 %4 0 @success1 @else2;
30 +
  @success1
29 31
    sload w32 %5 %3 4;
30 -
    ret %5;
32 +
    jmp @merge3;
31 33
  @else2
32 34
    ret 0;
35 +
  @merge3
36 +
    ret %5;
33 37
}
34 38
35 39
fn w64 $optionalLiftReturn(w64 %0, w32 %1) {
36 40
  @entry0
37 41
    reserve %2 8 4;
test/tests/cond.letelse.case.ril +5 -3
1 1
fn w32 $letElseCase(w32 %0) {
2 2
  @entry0
3 -
    br.eq w32 %0 1 @merge1 @else2;
4 -
  @merge1
5 -
    ret 1;
3 +
    br.eq w32 %0 1 @success1 @else2;
4 +
  @success1
5 +
    jmp @merge3;
6 6
  @else2
7 7
    ret 0;
8 +
  @merge3
9 +
    ret 1;
8 10
}
test/tests/cond.letelse.guard.ril +4 -2
2 2
  @entry0
3 3
    br.eq w32 %0 1 @guard2 @else1;
4 4
  @else1
5 5
    ret 0;
6 6
  @guard2
7 -
    br.slt w32 0 %0 @merge3 @else1;
8 -
  @merge3
7 +
    br.slt w32 0 %0 @success3 @else1;
8 +
  @success3
9 +
    jmp @merge4;
10 +
  @merge4
9 11
    ret 1;
10 12
}
test/tests/cond.letelse.mut.ril +6 -4
1 1
fn w32 $letMutElse(w64 %0) {
2 2
  @entry0
3 3
    reserve %1 8 4;
4 4
    blit %1 %0 8;
5 5
    load w8 %2 %1 0;
6 -
    br.ne w32 %2 0 @merge1 @else2;
7 -
  @merge1
6 +
    br.ne w32 %2 0 @success1 @else2;
7 +
  @success1
8 8
    sload w32 %3 %1 4;
9 -
    add w32 %4 %3 1;
10 -
    ret %4;
9 +
    jmp @merge3;
11 10
  @else2
12 11
    ret 0;
12 +
  @merge3
13 +
    add w32 %4 %3 1;
14 +
    ret %4;
13 15
}
test/tests/cond.letelse.optional.ril +5 -3
1 1
fn w32 $letElseOptional(w64 %0) {
2 2
  @entry0
3 -
    br.ne w32 %0 0 @merge1 @else2;
4 -
  @merge1
5 -
    ret 1;
3 +
    br.ne w32 %0 0 @success1 @else2;
4 +
  @success1
5 +
    jmp @merge3;
6 6
  @else2
7 7
    ret 0;
8 +
  @merge3
9 +
    ret 1;
8 10
}
test/tests/opt.slice.npo.ril +8 -7
135 135
    reserve %2 16 8;
136 136
    blit %2 %1 16;
137 137
    reserve %3 16 8;
138 138
    blit %3 %2 16;
139 139
    load w64 %4 %3 0;
140 -
    br.ne w32 %4 0 @merge1 @else2;
141 -
  @merge1
142 -
    load w32 %5 %3 8;
143 -
    br.eq w32 %5 3 @assert.ok4 @assert.fail3;
140 +
    br.ne w32 %4 0 @success1 @else2;
141 +
  @success1
142 +
    jmp @merge3;
144 143
  @else2
145 144
    ret 40;
146 -
  @assert.fail3
145 +
  @merge3
146 +
    load w32 %5 %3 8;
147 +
    br.eq w32 %5 3 @assert.ok5 @assert.fail4;
148 +
  @assert.fail4
147 149
    unreachable;
148 -
  @assert.ok4
150 +
  @assert.ok5
149 151
    ret 0;
150 152
}
151 153
152 154
fn w64 $returnNil(w64 %0) {
153 155
  @entry0
358 360
  @then15
359 361
    ret %7;
360 362
  @merge16
361 363
    ret 0;
362 364
}
363 -