| ... | ... | @@ -22,7 +22,8 @@ const IdResultType = spec.IdResultType; |
| 22 | 22 | const StorageClass = spec.StorageClass; |
| 23 | 23 | |
| 24 | 24 | const SpvModule = @import("spirv/Module.zig"); |
| 25 | | const SpvRef = SpvModule.TypeConstantCache.Ref; |
| 25 | const SpvCacheRef = SpvModule.TypeConstantCache.Ref; |
| 26 | const SpvCacheString = SpvModule.TypeConstantCache.String; |
| 26 | 27 | |
| 27 | 28 | const SpvSection = @import("spirv/Section.zig"); |
| 28 | 29 | const SpvType = @import("spirv/type.zig").Type; |
| ... | ... | @@ -1160,7 +1161,7 @@ pub const DeclGen = struct { |
| 1160 | 1161 | return try self.spv.resolveType(try SpvType.int(self.spv.arena, signedness, backing_bits)); |
| 1161 | 1162 | } |
| 1162 | 1163 | |
| 1163 | | fn intType2(self: *DeclGen, signedness: std.builtin.Signedness, bits: u16) !SpvRef { |
| 1164 | fn intType2(self: *DeclGen, signedness: std.builtin.Signedness, bits: u16) !SpvCacheRef { |
| 1164 | 1165 | const backing_bits = self.backingIntBits(bits) orelse { |
| 1165 | 1166 | // TODO: Integers too big for any native type are represented as "composite integers": |
| 1166 | 1167 | // An array of largestSupportedIntBits. |
| ... | ... | @@ -1177,7 +1178,7 @@ pub const DeclGen = struct { |
| 1177 | 1178 | return try self.intType(.unsigned, self.getTarget().ptrBitWidth()); |
| 1178 | 1179 | } |
| 1179 | 1180 | |
| 1180 | | fn sizeType2(self: *DeclGen) !SpvRef { |
| 1181 | fn sizeType2(self: *DeclGen) !SpvCacheRef { |
| 1181 | 1182 | return try self.intType2(.unsigned, self.getTarget().ptrBitWidth()); |
| 1182 | 1183 | } |
| 1183 | 1184 | |
| ... | ... | @@ -1256,7 +1257,91 @@ pub const DeclGen = struct { |
| 1256 | 1257 | return try self.spv.simpleStructType(members.slice()); |
| 1257 | 1258 | } |
| 1258 | 1259 | |
| 1259 | | fn resolveType2(self: *DeclGen, ty: Type, repr: Repr) !SpvRef { |
| 1260 | /// Generate a union type, optionally with a known field. If the tag alignment is greater |
| 1261 | /// than that of the payload, a regular union (non-packed, with both tag and payload), will |
| 1262 | /// be generated as follows: |
| 1263 | /// If the active field is known: |
| 1264 | /// struct { |
| 1265 | /// tag: TagType, |
| 1266 | /// payload: ActivePayloadType, |
| 1267 | /// payload_padding: [payload_size - @sizeOf(ActivePayloadType)]u8, |
| 1268 | /// padding: [padding_size]u8, |
| 1269 | /// } |
| 1270 | /// If the payload alignment is greater than that of the tag: |
| 1271 | /// struct { |
| 1272 | /// payload: ActivePayloadType, |
| 1273 | /// payload_padding: [payload_size - @sizeOf(ActivePayloadType)]u8, |
| 1274 | /// tag: TagType, |
| 1275 | /// padding: [padding_size]u8, |
| 1276 | /// } |
| 1277 | /// If the active payload is unknown, it will default back to the most aligned field. This is |
| 1278 | /// to make sure that the overal struct has the correct alignment in spir-v. |
| 1279 | /// If any of the fields' size is 0, it will be omitted. |
| 1280 | /// NOTE: When the active field is set to something other than the most aligned field, the |
| 1281 | /// resulting struct will be *underaligned*. |
| 1282 | fn resolveUnionType2(self: *DeclGen, ty: Type, maybe_active_field: ?usize) !SpvCacheRef { |
| 1283 | const target = self.getTarget(); |
| 1284 | const layout = ty.unionGetLayout(target); |
| 1285 | const union_ty = ty.cast(Type.Payload.Union).?.data; |
| 1286 | |
| 1287 | if (union_ty.layout == .Packed) { |
| 1288 | return self.todo("packed union types", .{}); |
| 1289 | } |
| 1290 | |
| 1291 | if (layout.payload_size == 0) { |
| 1292 | // No payload, so represent this as just the tag type. |
| 1293 | return try self.resolveType2(union_ty.tag_ty, .indirect); |
| 1294 | } |
| 1295 | |
| 1296 | var member_types = std.BoundedArray(SpvCacheRef, 4){}; |
| 1297 | var member_names = std.BoundedArray(SpvCacheString, 4){}; |
| 1298 | |
| 1299 | const has_tag = layout.tag_size != 0; |
| 1300 | const tag_first = layout.tag_align >= layout.payload_align; |
| 1301 | const u8_ty_ref = try self.intType2(.unsigned, 8); // TODO: What if Int8Type is not enabled? |
| 1302 | |
| 1303 | if (has_tag and tag_first) { |
| 1304 | const tag_ty_ref = try self.resolveType2(union_ty.tag_ty, .indirect); |
| 1305 | member_types.appendAssumeCapacity(tag_ty_ref); |
| 1306 | member_names.appendAssumeCapacity(try self.spv.resolveString("tag")); |
| 1307 | } |
| 1308 | |
| 1309 | const active_field = maybe_active_field orelse layout.most_aligned_field; |
| 1310 | const active_field_ty = union_ty.fields.values()[active_field].ty; |
| 1311 | |
| 1312 | const active_field_size = if (active_field_ty.hasRuntimeBitsIgnoreComptime()) blk: { |
| 1313 | const active_payload_ty_ref = try self.resolveType2(active_field_ty, .indirect); |
| 1314 | member_types.appendAssumeCapacity(active_payload_ty_ref); |
| 1315 | member_names.appendAssumeCapacity(try self.spv.resolveString("payload")); |
| 1316 | break :blk active_field_ty.abiSize(target); |
| 1317 | } else 0; |
| 1318 | |
| 1319 | const payload_padding_len = layout.payload_size - active_field_size; |
| 1320 | if (payload_padding_len != 0) { |
| 1321 | const payload_padding_ty_ref = try self.spv.arrayType2(@intCast(u32, payload_padding_len), u8_ty_ref); |
| 1322 | member_types.appendAssumeCapacity(payload_padding_ty_ref); |
| 1323 | member_names.appendAssumeCapacity(try self.spv.resolveString("payload_padding")); |
| 1324 | } |
| 1325 | |
| 1326 | if (has_tag and !tag_first) { |
| 1327 | const tag_ty_ref = try self.resolveType2(union_ty.tag_ty, .indirect); |
| 1328 | member_types.appendAssumeCapacity(tag_ty_ref); |
| 1329 | member_names.appendAssumeCapacity(try self.spv.resolveString("tag")); |
| 1330 | } |
| 1331 | |
| 1332 | if (layout.padding != 0) { |
| 1333 | const padding_ty_ref = try self.spv.arrayType2(layout.padding, u8_ty_ref); |
| 1334 | member_types.appendAssumeCapacity(padding_ty_ref); |
| 1335 | member_names.appendAssumeCapacity(try self.spv.resolveString("padding")); |
| 1336 | } |
| 1337 | |
| 1338 | return try self.spv.resolve(.{ .struct_type = .{ |
| 1339 | .member_types = member_types.slice(), |
| 1340 | .member_names = member_names.slice(), |
| 1341 | } }); |
| 1342 | } |
| 1343 | |
| 1344 | fn resolveType2(self: *DeclGen, ty: Type, repr: Repr) Error!SpvCacheRef { |
| 1260 | 1345 | const target = self.getTarget(); |
| 1261 | 1346 | switch (ty.zigTypeTag()) { |
| 1262 | 1347 | .Void, .NoReturn => return try self.spv.resolve(.void_type), |
| ... | ... | @@ -1297,15 +1382,7 @@ pub const DeclGen = struct { |
| 1297 | 1382 | const total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel()) orelse { |
| 1298 | 1383 | return self.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel()}); |
| 1299 | 1384 | }; |
| 1300 | | const len_ty_ref = try self.intType2(.unsigned, 32); |
| 1301 | | const len_ref = try self.spv.resolve(.{ .int = .{ |
| 1302 | | .ty = len_ty_ref, |
| 1303 | | .value = .{ .uint64 = total_len }, |
| 1304 | | } }); |
| 1305 | | return try self.spv.resolve(.{ .array_type = .{ |
| 1306 | | .element_type = elem_ty_ref, |
| 1307 | | .length = len_ref, |
| 1308 | | } }); |
| 1385 | return self.spv.arrayType2(total_len, elem_ty_ref); |
| 1309 | 1386 | }, |
| 1310 | 1387 | .Fn => switch (repr) { |
| 1311 | 1388 | .direct => { |
| ... | ... | @@ -1313,7 +1390,7 @@ pub const DeclGen = struct { |
| 1313 | 1390 | if (ty.fnIsVarArgs()) |
| 1314 | 1391 | return self.fail("VarArgs functions are unsupported for SPIR-V", .{}); |
| 1315 | 1392 | |
| 1316 | | const param_ty_refs = try self.gpa.alloc(SpvRef, ty.fnParamLen()); |
| 1393 | const param_ty_refs = try self.gpa.alloc(SpvCacheRef, ty.fnParamLen()); |
| 1317 | 1394 | defer self.gpa.free(param_ty_refs); |
| 1318 | 1395 | for (param_ty_refs, 0..) |*param_type, i| { |
| 1319 | 1396 | param_type.* = try self.resolveType2(ty.fnParamType(i), .direct); |
| ... | ... | @@ -1360,8 +1437,116 @@ pub const DeclGen = struct { |
| 1360 | 1437 | .component_count = @intCast(u32, ty.vectorLen()), |
| 1361 | 1438 | } }); |
| 1362 | 1439 | }, |
| 1440 | .Struct => { |
| 1441 | if (ty.isSimpleTupleOrAnonStruct()) { |
| 1442 | unreachable; // TODO |
| 1443 | } |
| 1363 | 1444 | |
| 1364 | | else => unreachable, // TODO |
| 1445 | const struct_ty = ty.castTag(.@"struct").?.data; |
| 1446 | |
| 1447 | if (struct_ty.layout == .Packed) { |
| 1448 | return try self.resolveType2(struct_ty.backing_int_ty, .direct); |
| 1449 | } |
| 1450 | |
| 1451 | const member_types = try self.gpa.alloc(SpvCacheRef, struct_ty.fields.count()); |
| 1452 | defer self.gpa.free(member_types); |
| 1453 | |
| 1454 | const member_names = try self.gpa.alloc(SpvCacheString, struct_ty.fields.count()); |
| 1455 | defer self.gpa.free(member_names); |
| 1456 | |
| 1457 | // const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, struct_ty.fields.count()); |
| 1458 | var member_index: usize = 0; |
| 1459 | for (struct_ty.fields.values(), 0..) |field, i| { |
| 1460 | if (field.is_comptime or !field.ty.hasRuntimeBits()) continue; |
| 1461 | |
| 1462 | member_types[member_index] = try self.resolveType2(field.ty, .indirect); |
| 1463 | member_names[member_index] = try self.spv.resolveString(struct_ty.fields.keys()[i]); |
| 1464 | member_index += 1; |
| 1465 | } |
| 1466 | |
| 1467 | const name = try struct_ty.getFullyQualifiedName(self.module); |
| 1468 | defer self.module.gpa.free(name); |
| 1469 | |
| 1470 | return try self.spv.resolve(.{ .struct_type = .{ |
| 1471 | .name = try self.spv.resolveString(name), |
| 1472 | .member_types = member_types[0..member_index], |
| 1473 | .member_names = member_names[0..member_index], |
| 1474 | } }); |
| 1475 | }, |
| 1476 | .Optional => { |
| 1477 | var buf: Type.Payload.ElemType = undefined; |
| 1478 | const payload_ty = ty.optionalChild(&buf); |
| 1479 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 1480 | // Just use a bool. |
| 1481 | // Note: Always generate the bool with indirect format, to save on some sanity |
| 1482 | // Perform the conversion to a direct bool when the field is extracted. |
| 1483 | return try self.resolveType2(Type.bool, .indirect); |
| 1484 | } |
| 1485 | |
| 1486 | const payload_ty_ref = try self.resolveType2(payload_ty, .indirect); |
| 1487 | if (ty.optionalReprIsPayload()) { |
| 1488 | // Optional is actually a pointer or a slice. |
| 1489 | return payload_ty_ref; |
| 1490 | } |
| 1491 | |
| 1492 | const bool_ty_ref = try self.resolveType2(Type.bool, .indirect); |
| 1493 | |
| 1494 | return try self.spv.resolve(.{ .struct_type = .{ |
| 1495 | .member_types = &.{ payload_ty_ref, bool_ty_ref }, |
| 1496 | .member_names = &.{ |
| 1497 | try self.spv.resolveString("payload"), |
| 1498 | try self.spv.resolveString("valid"), |
| 1499 | }, |
| 1500 | } }); |
| 1501 | }, |
| 1502 | .Union => return try self.resolveUnionType2(ty, null), |
| 1503 | .ErrorSet => return try self.intType2(.unsigned, 16), |
| 1504 | .ErrorUnion => { |
| 1505 | const payload_ty = ty.errorUnionPayload(); |
| 1506 | const error_ty_ref = try self.resolveType2(Type.anyerror, .indirect); |
| 1507 | |
| 1508 | const eu_layout = self.errorUnionLayout(payload_ty); |
| 1509 | if (!eu_layout.payload_has_bits) { |
| 1510 | return error_ty_ref; |
| 1511 | } |
| 1512 | |
| 1513 | const payload_ty_ref = try self.resolveType2(payload_ty, .indirect); |
| 1514 | |
| 1515 | var member_types: [2]SpvCacheRef = undefined; |
| 1516 | var member_names: [2]SpvCacheString = undefined; |
| 1517 | if (eu_layout.error_first) { |
| 1518 | // Put the error first |
| 1519 | member_types = .{ error_ty_ref, payload_ty_ref }; |
| 1520 | member_names = .{ |
| 1521 | try self.spv.resolveString("error"), |
| 1522 | try self.spv.resolveString("payload"), |
| 1523 | }; |
| 1524 | // TODO: ABI padding? |
| 1525 | } else { |
| 1526 | // Put the payload first. |
| 1527 | member_types = .{ payload_ty_ref, error_ty_ref }; |
| 1528 | member_names = .{ |
| 1529 | try self.spv.resolveString("payload"), |
| 1530 | try self.spv.resolveString("error"), |
| 1531 | }; |
| 1532 | // TODO: ABI padding? |
| 1533 | } |
| 1534 | |
| 1535 | return try self.spv.resolve(.{ .struct_type = .{ |
| 1536 | .member_types = &member_types, |
| 1537 | .member_names = &member_names, |
| 1538 | } }); |
| 1539 | }, |
| 1540 | |
| 1541 | .Null, |
| 1542 | .Undefined, |
| 1543 | .EnumLiteral, |
| 1544 | .ComptimeFloat, |
| 1545 | .ComptimeInt, |
| 1546 | .Type, |
| 1547 | => unreachable, // Must be comptime. |
| 1548 | |
| 1549 | else => |tag| return self.todo("Implement zig type '{}'", .{tag}), |
| 1365 | 1550 | } |
| 1366 | 1551 | } |
| 1367 | 1552 | |