casting.js (5927B)
1 // Test all possible casting combinations of the following graph: 2 // 3 // A1 A2 4 // | | 5 // B1 B2 6 // | \ | 7 // C1 C2 C3 8 // | \ | 9 // D1 D2 D3 10 // | \ | 11 // E1 E2 E3 12 // | \ | 13 // F1 F2 F3 14 // | \ | 15 // G1 G2 G3 16 // | \ | 17 // H1 H2 H3 18 // | \ | 19 // I1 I2 I3 20 // | \ | 21 // J1 J2 J3 22 // 23 // NOTE: this object needs to be ordered such that parent definitions come 24 // before children. Note also, to be properly effective, these trees need to 25 // have a depth of at least MinSuperTypeVectorLength as defined in 26 // wasm/WasmCodegenConstants.h; keep it in sync with that. 27 const TYPES = { 28 'A1': { super: null }, 29 'A2': { super: null }, 30 'B1': { super: 'A1' }, 31 'B2': { super: 'A2' }, 32 'C1': { super: 'B1' }, 33 'C2': { super: 'B1', final: true }, 34 'C3': { super: 'B2' }, 35 'D1': { super: 'C1' }, 36 'D2': { super: 'C1', final: true }, 37 'D3': { super: 'C3' }, 38 'E1': { super: 'D1' }, 39 'E2': { super: 'D1', final: true }, 40 'E3': { super: 'D3' }, 41 'F1': { super: 'E1' }, 42 'F2': { super: 'E1', final: true }, 43 'F3': { super: 'E3' }, 44 'G1': { super: 'F1' }, 45 'G2': { super: 'F1', final: true }, 46 'G3': { super: 'F3' }, 47 'H1': { super: 'G1' }, 48 'H2': { super: 'G1', final: true }, 49 'H3': { super: 'G3' }, 50 'I1': { super: 'H1' }, 51 'I2': { super: 'H1', final: true }, 52 'I3': { super: 'H3' }, 53 'J1': { super: 'I1' }, 54 'J2': { super: 'I1', final: true }, 55 'J3': { super: 'I3' }, 56 }; 57 58 // The oracle method for testing the declared subtype relationship. 59 function manualIsSubtype(types, subType, superType) { 60 while (subType !== superType && subType.super !== null) { 61 subType = types[subType.super]; 62 } 63 return subType === superType; 64 } 65 66 function makeAnyModule(types) { 67 let typeSection = ``; 68 let funcSection = ``; 69 for (let name in types) { 70 let type = types[name]; 71 typeSection += `(type \$${name} (sub ${type.final ? "final" : ""} ${type.super ? "$" + type.super : ""} (struct)))\n`; 72 funcSection += `(func (export "new${name}") (result externref) 73 struct.new_default \$${name} 74 extern.convert_any 75 )\n`; 76 funcSection += `(func (export "is${name}") (param externref) (result i32) 77 local.get 0 78 any.convert_extern 79 ref.test (ref \$${name}) 80 )\n`; 81 } 82 // NOTE: we place all types in a single recursion group to prevent 83 // canonicalization from combining them into a single type. 84 return `(module 85 (rec ${typeSection}) 86 ${funcSection} 87 )`; 88 } 89 90 function makeFuncModule(types) { 91 let typeSection = ``; 92 let funcSection = ``; 93 for (let name in types) { 94 let type = types[name]; 95 typeSection += `(type \$${name} (sub ${type.final ? "final" : ""} ${type.super ? "$" + type.super : ""} (func)))\n`; 96 funcSection += `(func \$f${name} (type \$${name}))\n`; 97 funcSection += `(func (export "new${name}") (result funcref) 98 ref.func \$f${name} 99 )\n`; 100 funcSection += `(func (export "is${name}") (param funcref) (result i32) 101 local.get 0 102 ref.test (ref \$${name}) 103 )\n`; 104 } 105 106 const elemSection = `(elem declare func ${Object.keys(types).map(name => `\$f${name}`).join(" ")})`; 107 108 // NOTE: we place all types in a single recursion group to prevent 109 // canonicalization from combining them into a single type. 110 return `(module 111 (rec ${typeSection}) 112 ${elemSection} 113 ${funcSection} 114 )`; 115 } 116 117 function testAllCasts(types, moduleText) { 118 // Instantiate the module and acquire the testing methods 119 let exports = wasmEvalText(moduleText).exports; 120 for (let name in types) { 121 let type = types[name]; 122 type['new'] = exports[`new${name}`]; 123 type['is'] = exports[`is${name}`]; 124 } 125 126 // Test every combination of types, comparing the oracle method against the 127 // JIT'ed method. 128 for (let subTypeName in types) { 129 let subType = types[subTypeName]; 130 for (let superTypeName in types) { 131 let superType = types[superTypeName]; 132 assertEq( 133 manualIsSubtype(types, subType, superType) ? 1 : 0, 134 superType['is'](subType['new']())); 135 } 136 } 137 } 138 testAllCasts(TYPES, makeAnyModule(TYPES)); 139 testAllCasts(TYPES, makeFuncModule(TYPES)); 140 141 // Test that combinations of ref.test and ref.cast compile correctly. 142 // (These can be optimized together.) 143 { 144 const { make, test1, test2, test3, test4 } = wasmEvalText(`(module 145 (type $a (array i32)) 146 (func (export "make") (param i32) (result anyref) 147 local.get 0 148 local.get 0 149 array.new_fixed $a 2 150 ) 151 (func (export "test1") (param anyref) (result i32) 152 (if (ref.test (ref $a) (local.get 0)) 153 (then 154 (ref.cast (ref $a) (local.get 0)) 155 (array.get $a (i32.const 0)) 156 return 157 ) 158 ) 159 i32.const -1 160 ) 161 (func (export "test2") (param anyref) (result i32) 162 (if (ref.test (ref $a) (local.get 0)) 163 (then) 164 (else 165 (ref.cast (ref $a) (local.get 0)) 166 (array.get $a (i32.const 0)) 167 return 168 ) 169 ) 170 i32.const -1 171 ) 172 (func (export "test3") (param anyref) (result i32) 173 (if (ref.test (ref $a) (local.get 0)) 174 (then 175 (if (ref.test (ref $a) (local.get 0)) 176 (then) 177 (else 178 (ref.cast (ref $a) (local.get 0)) 179 (array.get $a (i32.const 0)) 180 return 181 ) 182 ) 183 ) 184 ) 185 i32.const -1 186 ) 187 (func (export "test4") (param anyref) (result i32) 188 (if (ref.test (ref $a) (local.get 0)) 189 (then 190 (if (ref.test (ref $a) (local.get 0)) 191 (then 192 local.get 0 193 ref.cast (ref $a) 194 ref.cast (ref $a) 195 (array.get $a (i32.const 0)) 196 return 197 ) 198 ) 199 ) 200 ) 201 i32.const -1 202 ) 203 )`).exports; 204 assertEq(test1(make(99)), 99); 205 assertEq(test2(make(99)), -1); 206 assertEq(test3(make(99)), -1); 207 assertEq(test4(make(99)), 99); 208 }