compiler: Index exact nominal region applications

b7d3a9567cda99a7374d7826d55e075e7f0da8f9a4a04d548f52f8502fe203db
Alexis Sellier committed ago 1 parent 5db493fa
lib/std/lang/resolver.rad +27 -4
24 24
/// Maximum number of diagnostics recorded.
25 25
export constant MAX_ERRORS: u32 = 64;
26 26
27 27
/// Power-of-two bucket count for interned type lookup chains.
28 28
constant TYPE_BUCKETS: u32 = 1024;
29 -
/// Odd multiplier that combines type cache key components.
29 +
/// Power-of-two bucket count for exact nominal application lookup chains.
30 +
constant APPLICATION_BUCKETS: u32 = 1024;
31 +
/// Odd multiplier that combines cache key components.
30 32
constant CACHE_HASH_PRIME: u32 = 16777619;
31 33
32 34
/// Synthetic function name used when wrapping a bare expression for analysis.
33 35
export constant ANALYZE_EXPR_FN_NAME: *[u8] = "__expr__";
34 36
/// Synthetic function name used when wrapping a block for analysis.
1056 1058
    arguments: *unsafe [*unsafe types::Region],
1057 1059
    /// Stable descriptor for the substituted field or variant view.
1058 1060
    view: *unsafe mut NominalType,
1059 1061
    /// Next application in the resolver cache.
1060 1062
    next: ?*unsafe NominalApplication,
1063 +
    /// Next application in the same lookup bucket.
1064 +
    bucketNext: ?*unsafe NominalApplication,
1061 1065
}
1062 1066
1063 1067
/// Global resolver state.
1064 1068
export record Resolver: 'arena {
1065 1069
    /// Number of symbol identities allocated by this resolver.
1068 1072
    regionScope: ?*RegionScope,
1069 1073
    /// Interned applications of nominal region parameters.
1070 1074
    applications: ?*unsafe NominalApplication,
1071 1075
    /// First entry in the fully resolved suffix of the application list.
1072 1076
    completedApplications: ?*unsafe NominalApplication,
1077 +
    /// Exact application lookup chains, backed by the resolver arena.
1078 +
    applicationBuckets: *unsafe mut [?*unsafe NominalApplication],
1073 1079
    /// Current scope.
1074 1080
    scope: *unsafe mut Scope,
1075 1081
    /// Package scope containing package roots and top-level symbols.
1076 1082
    pkgScope: *unsafe mut Scope,
1077 1083
    /// Stack of loop contexts for nested loops.
1359 1365
1360 1366
/// Intern an exact nominal application before resolving its recursive members.
1361 1367
unsafe fn internNominalApplication 'arena (
1362 1368
    self: &mut Resolver 'arena, base: *unsafe NominalType, map: &RegionSubstitution
1363 1369
) -> *unsafe mut NominalType {
1364 -
    let mut cursor = self.applications;
1370 +
    let mut hash = addressHash(base as u64);
1371 +
    for argument in map.arguments {
1372 +
        let region = argument else panic "internNominalApplication: incomplete map";
1373 +
        set hash = (hash ^ region.id) * CACHE_HASH_PRIME;
1374 +
    }
1375 +
    let bucket = hash & (APPLICATION_BUCKETS - 1);
1376 +
    let mut cursor = self.applicationBuckets[bucket];
1365 1377
    while let applied = cursor {
1366 1378
        if applied.base == base {
1367 1379
            let mut same = true;
1368 1380
            for argument, i in applied.arguments {
1369 1381
                let other = map.arguments[i] else panic "internNominalApplication: missing argument";
1374 1386
            }
1375 1387
            if same {
1376 1388
                return applied.view;
1377 1389
            }
1378 1390
        }
1379 -
        set cursor = applied.next;
1391 +
        set cursor = applied.bucketNext;
1380 1392
    }
1381 1393
    let arguments = try! alloc::allocRawSlice(
1382 1394
        self.arena, @sizeOf(*unsafe types::Region), @alignOf(*unsafe types::Region), map.arguments.len
1383 1395
    ) as *unsafe mut [*unsafe types::Region];
1384 1396
    for argument, i in map.arguments {
1387 1399
    }
1388 1400
    let entry = try! alloc::allocRaw(
1389 1401
        self.arena, @sizeOf(NominalApplication), @alignOf(NominalApplication)
1390 1402
    ) as *unsafe mut NominalApplication;
1391 1403
    let view = allocNominalType(self, NominalType::Application(entry));
1392 -
    set *entry = NominalApplication { base, parameters: map.parameters, arguments, view, next: self.applications };
1404 +
    set *entry = NominalApplication {
1405 +
        base, parameters: map.parameters, arguments, view,
1406 +
        next: self.applications, bucketNext: self.applicationBuckets[bucket],
1407 +
    };
1393 1408
    set self.applications = entry;
1409 +
    set self.applicationBuckets[bucket] = entry;
1394 1410
    return view;
1395 1411
}
1396 1412
1397 1413
/// Check explicit region arguments and intern the applied nominal type.
1398 1414
unsafe fn applyNominalRegions 'arena (
1557 1573
    arena: &'arena mut alloc::Arena,
1558 1574
    storage: ResolverStorage,
1559 1575
    config: Config
1560 1576
) -> Resolver 'arena {
1561 1577
    let case ResolverStorage { nodeData, pkgScope, errors } = storage else panic "expected resolver storage";
1578 +
    let applicationBuckets = try! alloc::allocRawSlice(
1579 +
        arena, @sizeOf(?*unsafe NominalApplication), @alignOf(?*unsafe NominalApplication), APPLICATION_BUCKETS
1580 +
    ) as *unsafe mut [?*unsafe NominalApplication];
1581 +
    for i in 0..applicationBuckets.len {
1582 +
        set applicationBuckets[i] = nil;
1583 +
    }
1562 1584
    let types = try! alloc::allocRawSlice(
1563 1585
        arena, @sizeOf(?*TypeNode), @alignOf(?*TypeNode), TYPE_BUCKETS
1564 1586
    ) as *unsafe mut [?*TypeNode];
1565 1587
    for i in 0..types.len {
1566 1588
        set types[i] = nil;
1601 1623
    return Resolver 'arena {
1602 1624
        symbolCount: 0,
1603 1625
        regionScope: nil,
1604 1626
        applications: nil,
1605 1627
        completedApplications: nil,
1628 +
        applicationBuckets,
1606 1629
        scope: pkgScope,
1607 1630
        pkgScope: pkgScope,
1608 1631
        loopStack: undefined,
1609 1632
        loopDepth: 0,
1610 1633
        currentFn: nil,
lib/std/lang/resolver/tests/regions.rad +29 -0
609 609
            try super::expectNoErrors(&result);
610 610
        }
611 611
    }
612 612
}
613 613
614 +
/// Exact application keys include the base and the ordered region arguments.
615 +
@test unsafe fn testNominalApplicationKeys() throws (testing::TestError) {
616 +
    let mut arena = super::testArena();
617 +
    let storage: 'test = &mut arena in {
618 +
        let mut res = super::testResolver(storage);
619 +
        let result = try super::resolveProgramStr(&mut res,
620 +
            "record N: 'r + 's + Copy { value: u32 } record M: 'r + 's + Copy { value: u32 } fn f 'a 'b (x: N 'a 'b, y: N 'a 'b, z: N 'b 'a, w: N 'a 'a, v: M 'a 'b) {}"
621 +
        );
622 +
        try super::expectNoErrors(&result);
623 +
        let func = try super::getBlockStmt(result.root, 2);
624 +
        let ty = resolver::typeFor(&res, func) else throw testing::TestError::Failed;
625 +
        let case resolver::Type::Fn(info) = ty else throw testing::TestError::Failed;
626 +
        for param, i in info.paramTypes {
627 +
            for other, j in info.paramTypes {
628 +
                assert (param == other) == (i == j or (i < 2 and j < 2));
629 +
            }
630 +
        }
631 +
        let mut count: u32 = 0;
632 +
        let mut cursor = res.applications;
633 +
        while let entry = cursor {
634 +
            assert entry.arguments.len == 2;
635 +
            assert resolver::nominalApplication(entry.view) == entry;
636 +
            set count += 1;
637 +
            set cursor = entry.next;
638 +
        }
639 +
        assert count == 4;
640 +
    }
641 +
}
642 +
614 643
/// Exact arguments share one descriptor and all applications share the source layout.
615 644
@test unsafe fn testNominalRegionApplications() throws (testing::TestError) {
616 645
    let mut testArena19 = super::testArena();
617 646
    let testStorage19: 'test19 = &mut testArena19 in {
618 647
        let mut res = super::testResolver(testStorage19);
test/tests/regions.nominal.cache.rad added +97 -0
1 +
//! returns: 0
2 +
3 +
/// Adjacent swaps and rotation produce all 120 region argument permutations.
4 +
record Node: 'a + 'b + 'c + 'd + 'e + Copy {
5 +
    /// Link with rotated region arguments.
6 +
    rotated: ?*Node 'b 'c 'd 'e 'a,
7 +
    /// Link with the first two region arguments exchanged.
8 +
    swapped: ?*Node 'b 'a 'c 'd 'e,
9 +
    /// Value retained across all applications.
10 +
    value: u32,
11 +
}
12 +
13 +
/// Read through a distinct set of five region identities.
14 +
fn read0 'a 'b 'c 'd 'e (node: Node 'a 'b 'c 'd 'e) -> u32 {
15 +
    assert node.rotated == nil;
16 +
    assert node.swapped == nil;
17 +
    return node.value;
18 +
}
19 +
20 +
/// Read through a distinct set of five region identities.
21 +
fn read1 'a 'b 'c 'd 'e (node: Node 'a 'b 'c 'd 'e) -> u32 {
22 +
    assert node.rotated == nil;
23 +
    assert node.swapped == nil;
24 +
    return node.value;
25 +
}
26 +
27 +
/// Read through a distinct set of five region identities.
28 +
fn read2 'a 'b 'c 'd 'e (node: Node 'a 'b 'c 'd 'e) -> u32 {
29 +
    assert node.rotated == nil;
30 +
    assert node.swapped == nil;
31 +
    return node.value;
32 +
}
33 +
34 +
/// Read through a distinct set of five region identities.
35 +
fn read3 'a 'b 'c 'd 'e (node: Node 'a 'b 'c 'd 'e) -> u32 {
36 +
    assert node.rotated == nil;
37 +
    assert node.swapped == nil;
38 +
    return node.value;
39 +
}
40 +
41 +
/// Read through a distinct set of five region identities.
42 +
fn read4 'a 'b 'c 'd 'e (node: Node 'a 'b 'c 'd 'e) -> u32 {
43 +
    assert node.rotated == nil;
44 +
    assert node.swapped == nil;
45 +
    return node.value;
46 +
}
47 +
48 +
/// Read through a distinct set of five region identities.
49 +
fn read5 'a 'b 'c 'd 'e (node: Node 'a 'b 'c 'd 'e) -> u32 {
50 +
    assert node.rotated == nil;
51 +
    assert node.swapped == nil;
52 +
    return node.value;
53 +
}
54 +
55 +
/// Read through a distinct set of five region identities.
56 +
fn read6 'a 'b 'c 'd 'e (node: Node 'a 'b 'c 'd 'e) -> u32 {
57 +
    assert node.rotated == nil;
58 +
    assert node.swapped == nil;
59 +
    return node.value;
60 +
}
61 +
62 +
/// Read through a distinct set of five region identities.
63 +
fn read7 'a 'b 'c 'd 'e (node: Node 'a 'b 'c 'd 'e) -> u32 {
64 +
    assert node.rotated == nil;
65 +
    assert node.swapped == nil;
66 +
    return node.value;
67 +
}
68 +
69 +
/// Read through a distinct set of five region identities.
70 +
fn read8 'a 'b 'c 'd 'e (node: Node 'a 'b 'c 'd 'e) -> u32 {
71 +
    assert node.rotated == nil;
72 +
    assert node.swapped == nil;
73 +
    return node.value;
74 +
}
75 +
76 +
/// Resolve more exact applications than the cache has buckets.
77 +
fn check 'a 'b 'c 'd 'e (a: &'a u32, b: &'b u32, c: &'c u32, d: &'d u32, e: &'e u32) {
78 +
    let node = Node 'a 'b 'c 'd 'e { rotated: nil, swapped: nil, value: 42 };
79 +
    assert read0(node) == 42;
80 +
    assert read1(node) == 42;
81 +
    assert read2(node) == 42;
82 +
    assert read3(node) == 42;
83 +
    assert read4(node) == 42;
84 +
    assert read5(node) == 42;
85 +
    assert read6(node) == 42;
86 +
    assert read7(node) == 42;
87 +
    assert read8(node) == 42;
88 +
}
89 +
90 +
/// Execute the cache collision workload with live region arguments.
91 +
@default fn main() -> u32 {
92 +
    let value: u32 = 0;
93 +
    let p: 'r = &value in {
94 +
        check(p, p, p, p, p);
95 +
    }
96 +
    return 0;
97 +
}