Skip to content

Commit 73b6641

Browse files
LiedtkeV8-internal LUCI CQ
authored andcommitted
[wasm] Implement subtyping between abstract and index types
Change-Id: I2fff66108fdd99b64fdf26c9643879fb8c075c29 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8447376 Commit-Queue: Matthias Liedtke <[email protected]> Reviewed-by: Manos Koukoutos <[email protected]>
1 parent 95e6420 commit 73b6641

File tree

2 files changed

+179
-60
lines changed

2 files changed

+179
-60
lines changed

Sources/Fuzzilli/FuzzIL/TypeSystem.swift

Lines changed: 79 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,7 @@ public struct ILType: Hashable {
241241
}
242242

243243
static func wasmSelfReference() -> ILType {
244-
let type = wasmTypeDef()
245-
type.wasmTypeDefinition!.description = .selfReference
246-
return type
244+
wasmTypeDef(description: .selfReference)
247245
}
248246

249247
static func wasmRef(_ kind: WasmReferenceType.Kind, nullability: Bool) -> ILType {
@@ -252,7 +250,7 @@ public struct ILType: Hashable {
252250
wasmExt: WasmReferenceType(kind, nullability: nullability)))
253251
}
254252

255-
static func wasmIndexRef(_ desc: WasmTypeDescription?, nullability: Bool) -> ILType {
253+
static func wasmIndexRef(_ desc: WasmTypeDescription, nullability: Bool) -> ILType {
256254
return wasmRef(.Index(UnownedWasmTypeDescription(desc)), nullability: nullability)
257255
}
258256

@@ -1325,7 +1323,7 @@ public class WasmTypeDefinition: WasmTypeExtension {
13251323

13261324
func getReferenceTypeTo(nullability: Bool) -> ILType {
13271325
assert(description != nil)
1328-
return .wasmIndexRef(description, nullability: nullability)
1326+
return .wasmIndexRef(description!, nullability: nullability)
13291327
}
13301328
}
13311329

@@ -1455,23 +1453,72 @@ public class WasmReferenceType: WasmTypeExtension {
14551453
case Index(UnownedWasmTypeDescription = UnownedWasmTypeDescription())
14561454
case Abstract(WasmAbstractHeapType)
14571455

1458-
func subsumes(_ other: Kind) -> Bool {
1456+
func union(_ other: Self) -> Self? {
14591457
switch self {
14601458
case .Index(let desc):
14611459
switch other {
14621460
case .Index(let otherDesc):
1463-
return desc.get() == nil || desc.get() == otherDesc.get()
1464-
case .Abstract(_):
1465-
return false
1461+
if desc.get() == nil || otherDesc.get() == nil {
1462+
return .Index(.init())
1463+
}
1464+
if desc.get() == otherDesc.get() {
1465+
return self
1466+
}
1467+
if let abstract = desc.get()?.abstractHeapSupertype,
1468+
let otherAbstract = otherDesc.get()?.abstractHeapSupertype,
1469+
let upperBound = abstract.union(otherAbstract) {
1470+
return .Abstract(upperBound)
1471+
}
1472+
case .Abstract(let otherAbstract):
1473+
if let abstractSuper = desc.get()?.abstractHeapSupertype,
1474+
let upperBound = abstractSuper.union(otherAbstract) {
1475+
return .Abstract(upperBound)
1476+
}
14661477
}
14671478
case .Abstract(let heapType):
14681479
switch other {
1469-
case .Index(_):
1470-
return false
1480+
case .Index(let otherDesc):
1481+
if let otherAbstract = otherDesc.get()?.abstractHeapSupertype,
1482+
let upperBound = heapType.union(otherAbstract) {
1483+
return .Abstract(upperBound)
1484+
}
1485+
case .Abstract(let otherHeapType):
1486+
if let upperBound = heapType.union(otherHeapType) {
1487+
return .Abstract(upperBound)
1488+
}
1489+
}
1490+
}
1491+
return nil
1492+
}
1493+
1494+
func intersection(_ other: Self) -> Self? {
1495+
switch self {
1496+
case .Index(let desc):
1497+
switch other {
1498+
case .Index(let otherDesc):
1499+
if desc.get() == otherDesc.get() || desc.get() == nil || otherDesc.get() == nil {
1500+
return .Index(desc)
1501+
}
1502+
case .Abstract(let otherAbstract):
1503+
if let abstractSuper = desc.get()?.abstractHeapSupertype,
1504+
otherAbstract.subsumes(abstractSuper) {
1505+
return self
1506+
}
1507+
}
1508+
case .Abstract(let heapType):
1509+
switch other {
1510+
case .Index(let otherDesc):
1511+
if let otherAbstract = otherDesc.get()?.abstractHeapSupertype,
1512+
heapType.subsumes(otherAbstract) {
1513+
return other
1514+
}
14711515
case .Abstract(let otherHeapType):
1472-
return heapType.subsumes(otherHeapType)
1516+
if let lowerBound = heapType.intersection(otherHeapType) {
1517+
return .Abstract(lowerBound)
1518+
}
14731519
}
14741520
}
1521+
return nil
14751522
}
14761523
}
14771524
var kind: Kind
@@ -1498,61 +1545,27 @@ public class WasmReferenceType: WasmTypeExtension {
14981545

14991546
override func subsumes(_ other: WasmTypeExtension) -> Bool {
15001547
guard let other = other as? WasmReferenceType else { return false }
1501-
return self.kind.subsumes(other.kind) && (self.nullability || !other.nullability)
1548+
return self.kind.union(other.kind) == self.kind && (self.nullability || !other.nullability)
15021549
}
15031550

15041551
override func union(_ other: WasmTypeExtension) -> WasmTypeExtension? {
15051552
guard let other = other as? WasmReferenceType else { return nil }
1506-
// The union is nullable if either of the two types input types is nullable.
1507-
let nullability = self.nullability || other.nullability
1508-
// TODO(gc): Add subtyping between different index types, different abstract types and
1509-
// between index and abstract types.
1510-
switch self.kind {
1511-
case .Index(let desc):
1512-
switch other.kind {
1513-
case .Index(let otherDesc):
1514-
return desc.get() == otherDesc.get() ? WasmReferenceType(.Index(desc), nullability: nullability) : nil
1515-
case .Abstract(_):
1516-
return nil
1517-
}
1518-
case .Abstract(let heapType):
1519-
switch other.kind {
1520-
case .Index(_):
1521-
return nil
1522-
case .Abstract(let otherHeapType):
1523-
if let upperBound = heapType.union(otherHeapType) {
1524-
return WasmReferenceType(.Abstract(upperBound), nullability: nullability)
1525-
}
1526-
return nil
1527-
}
1553+
if let kind = self.kind.union(other.kind) {
1554+
// The union is nullable if either of the two types input types is nullable.
1555+
let nullability = self.nullability || other.nullability
1556+
return WasmReferenceType(kind, nullability: nullability)
15281557
}
1558+
return nil
15291559
}
15301560

15311561
override func intersection(_ other: WasmTypeExtension) -> WasmTypeExtension? {
15321562
guard let other = other as? WasmReferenceType else { return nil }
1533-
// The intersection is nullable if both are nullable.
1534-
let nullability = self.nullability && other.nullability
1535-
// TODO(gc): Add subtyping between different index types, different abstract types and
1536-
// between index and abstract types.
1537-
switch self.kind {
1538-
case .Index(let desc):
1539-
switch other.kind {
1540-
case .Index(let otherDesc):
1541-
return desc.get() == otherDesc.get() ? WasmReferenceType(.Index(desc), nullability: nullability) : nil
1542-
case .Abstract(_):
1543-
return nil
1544-
}
1545-
case .Abstract(let heapType):
1546-
switch other.kind {
1547-
case .Index(_):
1548-
return nil
1549-
case .Abstract(let otherHeapType):
1550-
if let lowerBound = heapType.intersection(otherHeapType) {
1551-
return WasmReferenceType(.Abstract(lowerBound), nullability: nullability)
1552-
}
1553-
return nil
1554-
}
1563+
if let kind = self.kind.intersection(other.kind) {
1564+
// The intersection is nullable if both are nullable.
1565+
let nullability = self.nullability && other.nullability
1566+
return WasmReferenceType(kind, nullability: nullability)
15551567
}
1568+
return nil
15561569
}
15571570

15581571
override public func hash(into hasher: inout Hasher) {
@@ -1935,9 +1948,15 @@ func => (parameters: [ILType], returnTypes: [ILType]) -> WasmSignature {
19351948
class WasmTypeDescription: Hashable {
19361949
static let selfReference = WasmTypeDescription(typeGroupIndex: -1)
19371950
public let typeGroupIndex: Int
1951+
// The "closest" super type that is an abstract type (.WasmArray for arrays, .WasmStruct for
1952+
// structs). It is nil for unresolved forward/self references for which the concrete abstract
1953+
// super type is still undecided.
1954+
public let abstractHeapSupertype: WasmAbstractHeapType?
19381955

1939-
init(typeGroupIndex: Int) {
1956+
// TODO(gc): We will also need to support subtyping of struct and array types at some point.
1957+
init(typeGroupIndex: Int, superType: WasmAbstractHeapType? = nil) {
19401958
self.typeGroupIndex = typeGroupIndex
1959+
self.abstractHeapSupertype = superType
19411960
}
19421961

19431962
static func == (lhs: WasmTypeDescription, rhs: WasmTypeDescription) -> Bool {
@@ -1956,7 +1975,7 @@ class WasmArrayTypeDescription: WasmTypeDescription {
19561975
init(elementType: ILType, mutability: Bool, typeGroupIndex: Int) {
19571976
self.elementType = elementType
19581977
self.mutability = mutability
1959-
super.init(typeGroupIndex: typeGroupIndex)
1978+
super.init(typeGroupIndex: typeGroupIndex, superType: .WasmArray)
19601979
}
19611980
}
19621981

@@ -1975,6 +1994,6 @@ class WasmStructTypeDescription: WasmTypeDescription {
19751994

19761995
init(fields: [Field], typeGroupIndex: Int) {
19771996
self.fields = fields
1978-
super.init(typeGroupIndex: typeGroupIndex)
1997+
super.init(typeGroupIndex: typeGroupIndex, superType: .WasmStruct)
19791998
}
19801999
}

Tests/FuzzilliTests/ProgramBuilderTest.swift

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2744,4 +2744,104 @@ class ProgramBuilderTests: XCTestCase {
27442744

27452745
XCTAssertEqual(typesB.count, 1)
27462746
}
2747+
2748+
func testWasmGCSubtyping() {
2749+
let env = JavaScriptEnvironment()
2750+
let config = Configuration(logLevel: .error)
2751+
let fuzzer = makeMockFuzzer(config: config, environment: env)
2752+
let b = fuzzer.makeBuilder()
2753+
2754+
let typeGroupA = b.wasmDefineTypeGroup {
2755+
[b.wasmDefineArrayType(elementType: .wasmi32, mutability: true),
2756+
b.wasmDefineStructType(fields: [.init(type: .wasmi64, mutability: false)], indexTypes: [])]
2757+
}
2758+
let arrayDefI32 = typeGroupA[0]
2759+
let structDef = typeGroupA[1]
2760+
let arrayDefI32B = b.wasmDefineTypeGroup {
2761+
[b.wasmDefineArrayType(elementType: .wasmi32, mutability: true)]
2762+
}[0]
2763+
2764+
let arrayDefI32Type = b.type(of: arrayDefI32)
2765+
let structDefType = b.type(of: structDef)
2766+
let arrayDefI32BType = b.type(of: arrayDefI32B)
2767+
2768+
XCTAssert(arrayDefI32Type.Is(.wasmTypeDef()))
2769+
XCTAssert(structDefType.Is(.wasmTypeDef()))
2770+
XCTAssert(arrayDefI32BType.Is(.wasmTypeDef()))
2771+
// The type of a type definition may not be confused with the type of an instance of such
2772+
// struct / array.
2773+
XCTAssertFalse(arrayDefI32Type.Is(.wasmGenericRef))
2774+
XCTAssertFalse(structDefType.Is(.wasmGenericRef))
2775+
XCTAssertFalse(arrayDefI32BType.Is(.wasmGenericRef))
2776+
2777+
b.buildWasmModule { wasmModule in
2778+
wasmModule.addWasmFunction(with: [.wasmi32] => []) { function, label, args in
2779+
let arrayI32 = function.wasmArrayNewFixed(arrayType: arrayDefI32, elements: [])
2780+
let arrayI32Type = b.type(of: arrayI32)
2781+
XCTAssert(arrayI32Type.Is(.wasmRef(.Index(), nullability: true)))
2782+
XCTAssert(arrayI32Type.Is(.wasmRef(.Index(), nullability: false)))
2783+
XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmArray), nullability: true)))
2784+
XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmArray), nullability: false)))
2785+
XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmEq), nullability: true)))
2786+
XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmEq), nullability: false)))
2787+
XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmAny), nullability: true)))
2788+
XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmAny), nullability: false)))
2789+
XCTAssertFalse(arrayI32Type.Is(.wasmRef(.Abstract(.WasmStruct), nullability: true)))
2790+
XCTAssertFalse(arrayI32Type.Is(.wasmRef(.Abstract(.WasmStruct), nullability: false)))
2791+
XCTAssertFalse(arrayI32Type.Is(.wasmRef(.Abstract(.WasmExn), nullability: false)))
2792+
2793+
let arrayI32B = function.wasmArrayNewFixed(arrayType: arrayDefI32B, elements: [])
2794+
let arrayI32BType = b.type(of: arrayI32B)
2795+
XCTAssertFalse(arrayI32BType.Is(arrayI32Type))
2796+
XCTAssertFalse(arrayI32Type.Is(arrayI32BType))
2797+
let refArrayType = ILType.wasmRef(.Abstract(.WasmArray), nullability: false)
2798+
XCTAssertEqual(arrayI32Type.union(with: arrayI32BType), refArrayType)
2799+
XCTAssertEqual(arrayI32BType.union(with: arrayI32Type), refArrayType)
2800+
XCTAssertEqual(arrayI32Type.intersection(with: arrayI32BType), .nothing)
2801+
XCTAssertEqual(arrayI32BType.intersection(with: arrayI32Type), .nothing)
2802+
2803+
let structVar = function.wasmStructNewDefault(structType: structDef)
2804+
let structType = b.type(of: structVar)
2805+
XCTAssert(structType.Is(.wasmRef(.Index(), nullability: true)))
2806+
XCTAssert(structType.Is(.wasmRef(.Index(), nullability: false)))
2807+
XCTAssert(structType.Is(.wasmRef(.Abstract(.WasmStruct), nullability: false)))
2808+
XCTAssert(structType.Is(.wasmRef(.Abstract(.WasmEq), nullability: false)))
2809+
XCTAssert(structType.Is(.wasmRef(.Abstract(.WasmAny), nullability: false)))
2810+
XCTAssertFalse(structType.Is(.wasmRef(.Abstract(.WasmArray), nullability: true)))
2811+
XCTAssertFalse(structType.Is(.wasmRef(.Abstract(.WasmArray), nullability: false)))
2812+
XCTAssertFalse(structType.Is(.wasmRef(.Abstract(.WasmExn), nullability: false)))
2813+
2814+
let refEqType = ILType.wasmRef(.Abstract(.WasmEq), nullability: false)
2815+
XCTAssertEqual(structType.union(with: arrayI32Type), refEqType)
2816+
XCTAssertEqual(arrayI32Type.union(with: structType), refEqType)
2817+
XCTAssertEqual(structType.intersection(with: arrayI32Type), .nothing)
2818+
XCTAssertEqual(arrayI32Type.intersection(with: structType), .nothing)
2819+
2820+
let i31 = function.wasmRefI31(function.consti32(42))
2821+
let i31Type = b.type(of: i31)
2822+
XCTAssertFalse(i31Type.Is(.wasmRef(.Index(), nullability: true)))
2823+
XCTAssert(i31Type.Is(.wasmRef(.Abstract(.WasmEq), nullability: false)))
2824+
XCTAssert(i31Type.Is(.wasmRef(.Abstract(.WasmAny), nullability: false)))
2825+
XCTAssertFalse(i31Type.Is(.wasmRef(.Abstract(.WasmArray), nullability: false)))
2826+
XCTAssertFalse(i31Type.Is(.wasmRef(.Abstract(.WasmStruct), nullability: false)))
2827+
XCTAssertFalse(i31Type.Is(.wasmRef(.Abstract(.WasmExn), nullability: false)))
2828+
2829+
XCTAssertEqual(structType.union(with: i31Type), refEqType)
2830+
XCTAssertEqual(arrayI32Type.union(with: i31Type), refEqType)
2831+
XCTAssertEqual(i31Type.union(with: refEqType), refEqType)
2832+
XCTAssertEqual(refArrayType.union(with: i31Type), refEqType)
2833+
let refStructType = ILType.wasmRef(.Abstract(.WasmStruct), nullability: false)
2834+
XCTAssertEqual(i31Type.union(with: refStructType), refEqType)
2835+
2836+
XCTAssertEqual(i31Type.intersection(with: refEqType), i31Type)
2837+
XCTAssertEqual(refEqType.intersection(with: i31Type), i31Type)
2838+
let refNone = ILType.wasmRef(.Abstract(.WasmNone), nullability: false)
2839+
XCTAssertEqual(i31Type.intersection(with: refArrayType), refNone)
2840+
XCTAssertEqual(refStructType.intersection(with: i31Type), refNone)
2841+
XCTAssertEqual(i31Type.intersection(with: .wasmExnRef), .nothing)
2842+
2843+
return []
2844+
}
2845+
}
2846+
}
27472847
}

0 commit comments

Comments
 (0)