| ... | @@ -765,21 +765,18 @@ pub const DeclGen = struct { | ... | @@ -765,21 +765,18 @@ pub const DeclGen = struct { |
| 765 | const is_pl = val.errorUnionIsPayload(); | 765 | const is_pl = val.errorUnionIsPayload(); |
| 766 | const error_val = if (!is_pl) val else Value.initTag(.zero); | 766 | const error_val = if (!is_pl) val else Value.initTag(.zero); |
| 767 | | 767 | |
| 768 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { | 768 | const eu_layout = dg.errorUnionLayout(payload_ty); |
| | 769 | if (!eu_layout.payload_has_bits) { |
| 769 | return try self.lower(Type.anyerror, error_val); | 770 | return try self.lower(Type.anyerror, error_val); |
| 770 | } | 771 | } |
| 771 | | 772 | |
| 772 | const payload_align = payload_ty.abiAlignment(target); | | |
| 773 | const error_align = Type.anyerror.abiAlignment(target); | | |
| 774 | | | |
| 775 | const payload_size = payload_ty.abiSize(target); | 773 | const payload_size = payload_ty.abiSize(target); |
| 776 | const error_size = Type.anyerror.abiAlignment(target); | 774 | const error_size = Type.anyerror.abiAlignment(target); |
| 777 | const ty_size = ty.abiSize(target); | 775 | const ty_size = ty.abiSize(target); |
| 778 | const padding = ty_size - payload_size - error_size; | 776 | const padding = ty_size - payload_size - error_size; |
| 779 | | | |
| 780 | const payload_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef); | 777 | const payload_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef); |
| 781 | | 778 | |
| 782 | if (error_align > payload_align) { | 779 | if (eu_layout.error_first) { |
| 783 | try self.lower(Type.anyerror, error_val); | 780 | try self.lower(Type.anyerror, error_val); |
| 784 | try self.lower(payload_ty, payload_val); | 781 | try self.lower(payload_ty, payload_val); |
| 785 | } else { | 782 | } else { |
| ... | @@ -1277,18 +1274,16 @@ pub const DeclGen = struct { | ... | @@ -1277,18 +1274,16 @@ pub const DeclGen = struct { |
| 1277 | .ErrorUnion => { | 1274 | .ErrorUnion => { |
| 1278 | const payload_ty = ty.errorUnionPayload(); | 1275 | const payload_ty = ty.errorUnionPayload(); |
| 1279 | const error_ty_ref = try self.resolveType(Type.anyerror, .indirect); | 1276 | const error_ty_ref = try self.resolveType(Type.anyerror, .indirect); |
| 1280 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { | 1277 | |
| | 1278 | const eu_layout = self.errorUnionLayout(payload_ty); |
| | 1279 | if (!eu_layout.payload_has_bits) { |
| 1281 | return error_ty_ref; | 1280 | return error_ty_ref; |
| 1282 | } | 1281 | } |
| 1283 | | 1282 | |
| 1284 | const payload_ty_ref = try self.resolveType(payload_ty, .indirect); | 1283 | const payload_ty_ref = try self.resolveType(payload_ty, .indirect); |
| 1285 | | 1284 | |
| 1286 | const payload_align = payload_ty.abiAlignment(target); | | |
| 1287 | const error_align = Type.anyerror.abiAlignment(target); | | |
| 1288 | | | |
| 1289 | var members = std.BoundedArray(SpvType.Payload.Struct.Member, 2){}; | 1285 | var members = std.BoundedArray(SpvType.Payload.Struct.Member, 2){}; |
| 1290 | // Similar to unions, we're going to put the most aligned member first. | 1286 | if (eu_layout.error_first) { |
| 1291 | if (error_align > payload_align) { | | |
| 1292 | // Put the error first | 1287 | // Put the error first |
| 1293 | members.appendAssumeCapacity(.{ .ty = error_ty_ref, .name = "error" }); | 1288 | members.appendAssumeCapacity(.{ .ty = error_ty_ref, .name = "error" }); |
| 1294 | members.appendAssumeCapacity(.{ .ty = payload_ty_ref, .name = "payload" }); | 1289 | members.appendAssumeCapacity(.{ .ty = payload_ty_ref, .name = "payload" }); |
| ... | @@ -1336,6 +1331,34 @@ pub const DeclGen = struct { | ... | @@ -1336,6 +1331,34 @@ pub const DeclGen = struct { |
| 1336 | }; | 1331 | }; |
| 1337 | } | 1332 | } |
| 1338 | | 1333 | |
| | 1334 | const ErrorUnionLayout = struct { |
| | 1335 | payload_has_bits: bool, |
| | 1336 | error_first: bool, |
| | 1337 | |
| | 1338 | fn errorFieldIndex(self: @This()) u32 { |
| | 1339 | assert(self.payload_has_bits); |
| | 1340 | return if (self.error_first) 0 else 1; |
| | 1341 | } |
| | 1342 | |
| | 1343 | fn payloadFieldIndex(self: @This()) u32 { |
| | 1344 | assert(self.payload_has_bits); |
| | 1345 | return if (self.error_first) 1 else 0; |
| | 1346 | } |
| | 1347 | }; |
| | 1348 | |
| | 1349 | fn errorUnionLayout(self: *DeclGen, payload_ty: Type) ErrorUnionLayout { |
| | 1350 | const target = self.getTarget(); |
| | 1351 | |
| | 1352 | const error_align = Type.anyerror.abiAlignment(target); |
| | 1353 | const payload_align = payload_ty.abiAlignment(target); |
| | 1354 | |
| | 1355 | const error_first = error_align > payload_align; |
| | 1356 | return .{ |
| | 1357 | .payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(), |
| | 1358 | .error_first = error_first, |
| | 1359 | }; |
| | 1360 | } |
| | 1361 | |
| 1339 | /// The SPIR-V backend is not yet advanced enough to support the std testing infrastructure. | 1362 | /// The SPIR-V backend is not yet advanced enough to support the std testing infrastructure. |
| 1340 | /// In order to be able to run tests, we "temporarily" lower test kernels into separate entry- | 1363 | /// In order to be able to run tests, we "temporarily" lower test kernels into separate entry- |
| 1341 | /// points. The test executor will then be able to invoke these to run the tests. | 1364 | /// points. The test executor will then be able to invoke these to run the tests. |
| ... | @@ -1585,6 +1608,7 @@ pub const DeclGen = struct { | ... | @@ -1585,6 +1608,7 @@ pub const DeclGen = struct { |
| 1585 | .loop => return self.airLoop(inst), | 1608 | .loop => return self.airLoop(inst), |
| 1586 | .ret => return self.airRet(inst), | 1609 | .ret => return self.airRet(inst), |
| 1587 | .ret_load => return self.airRetLoad(inst), | 1610 | .ret_load => return self.airRetLoad(inst), |
| | 1611 | .@"try" => try self.airTry(inst), |
| 1588 | .switch_br => return self.airSwitchBr(inst), | 1612 | .switch_br => return self.airSwitchBr(inst), |
| 1589 | .unreach => return self.airUnreach(), | 1613 | .unreach => return self.airUnreach(), |
| 1590 | | 1614 | |
| ... | @@ -1752,16 +1776,15 @@ pub const DeclGen = struct { | ... | @@ -1752,16 +1776,15 @@ pub const DeclGen = struct { |
| 1752 | const operand_ty_id = try self.resolveTypeId(operand_ty); | 1776 | const operand_ty_id = try self.resolveTypeId(operand_ty); |
| 1753 | const result_type_id = try self.resolveTypeId(result_ty); | 1777 | const result_type_id = try self.resolveTypeId(result_ty); |
| 1754 | | 1778 | |
| 1755 | const overflow_member_ty = try self.intType(.unsigned, info.bits); | 1779 | const overflow_member_ty_ref = try self.intType(.unsigned, info.bits); |
| 1756 | const overflow_member_ty_id = self.typeId(overflow_member_ty); | | |
| 1757 | | 1780 | |
| 1758 | const op_result_id = blk: { | 1781 | const op_result_id = blk: { |
| 1759 | // Construct the SPIR-V result type. | 1782 | // Construct the SPIR-V result type. |
| 1760 | // It is almost the same as the zig one, except that the fields must be the same type | 1783 | // It is almost the same as the zig one, except that the fields must be the same type |
| 1761 | // and they must be unsigned. | 1784 | // and they must be unsigned. |
| 1762 | const overflow_result_ty_ref = try self.spv.simpleStructType(&.{ | 1785 | const overflow_result_ty_ref = try self.spv.simpleStructType(&.{ |
| 1763 | .{ .ty = overflow_member_ty, .name = "res" }, | 1786 | .{ .ty = overflow_member_ty_ref, .name = "res" }, |
| 1764 | .{ .ty = overflow_member_ty, .name = "ov" }, | 1787 | .{ .ty = overflow_member_ty_ref, .name = "ov" }, |
| 1765 | }); | 1788 | }); |
| 1766 | const result_id = self.spv.allocId(); | 1789 | const result_id = self.spv.allocId(); |
| 1767 | try self.func.body.emit(self.spv.gpa, .OpIAddCarry, .{ | 1790 | try self.func.body.emit(self.spv.gpa, .OpIAddCarry, .{ |
| ... | @@ -1775,8 +1798,8 @@ pub const DeclGen = struct { | ... | @@ -1775,8 +1798,8 @@ pub const DeclGen = struct { |
| 1775 | | 1798 | |
| 1776 | // Now convert the SPIR-V flavor result into a Zig-flavor result. | 1799 | // Now convert the SPIR-V flavor result into a Zig-flavor result. |
| 1777 | // First, extract the two fields. | 1800 | // First, extract the two fields. |
| 1778 | const unsigned_result = try self.extractField(overflow_member_ty_id, op_result_id, 0); | 1801 | const unsigned_result = try self.extractField(overflow_member_ty_ref, op_result_id, 0); |
| 1779 | const overflow = try self.extractField(overflow_member_ty_id, op_result_id, 1); | 1802 | const overflow = try self.extractField(overflow_member_ty_ref, op_result_id, 1); |
| 1780 | | 1803 | |
| 1781 | // We need to convert the results to the types that Zig expects here. | 1804 | // We need to convert the results to the types that Zig expects here. |
| 1782 | // The `result` is the same type except unsigned, so we can just bitcast that. | 1805 | // The `result` is the same type except unsigned, so we can just bitcast that. |
| ... | @@ -1954,15 +1977,16 @@ pub const DeclGen = struct { | ... | @@ -1954,15 +1977,16 @@ pub const DeclGen = struct { |
| 1954 | return result_id; | 1977 | return result_id; |
| 1955 | } | 1978 | } |
| 1956 | | 1979 | |
| 1957 | fn extractField(self: *DeclGen, result_ty: IdResultType, object: IdRef, field: u32) !IdRef { | 1980 | fn extractField(self: *DeclGen, result_ty_ref: SpvType.Ref, object: IdRef, field: u32) !IdRef { |
| 1958 | const result_id = self.spv.allocId(); | 1981 | const result_id = self.spv.allocId(); |
| 1959 | const indexes = [_]u32{field}; | 1982 | const indexes = [_]u32{field}; |
| 1960 | try self.func.body.emit(self.spv.gpa, .OpCompositeExtract, .{ | 1983 | try self.func.body.emit(self.spv.gpa, .OpCompositeExtract, .{ |
| 1961 | .id_result_type = result_ty, | 1984 | .id_result_type = self.typeId(result_ty_ref), |
| 1962 | .id_result = result_id, | 1985 | .id_result = result_id, |
| 1963 | .composite = object, | 1986 | .composite = object, |
| 1964 | .indexes = &indexes, | 1987 | .indexes = &indexes, |
| 1965 | }); | 1988 | }); |
| | 1989 | // TODO: Convert bools, direct structs should have their field types as indirect values. |
| 1966 | return result_id; | 1990 | return result_id; |
| 1967 | } | 1991 | } |
| 1968 | | 1992 | |
| ... | @@ -1970,7 +1994,7 @@ pub const DeclGen = struct { | ... | @@ -1970,7 +1994,7 @@ pub const DeclGen = struct { |
| 1970 | if (self.liveness.isUnused(inst)) return null; | 1994 | if (self.liveness.isUnused(inst)) return null; |
| 1971 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1995 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1972 | return try self.extractField( | 1996 | return try self.extractField( |
| 1973 | try self.resolveTypeId(self.air.typeOfIndex(inst)), | 1997 | try self.resolveType(self.air.typeOfIndex(inst), .direct), |
| 1974 | try self.resolve(ty_op.operand), | 1998 | try self.resolve(ty_op.operand), |
| 1975 | field, | 1999 | field, |
| 1976 | ); | 2000 | ); |
| ... | @@ -2451,6 +2475,66 @@ pub const DeclGen = struct { | ... | @@ -2451,6 +2475,66 @@ pub const DeclGen = struct { |
| 2451 | }); | 2475 | }); |
| 2452 | } | 2476 | } |
| 2453 | | 2477 | |
| | 2478 | fn airTry(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| | 2479 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| | 2480 | const err_union_id = try self.resolve(pl_op.operand); |
| | 2481 | const extra = self.air.extraData(Air.Try, pl_op.payload); |
| | 2482 | const body = self.air.extra[extra.end..][0..extra.data.body_len]; |
| | 2483 | |
| | 2484 | const err_union_ty = self.air.typeOf(pl_op.operand); |
| | 2485 | const payload_ty = self.air.typeOfIndex(inst); |
| | 2486 | |
| | 2487 | const err_ty_ref = try self.resolveType(Type.anyerror, .direct); |
| | 2488 | const payload_ty_ref = try self.resolveType(payload_ty, .direct); |
| | 2489 | const bool_ty_ref = try self.resolveType(Type.bool, .direct); |
| | 2490 | |
| | 2491 | const eu_layout = self.errorUnionLayout(payload_ty); |
| | 2492 | |
| | 2493 | if (!err_union_ty.errorUnionSet().errorSetIsEmpty()) { |
| | 2494 | const err_id = if (eu_layout.payload_has_bits) |
| | 2495 | try self.extractField(err_ty_ref, err_union_id, eu_layout.errorFieldIndex()) |
| | 2496 | else |
| | 2497 | err_union_id; |
| | 2498 | |
| | 2499 | const zero_id = try self.constInt(err_ty_ref, 0); |
| | 2500 | const is_err_id = self.spv.allocId(); |
| | 2501 | try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{ |
| | 2502 | .id_result_type = self.typeId(bool_ty_ref), |
| | 2503 | .id_result = is_err_id, |
| | 2504 | .operand_1 = err_id, |
| | 2505 | .operand_2 = zero_id, |
| | 2506 | }); |
| | 2507 | |
| | 2508 | // When there is an error, we must evaluate `body`. Otherwise we must continue |
| | 2509 | // with the current body. |
| | 2510 | // Just generate a new block here, then generate a new block inline for the remainder of the body. |
| | 2511 | |
| | 2512 | const err_block = self.spv.allocId(); |
| | 2513 | const ok_block = self.spv.allocId(); |
| | 2514 | |
| | 2515 | // TODO: Merge block |
| | 2516 | try self.func.body.emit(self.spv.gpa, .OpBranchConditional, .{ |
| | 2517 | .condition = is_err_id, |
| | 2518 | .true_label = err_block, |
| | 2519 | .false_label = ok_block, |
| | 2520 | }); |
| | 2521 | |
| | 2522 | try self.beginSpvBlock(err_block); |
| | 2523 | try self.genBody(body); |
| | 2524 | |
| | 2525 | try self.beginSpvBlock(ok_block); |
| | 2526 | // Now just extract the payload, if required. |
| | 2527 | } |
| | 2528 | if (self.liveness.isUnused(inst)) { |
| | 2529 | return null; |
| | 2530 | } |
| | 2531 | if (!eu_layout.payload_has_bits) { |
| | 2532 | return null; |
| | 2533 | } |
| | 2534 | |
| | 2535 | return try self.extractField(payload_ty_ref, err_union_id, eu_layout.payloadFieldIndex()); |
| | 2536 | } |
| | 2537 | |
| 2454 | fn airSwitchBr(self: *DeclGen, inst: Air.Inst.Index) !void { | 2538 | fn airSwitchBr(self: *DeclGen, inst: Air.Inst.Index) !void { |
| 2455 | const target = self.getTarget(); | 2539 | const target = self.getTarget(); |
| 2456 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; | 2540 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |