| ... | @@ -425,6 +425,7 @@ pub const DeclGen = struct { | ... | @@ -425,6 +425,7 @@ pub const DeclGen = struct { |
| 425 | // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349 | 425 | // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349 |
| 426 | // For now, just initialize the struct by setting the fields manually... | 426 | // For now, just initialize the struct by setting the fields manually... |
| 427 | // TODO: Make this OpCompositeConstruct when we can | 427 | // TODO: Make this OpCompositeConstruct when we can |
| | 428 | // TODO: Make this Function storage type |
| 428 | const ptr_composite_id = try self.alloc(result_ty_ref, null); | 429 | const ptr_composite_id = try self.alloc(result_ty_ref, null); |
| 429 | // Note: using 32-bit ints here because usize crashes the translator as well | 430 | // Note: using 32-bit ints here because usize crashes the translator as well |
| 430 | const index_ty_ref = try self.intType(.unsigned, 32); | 431 | const index_ty_ref = try self.intType(.unsigned, 32); |
| ... | @@ -450,6 +451,40 @@ pub const DeclGen = struct { | ... | @@ -450,6 +451,40 @@ pub const DeclGen = struct { |
| 450 | return result_id; | 451 | return result_id; |
| 451 | } | 452 | } |
| 452 | | 453 | |
| | 454 | /// Construct a struct at runtime. |
| | 455 | /// result_ty_ref must be an array type. |
| | 456 | fn constructArray(self: *DeclGen, result_ty_ref: CacheRef, constituents: []const IdRef) !IdRef { |
| | 457 | // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which' |
| | 458 | // operands are not constant. |
| | 459 | // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349 |
| | 460 | // For now, just initialize the struct by setting the fields manually... |
| | 461 | // TODO: Make this OpCompositeConstruct when we can |
| | 462 | // TODO: Make this Function storage type |
| | 463 | const ptr_composite_id = try self.alloc(result_ty_ref, null); |
| | 464 | // Note: using 32-bit ints here because usize crashes the translator as well |
| | 465 | const index_ty_ref = try self.intType(.unsigned, 32); |
| | 466 | |
| | 467 | const spv_composite_ty = self.spv.cache.lookup(result_ty_ref).array_type; |
| | 468 | const elem_ty_ref = spv_composite_ty.element_type; |
| | 469 | const ptr_elem_ty_ref = try self.spv.ptrType(elem_ty_ref, .Generic); |
| | 470 | |
| | 471 | for (constituents, 0..) |constitent_id, index| { |
| | 472 | const index_id = try self.spv.constInt(index_ty_ref, index); |
| | 473 | const ptr_id = try self.accessChain(ptr_elem_ty_ref, ptr_composite_id, &.{index_id}); |
| | 474 | try self.func.body.emit(self.spv.gpa, .OpStore, .{ |
| | 475 | .pointer = ptr_id, |
| | 476 | .object = constitent_id, |
| | 477 | }); |
| | 478 | } |
| | 479 | const result_id = self.spv.allocId(); |
| | 480 | try self.func.body.emit(self.spv.gpa, .OpLoad, .{ |
| | 481 | .id_result_type = self.typeId(result_ty_ref), |
| | 482 | .id_result = result_id, |
| | 483 | .pointer = ptr_composite_id, |
| | 484 | }); |
| | 485 | return result_id; |
| | 486 | } |
| | 487 | |
| 453 | fn constructDeclRef(self: *DeclGen, ty: Type, decl_index: Decl.Index) !IdRef { | 488 | fn constructDeclRef(self: *DeclGen, ty: Type, decl_index: Decl.Index) !IdRef { |
| 454 | const mod = self.module; | 489 | const mod = self.module; |
| 455 | const ty_ref = try self.resolveType(ty, .direct); | 490 | const ty_ref = try self.resolveType(ty, .direct); |
| ... | @@ -459,10 +494,9 @@ pub const DeclGen = struct { | ... | @@ -459,10 +494,9 @@ pub const DeclGen = struct { |
| 459 | switch (mod.intern_pool.indexToKey(decl.val.ip_index)) { | 494 | switch (mod.intern_pool.indexToKey(decl.val.ip_index)) { |
| 460 | .func => { | 495 | .func => { |
| 461 | // TODO: Properly lower function pointers. For now we are going to hack around it and | 496 | // TODO: Properly lower function pointers. For now we are going to hack around it and |
| 462 | // just generate an empty pointer. Function pointers are represented by usize for now, | 497 | // just generate an empty pointer. Function pointers are represented by a pointer to usize. |
| 463 | // though. | | |
| 464 | // TODO: Add dependency | 498 | // TODO: Add dependency |
| 465 | return try self.spv.constInt(ty_ref, 0); | 499 | return try self.spv.constNull(ty_ref); |
| 466 | }, | 500 | }, |
| 467 | .extern_func => unreachable, // TODO | 501 | .extern_func => unreachable, // TODO |
| 468 | else => { | 502 | else => { |
| ... | @@ -1074,7 +1108,7 @@ pub const DeclGen = struct { | ... | @@ -1074,7 +1108,7 @@ pub const DeclGen = struct { |
| 1074 | else => {}, | 1108 | else => {}, |
| 1075 | } | 1109 | } |
| 1076 | | 1110 | |
| 1077 | log.debug("constant: ty = {}, val = {}", .{ ty.fmt(self.module), val.fmtValue(ty, self.module) }); | 1111 | log.debug("constant: ty = {}, val = {}", .{ ty.fmt(mod), val.fmtValue(ty, mod) }); |
| 1078 | if (val.isUndefDeep(mod)) { | 1112 | if (val.isUndefDeep(mod)) { |
| 1079 | return self.spv.constUndef(result_ty_ref); | 1113 | return self.spv.constUndef(result_ty_ref); |
| 1080 | } | 1114 | } |
| ... | @@ -1270,7 +1304,7 @@ pub const DeclGen = struct { | ... | @@ -1270,7 +1304,7 @@ pub const DeclGen = struct { |
| 1270 | if (array_type.sentinel != .none) { | 1304 | if (array_type.sentinel != .none) { |
| 1271 | constituents[constituents.len - 1] = try self.constant(elem_ty, array_type.sentinel.toValue(), .indirect); | 1305 | constituents[constituents.len - 1] = try self.constant(elem_ty, array_type.sentinel.toValue(), .indirect); |
| 1272 | } | 1306 | } |
| 1273 | return try self.constructStruct(result_ty_ref, constituents); | 1307 | return try self.constructArray(result_ty_ref, constituents); |
| 1274 | }, | 1308 | }, |
| 1275 | .struct_type => { | 1309 | .struct_type => { |
| 1276 | const struct_ty = mod.typeToStruct(ty).?; | 1310 | const struct_ty = mod.typeToStruct(ty).?; |
| ... | @@ -1281,18 +1315,14 @@ pub const DeclGen = struct { | ... | @@ -1281,18 +1315,14 @@ pub const DeclGen = struct { |
| 1281 | var constituents = std.ArrayList(IdRef).init(self.gpa); | 1315 | var constituents = std.ArrayList(IdRef).init(self.gpa); |
| 1282 | defer constituents.deinit(); | 1316 | defer constituents.deinit(); |
| 1283 | | 1317 | |
| 1284 | for (struct_ty.fields.values(), 0..) |field, i| { | 1318 | var field_it = struct_ty.runtimeFieldIterator(mod); |
| 1285 | if (field.is_comptime or !field.ty.hasRuntimeBits(mod)) continue; | 1319 | while (field_it.next()) |field_and_index| { |
| | 1320 | const field = field_and_index.field; |
| | 1321 | const index = field_and_index.index; |
| | 1322 | // TODO: Padding? |
| | 1323 | const field_val = try val.fieldValue(mod, index); |
| | 1324 | const field_id = try self.constant(field.ty, field_val, .indirect); |
| 1286 | | 1325 | |
| 1287 | const field_val = switch (aggregate.storage) { | | |
| 1288 | .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ | | |
| 1289 | .ty = field.ty.toIntern(), | | |
| 1290 | .storage = .{ .u64 = bytes[i] }, | | |
| 1291 | } }), | | |
| 1292 | .elems => |elems| elems[i], | | |
| 1293 | .repeated_elem => |elem| elem, | | |
| 1294 | }; | | |
| 1295 | const field_id = try self.constant(field.ty, field_val.toValue(), .indirect); | | |
| 1296 | try constituents.append(field_id); | 1326 | try constituents.append(field_id); |
| 1297 | } | 1327 | } |
| 1298 | | 1328 | |
| ... | @@ -1306,33 +1336,35 @@ pub const DeclGen = struct { | ... | @@ -1306,33 +1336,35 @@ pub const DeclGen = struct { |
| 1306 | // type that has the right field active, then pointer-cast and store | 1336 | // type that has the right field active, then pointer-cast and store |
| 1307 | // the active field, and finally load and return the entire union. | 1337 | // the active field, and finally load and return the entire union. |
| 1308 | | 1338 | |
| 1309 | const layout = ty.unionGetLayout(mod); | | |
| 1310 | const union_ty = mod.typeToUnion(ty).?; | 1339 | const union_ty = mod.typeToUnion(ty).?; |
| 1311 | | 1340 | |
| 1312 | if (union_ty.getLayout(ip) == .Packed) { | 1341 | if (union_ty.getLayout(ip) == .Packed) { |
| 1313 | return self.todo("packed union types", .{}); | 1342 | return self.todo("packed union types", .{}); |
| 1314 | } else if (layout.payload_size == 0) { | 1343 | } |
| | 1344 | |
| | 1345 | const active_field = ty.unionTagFieldIndex(un.tag.toValue(), mod).?; |
| | 1346 | const layout = self.unionLayout(ty, active_field); |
| | 1347 | |
| | 1348 | if (layout.payload_size == 0) { |
| 1315 | // No payload, so represent this as just the tag type. | 1349 | // No payload, so represent this as just the tag type. |
| 1316 | return try self.constant(ty.unionTagTypeSafety(mod).?, un.tag.toValue(), .indirect); | 1350 | return try self.constant(ty.unionTagTypeSafety(mod).?, un.tag.toValue(), .indirect); |
| 1317 | } | 1351 | } |
| 1318 | | 1352 | |
| 1319 | const has_tag = layout.tag_size != 0; | 1353 | const un_active_ty_ref = try self.resolveUnionType(ty, active_field); |
| 1320 | const tag_first = layout.tag_align >= layout.payload_align; | 1354 | const un_active_ptr_ty_ref = try self.spv.ptrType(un_active_ty_ref, .Function); |
| 1321 | | 1355 | const un_general_ptr_ty_ref = try self.spv.ptrType(result_ty_ref, .Function); |
| 1322 | const un_ptr_ty_ref = try self.spv.ptrType(result_ty_ref, .Function); | | |
| 1323 | | 1356 | |
| 1324 | const var_id = self.spv.allocId(); | 1357 | const var_id = self.spv.allocId(); |
| 1325 | try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ | 1358 | try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ |
| 1326 | .id_result_type = self.typeId(un_ptr_ty_ref), | 1359 | .id_result_type = self.typeId(un_active_ptr_ty_ref), |
| 1327 | .id_result = var_id, | 1360 | .id_result = var_id, |
| 1328 | .storage_class = .Function, | 1361 | .storage_class = .Function, |
| 1329 | }); | 1362 | }); |
| 1330 | | 1363 | |
| 1331 | const index_ty_ref = try self.intType(.unsigned, 32); | 1364 | const index_ty_ref = try self.intType(.unsigned, 32); |
| 1332 | | 1365 | |
| 1333 | if (has_tag) { | 1366 | if (layout.tag_size != 0) { |
| 1334 | const tag_index: u32 = if (tag_first) 0 else 1; | 1367 | const index_id = try self.spv.constInt(index_ty_ref, @as(u32, @intCast(layout.tag_index))); |
| 1335 | const index_id = try self.spv.constInt(index_ty_ref, tag_index); | | |
| 1336 | const tag_ty = ty.unionTagTypeSafety(mod).?; | 1368 | const tag_ty = ty.unionTagTypeSafety(mod).?; |
| 1337 | const tag_ty_ref = try self.resolveType(tag_ty, .indirect); | 1369 | const tag_ty_ref = try self.resolveType(tag_ty, .indirect); |
| 1338 | const tag_ptr_ty_ref = try self.spv.ptrType(tag_ty_ref, .Function); | 1370 | const tag_ptr_ty_ref = try self.spv.ptrType(tag_ty_ref, .Function); |
| ... | @@ -1344,56 +1376,39 @@ pub const DeclGen = struct { | ... | @@ -1344,56 +1376,39 @@ pub const DeclGen = struct { |
| 1344 | }); | 1376 | }); |
| 1345 | } | 1377 | } |
| 1346 | | 1378 | |
| 1347 | const pl_index: u32 = if (tag_first) 1 else 0; | 1379 | if (layout.active_field_size != 0) { |
| 1348 | const index_id = try self.spv.constInt(index_ty_ref, pl_index); | 1380 | const index_id = try self.spv.constInt(index_ty_ref, @as(u32, @intCast(layout.active_field_index))); |
| 1349 | const active_field = ty.unionTagFieldIndex(un.tag.toValue(), mod).?; | 1381 | const active_field_ty_ref = try self.resolveType(layout.active_field_ty, .indirect); |
| 1350 | const active_field_ty = union_ty.field_types.get(ip)[active_field].toType(); | 1382 | const active_field_ptr_ty_ref = try self.spv.ptrType(active_field_ty_ref, .Function); |
| 1351 | const active_field_ty_ref = try self.resolveType(active_field_ty, .indirect); | 1383 | const ptr_id = try self.accessChain(active_field_ptr_ty_ref, var_id, &.{index_id}); |
| 1352 | const active_field_ptr_ty_ref = try self.spv.ptrType(active_field_ty_ref, .Function); | 1384 | const value_id = try self.constant(layout.active_field_ty, un.val.toValue(), .indirect); |
| 1353 | const ptr_id = try self.accessChain(active_field_ptr_ty_ref, var_id, &.{index_id}); | 1385 | try self.func.body.emit(self.spv.gpa, .OpStore, .{ |
| 1354 | const value_id = try self.constant(active_field_ty, un.val.toValue(), .indirect); | 1386 | .pointer = ptr_id, |
| 1355 | try self.func.body.emit(self.spv.gpa, .OpStore, .{ | 1387 | .object = value_id, |
| 1356 | .pointer = ptr_id, | 1388 | }); |
| 1357 | .object = value_id, | 1389 | } |
| 1358 | }); | | |
| 1359 | | 1390 | |
| 1360 | // Just leave the padding fields uninitialized... | 1391 | // Just leave the padding fields uninitialized... |
| | 1392 | // TODO: Or should we initialize them with undef explicitly? |
| 1361 | | 1393 | |
| 1362 | const result_id = self.spv.allocId(); | 1394 | // Now cast the pointer and load it as the 'generic' union type. |
| 1363 | try self.func.body.emit(self.spv.gpa, .OpLoad, .{ | 1395 | |
| 1364 | .id_result_type = self.typeId(result_ty_ref), | 1396 | const casted_var_id = self.spv.allocId(); |
| 1365 | .id_result = result_id, | 1397 | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ |
| 1366 | .pointer = var_id, | 1398 | .id_result_type = self.typeId(un_general_ptr_ty_ref), |
| | 1399 | .id_result = casted_var_id, |
| | 1400 | .operand = var_id, |
| 1367 | }); | 1401 | }); |
| 1368 | return result_id; | | |
| 1369 | }, | | |
| 1370 | else => { | | |
| 1371 | // The value cannot be generated directly, so generate it as an indirect constant, | | |
| 1372 | // and then perform an OpLoad. | | |
| 1373 | const result_id = self.spv.allocId(); | | |
| 1374 | const alignment = ty.abiAlignment(mod); | | |
| 1375 | const spv_decl_index = try self.spv.allocDecl(.global); | | |
| 1376 | | | |
| 1377 | try self.lowerIndirectConstant( | | |
| 1378 | spv_decl_index, | | |
| 1379 | ty, | | |
| 1380 | val, | | |
| 1381 | .UniformConstant, | | |
| 1382 | false, | | |
| 1383 | @intCast(alignment.toByteUnits(0)), | | |
| 1384 | ); | | |
| 1385 | log.debug("indirect constant: index = {}", .{@intFromEnum(spv_decl_index)}); | | |
| 1386 | try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {}); | | |
| 1387 | | 1402 | |
| | 1403 | const result_id = self.spv.allocId(); |
| 1388 | try self.func.body.emit(self.spv.gpa, .OpLoad, .{ | 1404 | try self.func.body.emit(self.spv.gpa, .OpLoad, .{ |
| 1389 | .id_result_type = self.typeId(result_ty_ref), | 1405 | .id_result_type = self.typeId(result_ty_ref), |
| 1390 | .id_result = result_id, | 1406 | .id_result = result_id, |
| 1391 | .pointer = self.spv.declPtr(spv_decl_index).result_id, | 1407 | .pointer = casted_var_id, |
| 1392 | }); | 1408 | }); |
| 1393 | // TODO: Convert bools? This logic should hook into `load`. It should be a dead | | |
| 1394 | // path though considering .Bool is handled above. | | |
| 1395 | return result_id; | 1409 | return result_id; |
| 1396 | }, | 1410 | }, |
| | 1411 | .memoized_call => unreachable, |
| 1397 | } | 1412 | } |
| 1398 | } | 1413 | } |
| 1399 | | 1414 | |
| ... | @@ -1458,72 +1473,61 @@ pub const DeclGen = struct { | ... | @@ -1458,72 +1473,61 @@ pub const DeclGen = struct { |
| 1458 | fn resolveUnionType(self: *DeclGen, ty: Type, maybe_active_field: ?usize) !CacheRef { | 1473 | fn resolveUnionType(self: *DeclGen, ty: Type, maybe_active_field: ?usize) !CacheRef { |
| 1459 | const mod = self.module; | 1474 | const mod = self.module; |
| 1460 | const ip = &mod.intern_pool; | 1475 | const ip = &mod.intern_pool; |
| 1461 | const layout = ty.unionGetLayout(mod); | | |
| 1462 | const union_obj = mod.typeToUnion(ty).?; | 1476 | const union_obj = mod.typeToUnion(ty).?; |
| 1463 | | 1477 | |
| 1464 | if (union_obj.getLayout(ip) == .Packed) { | 1478 | if (union_obj.getLayout(ip) == .Packed) { |
| 1465 | return self.todo("packed union types", .{}); | 1479 | return self.todo("packed union types", .{}); |
| 1466 | } | 1480 | } |
| 1467 | | 1481 | |
| | 1482 | const layout = self.unionLayout(ty, maybe_active_field); |
| | 1483 | |
| 1468 | if (layout.payload_size == 0) { | 1484 | if (layout.payload_size == 0) { |
| 1469 | // No payload, so represent this as just the tag type. | 1485 | // No payload, so represent this as just the tag type. |
| 1470 | return try self.resolveType(union_obj.enum_tag_ty.toType(), .indirect); | 1486 | return try self.resolveType(union_obj.enum_tag_ty.toType(), .indirect); |
| 1471 | } | 1487 | } |
| 1472 | | 1488 | |
| 1473 | const entry = try self.type_map.getOrPut(self.gpa, ty.toIntern()); | 1489 | // TODO: We need to add the active field to the key. |
| 1474 | if (entry.found_existing) return entry.value_ptr.ty_ref; | 1490 | // const entry = try self.type_map.getOrPut(self.gpa, ty.toIntern()); |
| | 1491 | // if (entry.found_existing) return entry.value_ptr.ty_ref; |
| 1475 | | 1492 | |
| 1476 | var member_types = std.BoundedArray(CacheRef, 4){}; | 1493 | var member_types: [4]CacheRef = undefined; |
| 1477 | var member_names = std.BoundedArray(CacheString, 4){}; | 1494 | var member_names: [4]CacheString = undefined; |
| 1478 | | 1495 | |
| 1479 | const has_tag = layout.tag_size != 0; | | |
| 1480 | const tag_first = layout.tag_align.compare(.gte, layout.payload_align); | | |
| 1481 | const u8_ty_ref = try self.intType(.unsigned, 8); // TODO: What if Int8Type is not enabled? | 1496 | const u8_ty_ref = try self.intType(.unsigned, 8); // TODO: What if Int8Type is not enabled? |
| 1482 | | 1497 | |
| 1483 | if (has_tag and tag_first) { | 1498 | if (layout.tag_size != 0) { |
| 1484 | const tag_ty_ref = try self.resolveType(union_obj.enum_tag_ty.toType(), .indirect); | 1499 | const tag_ty_ref = try self.resolveType(union_obj.enum_tag_ty.toType(), .indirect); |
| 1485 | member_types.appendAssumeCapacity(tag_ty_ref); | 1500 | member_types[layout.tag_index] = tag_ty_ref; |
| 1486 | member_names.appendAssumeCapacity(try self.spv.resolveString("(tag)")); | 1501 | member_names[layout.tag_index] = try self.spv.resolveString("(tag)"); |
| 1487 | } | 1502 | } |
| 1488 | | 1503 | |
| 1489 | const active_field = maybe_active_field orelse layout.most_aligned_field; | 1504 | if (layout.active_field_size != 0) { |
| 1490 | const active_field_ty = union_obj.field_types.get(ip)[active_field].toType(); | 1505 | const active_payload_ty_ref = try self.resolveType(layout.active_field_ty, .indirect); |
| 1491 | | 1506 | member_types[layout.active_field_index] = active_payload_ty_ref; |
| 1492 | const active_field_size = if (active_field_ty.hasRuntimeBitsIgnoreComptime(mod)) blk: { | 1507 | member_names[layout.active_field_index] = try self.spv.resolveString("(payload)"); |
| 1493 | const active_payload_ty_ref = try self.resolveType(active_field_ty, .indirect); | | |
| 1494 | member_types.appendAssumeCapacity(active_payload_ty_ref); | | |
| 1495 | member_names.appendAssumeCapacity(try self.spv.resolveString("(payload)")); | | |
| 1496 | break :blk active_field_ty.abiSize(mod); | | |
| 1497 | } else 0; | | |
| 1498 | | | |
| 1499 | const payload_padding_len = layout.payload_size - active_field_size; | | |
| 1500 | if (payload_padding_len != 0) { | | |
| 1501 | const payload_padding_ty_ref = try self.spv.arrayType(@as(u32, @intCast(payload_padding_len)), u8_ty_ref); | | |
| 1502 | member_types.appendAssumeCapacity(payload_padding_ty_ref); | | |
| 1503 | member_names.appendAssumeCapacity(try self.spv.resolveString("(payload padding)")); | | |
| 1504 | } | 1508 | } |
| 1505 | | 1509 | |
| 1506 | if (has_tag and !tag_first) { | 1510 | if (layout.payload_padding_size != 0) { |
| 1507 | const tag_ty_ref = try self.resolveType(union_obj.enum_tag_ty.toType(), .indirect); | 1511 | const payload_padding_ty_ref = try self.spv.arrayType(@intCast(layout.payload_padding_size), u8_ty_ref); |
| 1508 | member_types.appendAssumeCapacity(tag_ty_ref); | 1512 | member_types[layout.payload_padding_index] = payload_padding_ty_ref; |
| 1509 | member_names.appendAssumeCapacity(try self.spv.resolveString("(tag)")); | 1513 | member_names[layout.payload_padding_index] = try self.spv.resolveString("(payload padding)"); |
| 1510 | } | 1514 | } |
| 1511 | | 1515 | |
| 1512 | if (layout.padding != 0) { | 1516 | if (layout.padding_size != 0) { |
| 1513 | const padding_ty_ref = try self.spv.arrayType(layout.padding, u8_ty_ref); | 1517 | const padding_ty_ref = try self.spv.arrayType(@intCast(layout.padding_size), u8_ty_ref); |
| 1514 | member_types.appendAssumeCapacity(padding_ty_ref); | 1518 | member_types[layout.padding_index] = padding_ty_ref; |
| 1515 | member_names.appendAssumeCapacity(try self.spv.resolveString("(padding)")); | 1519 | member_names[layout.padding_index] = try self.spv.resolveString("(padding)"); |
| 1516 | } | 1520 | } |
| 1517 | | 1521 | |
| 1518 | const ty_ref = try self.spv.resolve(.{ .struct_type = .{ | 1522 | const ty_ref = try self.spv.resolve(.{ .struct_type = .{ |
| 1519 | .name = try self.resolveTypeName(ty), | 1523 | .name = try self.resolveTypeName(ty), |
| 1520 | .member_types = member_types.slice(), | 1524 | .member_types = member_types[0..layout.total_fields], |
| 1521 | .member_names = member_names.slice(), | 1525 | .member_names = member_names[0..layout.total_fields], |
| 1522 | } }); | 1526 | } }); |
| 1523 | | 1527 | |
| 1524 | entry.value_ptr.* = .{ | 1528 | // entry.value_ptr.* = .{ |
| 1525 | .ty_ref = ty_ref, | 1529 | // .ty_ref = ty_ref, |
| 1526 | }; | 1530 | // }; |
| 1527 | | 1531 | |
| 1528 | return ty_ref; | 1532 | return ty_ref; |
| 1529 | } | 1533 | } |
| ... | @@ -1532,7 +1536,7 @@ pub const DeclGen = struct { | ... | @@ -1532,7 +1536,7 @@ pub const DeclGen = struct { |
| 1532 | fn resolveType(self: *DeclGen, ty: Type, repr: Repr) Error!CacheRef { | 1536 | fn resolveType(self: *DeclGen, ty: Type, repr: Repr) Error!CacheRef { |
| 1533 | const mod = self.module; | 1537 | const mod = self.module; |
| 1534 | const ip = &mod.intern_pool; | 1538 | const ip = &mod.intern_pool; |
| 1535 | log.debug("resolveType: ty = {}", .{ty.fmt(self.module)}); | 1539 | log.debug("resolveType: ty = {}", .{ty.fmt(mod)}); |
| 1536 | const target = self.getTarget(); | 1540 | const target = self.getTarget(); |
| 1537 | switch (ty.zigTypeTag(mod)) { | 1541 | switch (ty.zigTypeTag(mod)) { |
| 1538 | .Void, .NoReturn => return try self.spv.resolve(.void_type), | 1542 | .Void, .NoReturn => return try self.spv.resolve(.void_type), |
| ... | @@ -1854,6 +1858,85 @@ pub const DeclGen = struct { | ... | @@ -1854,6 +1858,85 @@ pub const DeclGen = struct { |
| 1854 | }; | 1858 | }; |
| 1855 | } | 1859 | } |
| 1856 | | 1860 | |
| | 1861 | const UnionLayout = struct { |
| | 1862 | active_field: usize, |
| | 1863 | active_field_ty: Type, |
| | 1864 | payload_size: usize, |
| | 1865 | |
| | 1866 | tag_size: usize, |
| | 1867 | tag_index: usize, |
| | 1868 | active_field_size: usize, |
| | 1869 | active_field_index: usize, |
| | 1870 | payload_padding_size: usize, |
| | 1871 | payload_padding_index: usize, |
| | 1872 | padding_size: usize, |
| | 1873 | padding_index: usize, |
| | 1874 | total_fields: usize, |
| | 1875 | }; |
| | 1876 | |
| | 1877 | fn unionLayout(self: *DeclGen, ty: Type, maybe_active_field: ?usize) UnionLayout { |
| | 1878 | const mod = self.module; |
| | 1879 | const ip = &mod.intern_pool; |
| | 1880 | const layout = ty.unionGetLayout(self.module); |
| | 1881 | const union_obj = mod.typeToUnion(ty).?; |
| | 1882 | |
| | 1883 | const active_field = maybe_active_field orelse layout.most_aligned_field; |
| | 1884 | const active_field_ty = union_obj.field_types.get(ip)[active_field].toType(); |
| | 1885 | |
| | 1886 | var union_layout = UnionLayout{ |
| | 1887 | .active_field = active_field, |
| | 1888 | .active_field_ty = active_field_ty, |
| | 1889 | .payload_size = layout.payload_size, |
| | 1890 | .tag_size = layout.tag_size, |
| | 1891 | .tag_index = undefined, |
| | 1892 | .active_field_size = undefined, |
| | 1893 | .active_field_index = undefined, |
| | 1894 | .payload_padding_size = undefined, |
| | 1895 | .payload_padding_index = undefined, |
| | 1896 | .padding_size = layout.padding, |
| | 1897 | .padding_index = undefined, |
| | 1898 | .total_fields = undefined, |
| | 1899 | }; |
| | 1900 | |
| | 1901 | union_layout.active_field_size = if (active_field_ty.hasRuntimeBitsIgnoreComptime(mod)) |
| | 1902 | active_field_ty.abiSize(mod) |
| | 1903 | else |
| | 1904 | 0; |
| | 1905 | union_layout.payload_padding_size = layout.payload_size - union_layout.active_field_size; |
| | 1906 | |
| | 1907 | const tag_first = layout.tag_align.compare(.gte, layout.payload_align); |
| | 1908 | var field_index: usize = 0; |
| | 1909 | |
| | 1910 | if (union_layout.tag_size != 0 and tag_first) { |
| | 1911 | union_layout.tag_index = field_index; |
| | 1912 | field_index += 1; |
| | 1913 | } |
| | 1914 | |
| | 1915 | if (union_layout.active_field_size != 0) { |
| | 1916 | union_layout.active_field_index = field_index; |
| | 1917 | field_index += 1; |
| | 1918 | } |
| | 1919 | |
| | 1920 | if (union_layout.payload_padding_size != 0) { |
| | 1921 | union_layout.payload_padding_index = field_index; |
| | 1922 | field_index += 1; |
| | 1923 | } |
| | 1924 | |
| | 1925 | if (union_layout.tag_size != 0 and !tag_first) { |
| | 1926 | union_layout.tag_index = field_index; |
| | 1927 | field_index += 1; |
| | 1928 | } |
| | 1929 | |
| | 1930 | if (union_layout.padding_size != 0) { |
| | 1931 | union_layout.padding_index = field_index; |
| | 1932 | field_index += 1; |
| | 1933 | } |
| | 1934 | |
| | 1935 | union_layout.total_fields = field_index; |
| | 1936 | |
| | 1937 | return union_layout; |
| | 1938 | } |
| | 1939 | |
| 1857 | /// The SPIR-V backend is not yet advanced enough to support the std testing infrastructure. | 1940 | /// The SPIR-V backend is not yet advanced enough to support the std testing infrastructure. |
| 1858 | /// In order to be able to run tests, we "temporarily" lower test kernels into separate entry- | 1941 | /// In order to be able to run tests, we "temporarily" lower test kernels into separate entry- |
| 1859 | /// points. The test executor will then be able to invoke these to run the tests. | 1942 | /// points. The test executor will then be able to invoke these to run the tests. |
| ... | @@ -1995,22 +2078,60 @@ pub const DeclGen = struct { | ... | @@ -1995,22 +2078,60 @@ pub const DeclGen = struct { |
| 1995 | return self.todo("importing extern variables", .{}); | 2078 | return self.todo("importing extern variables", .{}); |
| 1996 | } | 2079 | } |
| 1997 | | 2080 | |
| 1998 | // TODO: integrate with variable(). | 2081 | // Currently, initializers for CrossWorkgroup variables is not implemented |
| | 2082 | // in Mesa. Therefore we generate an initialization kernel instead. |
| | 2083 | |
| | 2084 | const void_ty_ref = try self.resolveType(Type.void, .direct); |
| | 2085 | |
| | 2086 | const initializer_proto_ty_ref = try self.spv.resolve(.{ .function_type = .{ |
| | 2087 | .return_type = void_ty_ref, |
| | 2088 | .parameters = &.{}, |
| | 2089 | } }); |
| 1999 | | 2090 | |
| | 2091 | // Generate the actual variable for the global... |
| 2000 | const final_storage_class = spvStorageClass(decl.@"addrspace"); | 2092 | const final_storage_class = spvStorageClass(decl.@"addrspace"); |
| 2001 | const actual_storage_class = switch (final_storage_class) { | 2093 | const actual_storage_class = switch (final_storage_class) { |
| 2002 | .Generic => .CrossWorkgroup, | 2094 | .Generic => .CrossWorkgroup, |
| 2003 | else => final_storage_class, | 2095 | else => final_storage_class, |
| 2004 | }; | 2096 | }; |
| 2005 | | 2097 | |
| 2006 | try self.lowerIndirectConstant( | 2098 | const ty_ref = try self.resolveType(decl.ty, .indirect); |
| 2007 | spv_decl_index, | 2099 | const ptr_ty_ref = try self.spv.ptrType(ty_ref, actual_storage_class); |
| 2008 | decl.ty, | 2100 | |
| 2009 | init_val, | 2101 | const begin = self.spv.beginGlobal(); |
| 2010 | actual_storage_class, | 2102 | try self.spv.globals.section.emit(self.spv.gpa, .OpVariable, .{ |
| 2011 | final_storage_class == .Generic, | 2103 | .id_result_type = self.typeId(ptr_ty_ref), |
| 2012 | @intCast(decl.alignment.toByteUnits(0)), | 2104 | .id_result = decl_id, |
| 2013 | ); | 2105 | .storage_class = actual_storage_class, |
| | 2106 | }); |
| | 2107 | // TODO: We should be able to get rid of this by now... |
| | 2108 | self.spv.endGlobal(spv_decl_index, begin); |
| | 2109 | |
| | 2110 | // Now emit the instructions that initialize the variable. |
| | 2111 | const initializer_id = self.spv.allocId(); |
| | 2112 | try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{ |
| | 2113 | .id_result_type = self.typeId(void_ty_ref), |
| | 2114 | .id_result = initializer_id, |
| | 2115 | .function_control = .{}, |
| | 2116 | .function_type = self.typeId(initializer_proto_ty_ref), |
| | 2117 | }); |
| | 2118 | const root_block_id = self.spv.allocId(); |
| | 2119 | try self.func.prologue.emit(self.spv.gpa, .OpLabel, .{ |
| | 2120 | .id_result = root_block_id, |
| | 2121 | }); |
| | 2122 | self.current_block_label_id = root_block_id; |
| | 2123 | |
| | 2124 | const val_id = try self.constant(decl.ty, init_val, .indirect); |
| | 2125 | try self.func.body.emit(self.spv.gpa, .OpStore, .{ |
| | 2126 | .pointer = decl_id, |
| | 2127 | .object = val_id, |
| | 2128 | }); |
| | 2129 | |
| | 2130 | try self.func.body.emit(self.spv.gpa, .OpReturn, {}); |
| | 2131 | try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {}); |
| | 2132 | try self.spv.addFunction(spv_decl_index, self.func); |
| | 2133 | |
| | 2134 | try self.spv.initializers.append(self.spv.gpa, initializer_id); |
| 2014 | } | 2135 | } |
| 2015 | } | 2136 | } |
| 2016 | | 2137 | |