| author | |
| committer | |
| log | 6bb6013d3a4da18ca79c5d4d882920cc147520bf |
| tree | 8ddc329028ab11506fc22fd9a9a673243e26628a |
| parent | 298328581534de4b7cb984f7d5e8a5c4080a881c |
| parent | 64563e2fffd0e304019c343a32f31c925be20ea2 |
| signature |
spirv: remove prune_unused ISel15 files changed, 154 insertions(+), 484 deletions(-)
src/codegen/spirv/CodeGen.zig+96-105| ... | @@ -274,6 +274,13 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void { | ... | @@ -274,6 +274,13 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void { |
| 274 | .storage_class = storage_class, | 274 | .storage_class = storage_class, |
| 275 | }); | 275 | }); |
| 276 | 276 | ||
| 277 | if (nav.getAlignment() != ty.abiAlignment(zcu)) { | ||
| 278 | if (target.os.tag != .opencl) return cg.fail("cannot apply alignment to variables", .{}); | ||
| 279 | try cg.module.decorate(result_id, .{ | ||
| 280 | .alignment = .{ .alignment = @intCast(nav.getAlignment().toByteUnits().?) }, | ||
| 281 | }); | ||
| 282 | } | ||
| 283 | |||
| 277 | switch (target.os.tag) { | 284 | switch (target.os.tag) { |
| 278 | .vulkan, .opengl => { | 285 | .vulkan, .opengl => { |
| 279 | if (ty.zigTypeTag(zcu) == .@"struct") { | 286 | if (ty.zigTypeTag(zcu) == .@"struct") { |
| ... | @@ -348,7 +355,7 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void { | ... | @@ -348,7 +355,7 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void { |
| 348 | .id_result_type = ptr_ty_id, | 355 | .id_result_type = ptr_ty_id, |
| 349 | .id_result = result_id, | 356 | .id_result = result_id, |
| 350 | .set = try cg.module.importInstructionSet(.zig), | 357 | .set = try cg.module.importInstructionSet(.zig), |
| 351 | .instruction = .{ .inst = 0 }, // TODO: Put this definition somewhere... | 358 | .instruction = .{ .inst = @intFromEnum(spec.Zig.InvocationGlobal) }, |
| 352 | .id_ref_4 = &.{initializer_id}, | 359 | .id_ref_4 = &.{initializer_id}, |
| 353 | }); | 360 | }); |
| 354 | } else { | 361 | } else { |
| ... | @@ -356,7 +363,7 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void { | ... | @@ -356,7 +363,7 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void { |
| 356 | .id_result_type = ptr_ty_id, | 363 | .id_result_type = ptr_ty_id, |
| 357 | .id_result = result_id, | 364 | .id_result = result_id, |
| 358 | .set = try cg.module.importInstructionSet(.zig), | 365 | .set = try cg.module.importInstructionSet(.zig), |
| 359 | .instruction = .{ .inst = 0 }, // TODO: Put this definition somewhere... | 366 | .instruction = .{ .inst = @intFromEnum(spec.Zig.InvocationGlobal) }, |
| 360 | .id_ref_4 = &.{}, | 367 | .id_ref_4 = &.{}, |
| 361 | }); | 368 | }); |
| 362 | } | 369 | } |
| ... | @@ -498,7 +505,7 @@ fn resolveUav(cg: *CodeGen, val: InternPool.Index) !Id { | ... | @@ -498,7 +505,7 @@ fn resolveUav(cg: *CodeGen, val: InternPool.Index) !Id { |
| 498 | .id_result_type = fn_decl_ptr_ty_id, | 505 | .id_result_type = fn_decl_ptr_ty_id, |
| 499 | .id_result = result_id, | 506 | .id_result = result_id, |
| 500 | .set = try cg.module.importInstructionSet(.zig), | 507 | .set = try cg.module.importInstructionSet(.zig), |
| 501 | .instruction = .{ .inst = 0 }, // TODO: Put this definition somewhere... | 508 | .instruction = .{ .inst = @intFromEnum(spec.Zig.InvocationGlobal) }, |
| 502 | .id_ref_4 = &.{initializer_id}, | 509 | .id_ref_4 = &.{initializer_id}, |
| 503 | }); | 510 | }); |
| 504 | } | 511 | } |
| ... | @@ -1037,9 +1044,18 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id { | ... | @@ -1037,9 +1044,18 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id { |
| 1037 | const gpa = cg.module.gpa; | 1044 | const gpa = cg.module.gpa; |
| 1038 | const pt = cg.pt; | 1045 | const pt = cg.pt; |
| 1039 | const zcu = cg.module.zcu; | 1046 | const zcu = cg.module.zcu; |
| 1047 | const target = zcu.getTarget(); | ||
| 1040 | switch (derivation) { | 1048 | switch (derivation) { |
| 1041 | .comptime_alloc_ptr, .comptime_field_ptr => unreachable, | 1049 | .comptime_alloc_ptr, .comptime_field_ptr => unreachable, |
| 1042 | .int => |int| { | 1050 | .int => |int| { |
| 1051 | if (target.os.tag != .opencl) { | ||
| 1052 | if (int.ptr_ty.ptrAddressSpace(zcu) != .physical_storage_buffer) { | ||
| 1053 | return cg.fail( | ||
| 1054 | "cannot cast integer to pointer with address space '{s}'", | ||
| 1055 | .{@tagName(int.ptr_ty.ptrAddressSpace(zcu))}, | ||
| 1056 | ); | ||
| 1057 | } | ||
| 1058 | } | ||
| 1043 | const result_ty_id = try cg.resolveType(int.ptr_ty, .direct); | 1059 | const result_ty_id = try cg.resolveType(int.ptr_ty, .direct); |
| 1044 | // TODO: This can probably be an OpSpecConstantOp Bitcast, but | 1060 | // TODO: This can probably be an OpSpecConstantOp Bitcast, but |
| 1045 | // that is not implemented by Mesa yet. Therefore, just generate it | 1061 | // that is not implemented by Mesa yet. Therefore, just generate it |
| ... | @@ -1137,7 +1153,7 @@ fn constantUavRef( | ... | @@ -1137,7 +1153,7 @@ fn constantUavRef( |
| 1137 | // Uav refs are always generic. | 1153 | // Uav refs are always generic. |
| 1138 | assert(ty.ptrAddressSpace(zcu) == .generic); | 1154 | assert(ty.ptrAddressSpace(zcu) == .generic); |
| 1139 | const uav_ty_id = try cg.resolveType(uav_ty, .indirect); | 1155 | const uav_ty_id = try cg.resolveType(uav_ty, .indirect); |
| 1140 | const decl_ptr_ty_id = try cg.module.ptrType(uav_ty_id, .generic); | 1156 | const decl_ptr_ty_id = try cg.module.ptrType(uav_ty_id, .function); |
| 1141 | const ptr_id = try cg.resolveUav(uav.val); | 1157 | const ptr_id = try cg.resolveUav(uav.val); |
| 1142 | 1158 | ||
| 1143 | if (decl_ptr_ty_id != ty_id) { | 1159 | if (decl_ptr_ty_id != ty_id) { |
| ... | @@ -1327,7 +1343,10 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { | ... | @@ -1327,7 +1343,10 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { |
| 1327 | }, | 1343 | }, |
| 1328 | .void => switch (repr) { | 1344 | .void => switch (repr) { |
| 1329 | .direct => return try cg.module.voidType(), | 1345 | .direct => return try cg.module.voidType(), |
| 1330 | .indirect => return try cg.module.opaqueType("void"), | 1346 | .indirect => { |
| 1347 | if (target.os.tag != .opencl) return cg.fail("cannot generate opaque type", .{}); | ||
| 1348 | return try cg.module.opaqueType("void"); | ||
| 1349 | }, | ||
| 1331 | }, | 1350 | }, |
| 1332 | .bool => switch (repr) { | 1351 | .bool => switch (repr) { |
| 1333 | .direct => return try cg.module.boolType(), | 1352 | .direct => return try cg.module.boolType(), |
| ... | @@ -1337,6 +1356,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { | ... | @@ -1337,6 +1356,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { |
| 1337 | const int_info = ty.intInfo(zcu); | 1356 | const int_info = ty.intInfo(zcu); |
| 1338 | if (int_info.bits == 0) { | 1357 | if (int_info.bits == 0) { |
| 1339 | assert(repr == .indirect); | 1358 | assert(repr == .indirect); |
| 1359 | if (target.os.tag != .opencl) return cg.fail("cannot generate opaque type", .{}); | ||
| 1340 | return try cg.module.opaqueType("u0"); | 1360 | return try cg.module.opaqueType("u0"); |
| 1341 | } | 1361 | } |
| 1342 | return try cg.module.intType(int_info.signedness, int_info.bits); | 1362 | return try cg.module.intType(int_info.signedness, int_info.bits); |
| ... | @@ -1369,6 +1389,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { | ... | @@ -1369,6 +1389,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { |
| 1369 | 1389 | ||
| 1370 | if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 1390 | if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 1371 | assert(repr == .indirect); | 1391 | assert(repr == .indirect); |
| 1392 | if (target.os.tag != .opencl) return cg.fail("cannot generate opaque type", .{}); | ||
| 1372 | return try cg.module.opaqueType("zero-sized-array"); | 1393 | return try cg.module.opaqueType("zero-sized-array"); |
| 1373 | } else if (total_len == 0) { | 1394 | } else if (total_len == 0) { |
| 1374 | // The size of the array would be 0, but that is not allowed in SPIR-V. | 1395 | // The size of the array would be 0, but that is not allowed in SPIR-V. |
| ... | @@ -1590,6 +1611,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { | ... | @@ -1590,6 +1611,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { |
| 1590 | return try cg.module.structType(&member_types, &member_names, null, .none); | 1611 | return try cg.module.structType(&member_types, &member_names, null, .none); |
| 1591 | }, | 1612 | }, |
| 1592 | .@"opaque" => { | 1613 | .@"opaque" => { |
| 1614 | if (target.os.tag != .opencl) return cg.fail("cannot generate opaque type", .{}); | ||
| 1593 | const type_name = try cg.resolveTypeName(ty); | 1615 | const type_name = try cg.resolveTypeName(ty); |
| 1594 | defer gpa.free(type_name); | 1616 | defer gpa.free(type_name); |
| 1595 | return try cg.module.opaqueType(type_name); | 1617 | return try cg.module.opaqueType(type_name); |
| ... | @@ -2510,11 +2532,7 @@ fn generateTestEntryPoint( | ... | @@ -2510,11 +2532,7 @@ fn generateTestEntryPoint( |
| 2510 | try cg.module.declareEntryPoint(spv_decl_index, test_name, execution_mode, null); | 2532 | try cg.module.declareEntryPoint(spv_decl_index, test_name, execution_mode, null); |
| 2511 | } | 2533 | } |
| 2512 | 2534 | ||
| 2513 | fn intFromBool(cg: *CodeGen, value: Temporary) !Temporary { | 2535 | fn intFromBool(cg: *CodeGen, value: Temporary, result_ty: Type) !Temporary { |
| 2514 | return try cg.intFromBool2(value, Type.u1); | ||
| 2515 | } | ||
| 2516 | |||
| 2517 | fn intFromBool2(cg: *CodeGen, value: Temporary, result_ty: Type) !Temporary { | ||
| 2518 | const zero_id = try cg.constInt(result_ty, 0); | 2536 | const zero_id = try cg.constInt(result_ty, 0); |
| 2519 | const one_id = try cg.constInt(result_ty, 1); | 2537 | const one_id = try cg.constInt(result_ty, 1); |
| 2520 | 2538 | ||
| ... | @@ -2558,7 +2576,7 @@ fn convertToIndirect(cg: *CodeGen, ty: Type, operand_id: Id) !Id { | ... | @@ -2558,7 +2576,7 @@ fn convertToIndirect(cg: *CodeGen, ty: Type, operand_id: Id) !Id { |
| 2558 | const zcu = cg.module.zcu; | 2576 | const zcu = cg.module.zcu; |
| 2559 | switch (ty.scalarType(zcu).zigTypeTag(zcu)) { | 2577 | switch (ty.scalarType(zcu).zigTypeTag(zcu)) { |
| 2560 | .bool => { | 2578 | .bool => { |
| 2561 | const result = try cg.intFromBool(Temporary.init(ty, operand_id)); | 2579 | const result = try cg.intFromBool(.init(ty, operand_id), .u1); |
| 2562 | return try result.materialize(cg); | 2580 | return try result.materialize(cg); |
| 2563 | }, | 2581 | }, |
| 2564 | else => return operand_id, | 2582 | else => return operand_id, |
| ... | @@ -2958,7 +2976,7 @@ fn normalize(cg: *CodeGen, value: Temporary, info: ArithmeticTypeInfo) !Temporar | ... | @@ -2958,7 +2976,7 @@ fn normalize(cg: *CodeGen, value: Temporary, info: ArithmeticTypeInfo) !Temporar |
| 2958 | .composite_integer, .integer, .bool, .float => return value, | 2976 | .composite_integer, .integer, .bool, .float => return value, |
| 2959 | .strange_integer => switch (info.signedness) { | 2977 | .strange_integer => switch (info.signedness) { |
| 2960 | .unsigned => { | 2978 | .unsigned => { |
| 2961 | const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1; | 2979 | const mask_value = @as(u64, std.math.maxInt(u64)) >> @as(u6, @intCast(64 - info.bits)); |
| 2962 | const mask_id = try cg.constInt(ty.scalarType(zcu), mask_value); | 2980 | const mask_id = try cg.constInt(ty.scalarType(zcu), mask_value); |
| 2963 | return try cg.buildBinary(.OpBitwiseAnd, value, Temporary.init(ty.scalarType(zcu), mask_id)); | 2981 | return try cg.buildBinary(.OpBitwiseAnd, value, Temporary.init(ty.scalarType(zcu), mask_id)); |
| 2964 | }, | 2982 | }, |
| ... | @@ -2997,28 +3015,12 @@ fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) !?Id { | ... | @@ -2997,28 +3015,12 @@ fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 2997 | 3015 | ||
| 2998 | const div = try cg.buildBinary(.OpSDiv, lhs, rhs); | 3016 | const div = try cg.buildBinary(.OpSDiv, lhs, rhs); |
| 2999 | const rem = try cg.buildBinary(.OpSRem, lhs, rhs); | 3017 | const rem = try cg.buildBinary(.OpSRem, lhs, rhs); |
| 3000 | |||
| 3001 | const zero: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, 0)); | 3018 | const zero: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, 0)); |
| 3002 | 3019 | const rem_non_zero = try cg.buildCmp(.OpINotEqual, rem, zero); | |
| 3003 | const rem_is_not_zero = try cg.buildCmp(.OpINotEqual, rem, zero); | 3020 | const lhs_rhs_xor = try cg.buildBinary(.OpBitwiseXor, lhs, rhs); |
| 3004 | 3021 | const signs_differ = try cg.buildCmp(.OpSLessThan, lhs_rhs_xor, zero); | |
| 3005 | const result_negative = try cg.buildCmp( | 3022 | const adjust = try cg.buildBinary(.OpLogicalAnd, rem_non_zero, signs_differ); |
| 3006 | .OpLogicalNotEqual, | 3023 | const result = try cg.buildBinary(.OpISub, div, try cg.intFromBool(adjust, div.ty)); |
| 3007 | try cg.buildCmp(.OpSLessThan, lhs, zero), | ||
| 3008 | try cg.buildCmp(.OpSLessThan, rhs, zero), | ||
| 3009 | ); | ||
| 3010 | const rem_is_not_zero_and_result_is_negative = try cg.buildBinary( | ||
| 3011 | .OpLogicalAnd, | ||
| 3012 | rem_is_not_zero, | ||
| 3013 | result_negative, | ||
| 3014 | ); | ||
| 3015 | |||
| 3016 | const result = try cg.buildBinary( | ||
| 3017 | .OpISub, | ||
| 3018 | div, | ||
| 3019 | try cg.intFromBool2(rem_is_not_zero_and_result_is_negative, div.ty), | ||
| 3020 | ); | ||
| 3021 | |||
| 3022 | return try result.materialize(cg); | 3024 | return try result.materialize(cg); |
| 3023 | }, | 3025 | }, |
| 3024 | .float => { | 3026 | .float => { |
| ... | @@ -3032,10 +3034,8 @@ fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) !?Id { | ... | @@ -3032,10 +3034,8 @@ fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 3032 | 3034 | ||
| 3033 | fn airDivTrunc(cg: *CodeGen, inst: Air.Inst.Index) !?Id { | 3035 | fn airDivTrunc(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 3034 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 3036 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 3035 | |||
| 3036 | const lhs = try cg.temporary(bin_op.lhs); | 3037 | const lhs = try cg.temporary(bin_op.lhs); |
| 3037 | const rhs = try cg.temporary(bin_op.rhs); | 3038 | const rhs = try cg.temporary(bin_op.rhs); |
| 3038 | |||
| 3039 | const info = cg.arithmeticTypeInfo(lhs.ty); | 3039 | const info = cg.arithmeticTypeInfo(lhs.ty); |
| 3040 | switch (info.class) { | 3040 | switch (info.class) { |
| 3041 | .composite_integer => unreachable, // TODO | 3041 | .composite_integer => unreachable, // TODO |
| ... | @@ -3073,12 +3073,9 @@ fn airArithOp( | ... | @@ -3073,12 +3073,9 @@ fn airArithOp( |
| 3073 | comptime uop: Opcode, | 3073 | comptime uop: Opcode, |
| 3074 | ) !?Id { | 3074 | ) !?Id { |
| 3075 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 3075 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 3076 | |||
| 3077 | const lhs = try cg.temporary(bin_op.lhs); | 3076 | const lhs = try cg.temporary(bin_op.lhs); |
| 3078 | const rhs = try cg.temporary(bin_op.rhs); | 3077 | const rhs = try cg.temporary(bin_op.rhs); |
| 3079 | |||
| 3080 | const info = cg.arithmeticTypeInfo(lhs.ty); | 3078 | const info = cg.arithmeticTypeInfo(lhs.ty); |
| 3081 | |||
| 3082 | const result = switch (info.class) { | 3079 | const result = switch (info.class) { |
| 3083 | .composite_integer => unreachable, // TODO | 3080 | .composite_integer => unreachable, // TODO |
| 3084 | .integer, .strange_integer => switch (info.signedness) { | 3081 | .integer, .strange_integer => switch (info.signedness) { |
| ... | @@ -3088,7 +3085,6 @@ fn airArithOp( | ... | @@ -3088,7 +3085,6 @@ fn airArithOp( |
| 3088 | .float => try cg.buildBinary(fop, lhs, rhs), | 3085 | .float => try cg.buildBinary(fop, lhs, rhs), |
| 3089 | .bool => unreachable, | 3086 | .bool => unreachable, |
| 3090 | }; | 3087 | }; |
| 3091 | |||
| 3092 | return try result.materialize(cg); | 3088 | return try result.materialize(cg); |
| 3093 | } | 3089 | } |
| 3094 | 3090 | ||
| ... | @@ -3105,12 +3101,10 @@ fn abs(cg: *CodeGen, result_ty: Type, value: Temporary) !Temporary { | ... | @@ -3105,12 +3101,10 @@ fn abs(cg: *CodeGen, result_ty: Type, value: Temporary) !Temporary { |
| 3105 | const zcu = cg.module.zcu; | 3101 | const zcu = cg.module.zcu; |
| 3106 | const target = cg.module.zcu.getTarget(); | 3102 | const target = cg.module.zcu.getTarget(); |
| 3107 | const operand_info = cg.arithmeticTypeInfo(value.ty); | 3103 | const operand_info = cg.arithmeticTypeInfo(value.ty); |
| 3108 | |||
| 3109 | switch (operand_info.class) { | 3104 | switch (operand_info.class) { |
| 3110 | .float => return try cg.buildUnary(.f_abs, value), | 3105 | .float => return try cg.buildUnary(.f_abs, value), |
| 3111 | .integer, .strange_integer => { | 3106 | .integer, .strange_integer => { |
| 3112 | const abs_value = try cg.buildUnary(.i_abs, value); | 3107 | const abs_value = try cg.buildUnary(.i_abs, value); |
| 3113 | |||
| 3114 | switch (target.os.tag) { | 3108 | switch (target.os.tag) { |
| 3115 | .vulkan, .opengl => { | 3109 | .vulkan, .opengl => { |
| 3116 | if (value.ty.intInfo(zcu).signedness == .signed) { | 3110 | if (value.ty.intInfo(zcu).signedness == .signed) { |
| ... | @@ -3119,7 +3113,6 @@ fn abs(cg: *CodeGen, result_ty: Type, value: Temporary) !Temporary { | ... | @@ -3119,7 +3113,6 @@ fn abs(cg: *CodeGen, result_ty: Type, value: Temporary) !Temporary { |
| 3119 | }, | 3113 | }, |
| 3120 | else => {}, | 3114 | else => {}, |
| 3121 | } | 3115 | } |
| 3122 | |||
| 3123 | return try cg.normalize(abs_value, cg.arithmeticTypeInfo(result_ty)); | 3116 | return try cg.normalize(abs_value, cg.arithmeticTypeInfo(result_ty)); |
| 3124 | }, | 3117 | }, |
| 3125 | .composite_integer => unreachable, // TODO | 3118 | .composite_integer => unreachable, // TODO |
| ... | @@ -3134,19 +3127,18 @@ fn airAddSubOverflow( | ... | @@ -3134,19 +3127,18 @@ fn airAddSubOverflow( |
| 3134 | u_opcode: Opcode, | 3127 | u_opcode: Opcode, |
| 3135 | s_opcode: Opcode, | 3128 | s_opcode: Opcode, |
| 3136 | ) !?Id { | 3129 | ) !?Id { |
| 3137 | _ = s_opcode; | ||
| 3138 | // Note: OpIAddCarry and OpISubBorrow are not really useful here: For unsigned numbers, | 3130 | // Note: OpIAddCarry and OpISubBorrow are not really useful here: For unsigned numbers, |
| 3139 | // there is in both cases only one extra operation required. For signed operations, | 3131 | // there is in both cases only one extra operation required. For signed operations, |
| 3140 | // the overflow bit is set then going from 0x80.. to 0x00.., but this doesn't actually | 3132 | // the overflow bit is set then going from 0x80.. to 0x00.., but this doesn't actually |
| 3141 | // normally set a carry bit. So the SPIR-V overflow operations are not particularly | 3133 | // normally set a carry bit. So the SPIR-V overflow operations are not particularly |
| 3142 | // useful here. | 3134 | // useful here. |
| 3143 | 3135 | ||
| 3136 | _ = s_opcode; | ||
| 3137 | |||
| 3144 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 3138 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 3145 | const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data; | 3139 | const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data; |
| 3146 | |||
| 3147 | const lhs = try cg.temporary(extra.lhs); | 3140 | const lhs = try cg.temporary(extra.lhs); |
| 3148 | const rhs = try cg.temporary(extra.rhs); | 3141 | const rhs = try cg.temporary(extra.rhs); |
| 3149 | |||
| 3150 | const result_ty = cg.typeOfIndex(inst); | 3142 | const result_ty = cg.typeOfIndex(inst); |
| 3151 | 3143 | ||
| 3152 | const info = cg.arithmeticTypeInfo(lhs.ty); | 3144 | const info = cg.arithmeticTypeInfo(lhs.ty); |
| ... | @@ -3158,7 +3150,6 @@ fn airAddSubOverflow( | ... | @@ -3158,7 +3150,6 @@ fn airAddSubOverflow( |
| 3158 | 3150 | ||
| 3159 | const sum = try cg.buildBinary(add, lhs, rhs); | 3151 | const sum = try cg.buildBinary(add, lhs, rhs); |
| 3160 | const result = try cg.normalize(sum, info); | 3152 | const result = try cg.normalize(sum, info); |
| 3161 | |||
| 3162 | const overflowed = switch (info.signedness) { | 3153 | const overflowed = switch (info.signedness) { |
| 3163 | // Overflow happened if the result is smaller than either of the operands. It doesn't matter which. | 3154 | // Overflow happened if the result is smaller than either of the operands. It doesn't matter which. |
| 3164 | // For subtraction the conditions need to be swapped. | 3155 | // For subtraction the conditions need to be swapped. |
| ... | @@ -3173,38 +3164,31 @@ fn airAddSubOverflow( | ... | @@ -3173,38 +3164,31 @@ fn airAddSubOverflow( |
| 3173 | // and the result's sign is different from the minuend's (a's) sign. | 3164 | // and the result's sign is different from the minuend's (a's) sign. |
| 3174 | // (sign(a) != sign(b)) && (sign(a) != sign(result)) | 3165 | // (sign(a) != sign(b)) && (sign(a) != sign(result)) |
| 3175 | const zero: Temporary = .init(rhs.ty, try cg.constInt(rhs.ty, 0)); | 3166 | const zero: Temporary = .init(rhs.ty, try cg.constInt(rhs.ty, 0)); |
| 3176 | |||
| 3177 | const lhs_is_neg = try cg.buildCmp(.OpSLessThan, lhs, zero); | 3167 | const lhs_is_neg = try cg.buildCmp(.OpSLessThan, lhs, zero); |
| 3178 | const rhs_is_neg = try cg.buildCmp(.OpSLessThan, rhs, zero); | 3168 | const rhs_is_neg = try cg.buildCmp(.OpSLessThan, rhs, zero); |
| 3179 | const result_is_neg = try cg.buildCmp(.OpSLessThan, result, zero); | 3169 | const result_is_neg = try cg.buildCmp(.OpSLessThan, result, zero); |
| 3180 | |||
| 3181 | const signs_match = try cg.buildCmp(.OpLogicalEqual, lhs_is_neg, rhs_is_neg); | 3170 | const signs_match = try cg.buildCmp(.OpLogicalEqual, lhs_is_neg, rhs_is_neg); |
| 3182 | const result_sign_differs = try cg.buildCmp(.OpLogicalNotEqual, lhs_is_neg, result_is_neg); | 3171 | const result_sign_differs = try cg.buildCmp(.OpLogicalNotEqual, lhs_is_neg, result_is_neg); |
| 3183 | 3172 | const overflow_condition = switch (add) { | |
| 3184 | const overflow_condition = if (add == .OpIAdd) | 3173 | .OpIAdd => signs_match, |
| 3185 | signs_match | 3174 | .OpISub => try cg.buildUnary(.l_not, signs_match), |
| 3186 | else // .OpISub | 3175 | else => unreachable, |
| 3187 | try cg.buildUnary(.l_not, signs_match); | 3176 | }; |
| 3188 | |||
| 3189 | break :blk try cg.buildCmp(.OpLogicalAnd, overflow_condition, result_sign_differs); | 3177 | break :blk try cg.buildCmp(.OpLogicalAnd, overflow_condition, result_sign_differs); |
| 3190 | }, | 3178 | }, |
| 3191 | }; | 3179 | }; |
| 3192 | 3180 | ||
| 3193 | const ov = try cg.intFromBool(overflowed); | 3181 | const ov = try cg.intFromBool(overflowed, .u1); |
| 3194 | |||
| 3195 | const result_ty_id = try cg.resolveType(result_ty, .direct); | 3182 | const result_ty_id = try cg.resolveType(result_ty, .direct); |
| 3196 | return try cg.constructComposite(result_ty_id, &.{ try result.materialize(cg), try ov.materialize(cg) }); | 3183 | return try cg.constructComposite(result_ty_id, &.{ try result.materialize(cg), try ov.materialize(cg) }); |
| 3197 | } | 3184 | } |
| 3198 | 3185 | ||
| 3199 | fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id { | 3186 | fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 3200 | const pt = cg.pt; | 3187 | const pt = cg.pt; |
| 3201 | |||
| 3202 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 3188 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 3203 | const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data; | 3189 | const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data; |
| 3204 | |||
| 3205 | const lhs = try cg.temporary(extra.lhs); | 3190 | const lhs = try cg.temporary(extra.lhs); |
| 3206 | const rhs = try cg.temporary(extra.rhs); | 3191 | const rhs = try cg.temporary(extra.rhs); |
| 3207 | |||
| 3208 | const result_ty = cg.typeOfIndex(inst); | 3192 | const result_ty = cg.typeOfIndex(inst); |
| 3209 | 3193 | ||
| 3210 | const info = cg.arithmeticTypeInfo(lhs.ty); | 3194 | const info = cg.arithmeticTypeInfo(lhs.ty); |
| ... | @@ -3237,20 +3221,15 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id { | ... | @@ -3237,20 +3221,15 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 3237 | const op_ty = try pt.intType(.unsigned, op_ty_bits); | 3221 | const op_ty = try pt.intType(.unsigned, op_ty_bits); |
| 3238 | const casted_lhs = try cg.buildConvert(op_ty, lhs); | 3222 | const casted_lhs = try cg.buildConvert(op_ty, lhs); |
| 3239 | const casted_rhs = try cg.buildConvert(op_ty, rhs); | 3223 | const casted_rhs = try cg.buildConvert(op_ty, rhs); |
| 3240 | |||
| 3241 | const full_result = try cg.buildBinary(.OpIMul, casted_lhs, casted_rhs); | 3224 | const full_result = try cg.buildBinary(.OpIMul, casted_lhs, casted_rhs); |
| 3242 | |||
| 3243 | const low_bits = try cg.buildConvert(lhs.ty, full_result); | 3225 | const low_bits = try cg.buildConvert(lhs.ty, full_result); |
| 3244 | const result = try cg.normalize(low_bits, info); | 3226 | const result = try cg.normalize(low_bits, info); |
| 3245 | |||
| 3246 | // Shift the result bits away to get the overflow bits. | 3227 | // Shift the result bits away to get the overflow bits. |
| 3247 | const shift: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, info.bits)); | 3228 | const shift: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, info.bits)); |
| 3248 | const overflow = try cg.buildBinary(.OpShiftRightLogical, full_result, shift); | 3229 | const overflow = try cg.buildBinary(.OpShiftRightLogical, full_result, shift); |
| 3249 | |||
| 3250 | // Directly check if its zero in the op_ty without converting first. | 3230 | // Directly check if its zero in the op_ty without converting first. |
| 3251 | const zero: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, 0)); | 3231 | const zero: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, 0)); |
| 3252 | const overflowed = try cg.buildCmp(.OpINotEqual, zero, overflow); | 3232 | const overflowed = try cg.buildCmp(.OpINotEqual, zero, overflow); |
| 3253 | |||
| 3254 | break :blk .{ result, overflowed }; | 3233 | break :blk .{ result, overflowed }; |
| 3255 | } | 3234 | } |
| 3256 | 3235 | ||
| ... | @@ -3362,7 +3341,7 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id { | ... | @@ -3362,7 +3341,7 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 3362 | }, | 3341 | }, |
| 3363 | }; | 3342 | }; |
| 3364 | 3343 | ||
| 3365 | const ov = try cg.intFromBool(overflowed); | 3344 | const ov = try cg.intFromBool(overflowed, .u1); |
| 3366 | 3345 | ||
| 3367 | const result_ty_id = try cg.resolveType(result_ty, .direct); | 3346 | const result_ty_id = try cg.resolveType(result_ty, .direct); |
| 3368 | return try cg.constructComposite(result_ty_id, &.{ try result.materialize(cg), try ov.materialize(cg) }); | 3347 | return try cg.constructComposite(result_ty_id, &.{ try result.materialize(cg), try ov.materialize(cg) }); |
| ... | @@ -3403,7 +3382,7 @@ fn airShlOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id { | ... | @@ -3403,7 +3382,7 @@ fn airShlOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 3403 | }; | 3382 | }; |
| 3404 | 3383 | ||
| 3405 | const overflowed = try cg.buildCmp(.OpINotEqual, base, right); | 3384 | const overflowed = try cg.buildCmp(.OpINotEqual, base, right); |
| 3406 | const ov = try cg.intFromBool(overflowed); | 3385 | const ov = try cg.intFromBool(overflowed, .u1); |
| 3407 | 3386 | ||
| 3408 | const result_ty_id = try cg.resolveType(result_ty, .direct); | 3387 | const result_ty_id = try cg.resolveType(result_ty, .direct); |
| 3409 | return try cg.constructComposite(result_ty_id, &.{ try result.materialize(cg), try ov.materialize(cg) }); | 3388 | return try cg.constructComposite(result_ty_id, &.{ try result.materialize(cg), try ov.materialize(cg) }); |
| ... | @@ -3931,6 +3910,7 @@ fn bitCast( | ... | @@ -3931,6 +3910,7 @@ fn bitCast( |
| 3931 | ) !Id { | 3910 | ) !Id { |
| 3932 | const gpa = cg.module.gpa; | 3911 | const gpa = cg.module.gpa; |
| 3933 | const zcu = cg.module.zcu; | 3912 | const zcu = cg.module.zcu; |
| 3913 | const target = zcu.getTarget(); | ||
| 3934 | const src_ty_id = try cg.resolveType(src_ty, .direct); | 3914 | const src_ty_id = try cg.resolveType(src_ty, .direct); |
| 3935 | const dst_ty_id = try cg.resolveType(dst_ty, .direct); | 3915 | const dst_ty_id = try cg.resolveType(dst_ty, .direct); |
| 3936 | 3916 | ||
| ... | @@ -3941,6 +3921,15 @@ fn bitCast( | ... | @@ -3941,6 +3921,15 @@ fn bitCast( |
| 3941 | // See fn bitCast in llvm.zig | 3921 | // See fn bitCast in llvm.zig |
| 3942 | 3922 | ||
| 3943 | if (src_ty.zigTypeTag(zcu) == .int and dst_ty.isPtrAtRuntime(zcu)) { | 3923 | if (src_ty.zigTypeTag(zcu) == .int and dst_ty.isPtrAtRuntime(zcu)) { |
| 3924 | if (target.os.tag != .opencl) { | ||
| 3925 | if (dst_ty.ptrAddressSpace(zcu) != .physical_storage_buffer) { | ||
| 3926 | return cg.fail( | ||
| 3927 | "cannot cast integer to pointer with address space '{s}'", | ||
| 3928 | .{@tagName(dst_ty.ptrAddressSpace(zcu))}, | ||
| 3929 | ); | ||
| 3930 | } | ||
| 3931 | } | ||
| 3932 | |||
| 3944 | const result_id = cg.module.allocId(); | 3933 | const result_id = cg.module.allocId(); |
| 3945 | try cg.body.emit(gpa, .OpConvertUToPtr, .{ | 3934 | try cg.body.emit(gpa, .OpConvertUToPtr, .{ |
| 3946 | .id_result_type = dst_ty_id, | 3935 | .id_result_type = dst_ty_id, |
| ... | @@ -3967,7 +3956,8 @@ fn bitCast( | ... | @@ -3967,7 +3956,8 @@ fn bitCast( |
| 3967 | 3956 | ||
| 3968 | const dst_ptr_ty_id = try cg.module.ptrType(dst_ty_id, .function); | 3957 | const dst_ptr_ty_id = try cg.module.ptrType(dst_ty_id, .function); |
| 3969 | 3958 | ||
| 3970 | const tmp_id = try cg.alloc(src_ty, .{ .storage_class = .function }); | 3959 | const src_ty_indirect_id = try cg.resolveType(src_ty, .indirect); |
| 3960 | const tmp_id = try cg.alloc(src_ty_indirect_id, null); | ||
| 3971 | try cg.store(src_ty, tmp_id, src_id, .{}); | 3961 | try cg.store(src_ty, tmp_id, src_id, .{}); |
| 3972 | const casted_ptr_id = cg.module.allocId(); | 3962 | const casted_ptr_id = cg.module.allocId(); |
| 3973 | try cg.body.emit(gpa, .OpBitcast, .{ | 3963 | try cg.body.emit(gpa, .OpBitcast, .{ |
| ... | @@ -3997,7 +3987,7 @@ fn airBitCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id { | ... | @@ -3997,7 +3987,7 @@ fn airBitCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 3997 | const result_ty = cg.typeOfIndex(inst); | 3987 | const result_ty = cg.typeOfIndex(inst); |
| 3998 | if (operand_ty.toIntern() == .bool_type) { | 3988 | if (operand_ty.toIntern() == .bool_type) { |
| 3999 | const operand = try cg.temporary(ty_op.operand); | 3989 | const operand = try cg.temporary(ty_op.operand); |
| 4000 | const result = try cg.intFromBool(operand); | 3990 | const result = try cg.intFromBool(operand, .u1); |
| 4001 | return try result.materialize(cg); | 3991 | return try result.materialize(cg); |
| 4002 | } | 3992 | } |
| 4003 | const operand_id = try cg.resolve(ty_op.operand); | 3993 | const operand_id = try cg.resolve(ty_op.operand); |
| ... | @@ -4420,7 +4410,6 @@ fn airArrayElemVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id { | ... | @@ -4420,7 +4410,6 @@ fn airArrayElemVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 4420 | // TODO: This backend probably also should use isByRef from llvm... | 4410 | // TODO: This backend probably also should use isByRef from llvm... |
| 4421 | 4411 | ||
| 4422 | const is_vector = array_ty.isVector(zcu); | 4412 | const is_vector = array_ty.isVector(zcu); |
| 4423 | |||
| 4424 | const elem_repr: Repr = if (is_vector) .direct else .indirect; | 4413 | const elem_repr: Repr = if (is_vector) .direct else .indirect; |
| 4425 | const array_ty_id = try cg.resolveType(array_ty, .direct); | 4414 | const array_ty_id = try cg.resolveType(array_ty, .direct); |
| 4426 | const elem_ty_id = try cg.resolveType(elem_ty, elem_repr); | 4415 | const elem_ty_id = try cg.resolveType(elem_ty, elem_repr); |
| ... | @@ -4588,7 +4577,8 @@ fn unionInit( | ... | @@ -4588,7 +4577,8 @@ fn unionInit( |
| 4588 | return try cg.constInt(tag_ty, tag_int); | 4577 | return try cg.constInt(tag_ty, tag_int); |
| 4589 | } | 4578 | } |
| 4590 | 4579 | ||
| 4591 | const tmp_id = try cg.alloc(ty, .{ .storage_class = .function }); | 4580 | const ty_id = try cg.resolveType(ty, .indirect); |
| 4581 | const tmp_id = try cg.alloc(ty_id, null); | ||
| 4592 | 4582 | ||
| 4593 | if (layout.tag_size != 0) { | 4583 | if (layout.tag_size != 0) { |
| 4594 | const tag_ty_id = try cg.resolveType(tag_ty, .indirect); | 4584 | const tag_ty_id = try cg.resolveType(tag_ty, .indirect); |
| ... | @@ -4709,7 +4699,8 @@ fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id { | ... | @@ -4709,7 +4699,8 @@ fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 4709 | const layout = cg.unionLayout(object_ty); | 4699 | const layout = cg.unionLayout(object_ty); |
| 4710 | assert(layout.has_payload); | 4700 | assert(layout.has_payload); |
| 4711 | 4701 | ||
| 4712 | const tmp_id = try cg.alloc(object_ty, .{ .storage_class = .function }); | 4702 | const object_ty_id = try cg.resolveType(object_ty, .indirect); |
| 4703 | const tmp_id = try cg.alloc(object_ty_id, null); | ||
| 4713 | try cg.store(object_ty, tmp_id, object_id, .{}); | 4704 | try cg.store(object_ty, tmp_id, object_id, .{}); |
| 4714 | 4705 | ||
| 4715 | const layout_payload_ty_id = try cg.resolveType(layout.payload_ty, .indirect); | 4706 | const layout_payload_ty_id = try cg.resolveType(layout.payload_ty, .indirect); |
| ... | @@ -4733,13 +4724,16 @@ fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id { | ... | @@ -4733,13 +4724,16 @@ fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 4733 | 4724 | ||
| 4734 | fn airFieldParentPtr(cg: *CodeGen, inst: Air.Inst.Index) !?Id { | 4725 | fn airFieldParentPtr(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 4735 | const zcu = cg.module.zcu; | 4726 | const zcu = cg.module.zcu; |
| 4727 | const target = zcu.getTarget(); | ||
| 4736 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 4728 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 4737 | const extra = cg.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; | 4729 | const extra = cg.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; |
| 4738 | 4730 | ||
| 4739 | const parent_ty = ty_pl.ty.toType().childType(zcu); | 4731 | const parent_ptr_ty = ty_pl.ty.toType(); |
| 4740 | const result_ty_id = try cg.resolveType(ty_pl.ty.toType(), .indirect); | 4732 | const parent_ty = parent_ptr_ty.childType(zcu); |
| 4733 | const result_ty_id = try cg.resolveType(parent_ptr_ty, .indirect); | ||
| 4741 | 4734 | ||
| 4742 | const field_ptr = try cg.resolve(extra.field_ptr); | 4735 | const field_ptr = try cg.resolve(extra.field_ptr); |
| 4736 | const field_ptr_ty = cg.typeOf(extra.field_ptr); | ||
| 4743 | const field_ptr_int = try cg.intFromPtr(field_ptr); | 4737 | const field_ptr_int = try cg.intFromPtr(field_ptr); |
| 4744 | const field_offset = parent_ty.structFieldOffset(extra.field_index, zcu); | 4738 | const field_offset = parent_ty.structFieldOffset(extra.field_index, zcu); |
| 4745 | 4739 | ||
| ... | @@ -4753,6 +4747,15 @@ fn airFieldParentPtr(cg: *CodeGen, inst: Air.Inst.Index) !?Id { | ... | @@ -4753,6 +4747,15 @@ fn airFieldParentPtr(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 4753 | break :base_ptr_int try result.materialize(cg); | 4747 | break :base_ptr_int try result.materialize(cg); |
| 4754 | }; | 4748 | }; |
| 4755 | 4749 | ||
| 4750 | if (target.os.tag != .opencl) { | ||
| 4751 | if (field_ptr_ty.ptrAddressSpace(zcu) != .physical_storage_buffer) { | ||
| 4752 | return cg.fail( | ||
| 4753 | "cannot cast integer to pointer with address space '{s}'", | ||
| 4754 | .{@tagName(field_ptr_ty.ptrAddressSpace(zcu))}, | ||
| 4755 | ); | ||
| 4756 | } | ||
| 4757 | } | ||
| 4758 | |||
| 4756 | const base_ptr = cg.module.allocId(); | 4759 | const base_ptr = cg.module.allocId(); |
| 4757 | try cg.body.emit(cg.module.gpa, .OpConvertUToPtr, .{ | 4760 | try cg.body.emit(cg.module.gpa, .OpConvertUToPtr, .{ |
| 4758 | .id_result_type = result_ty_id, | 4761 | .id_result_type = result_ty_id, |
| ... | @@ -4821,46 +4824,33 @@ fn airStructFieldPtrIndex(cg: *CodeGen, inst: Air.Inst.Index, field_index: u32) | ... | @@ -4821,46 +4824,33 @@ fn airStructFieldPtrIndex(cg: *CodeGen, inst: Air.Inst.Index, field_index: u32) |
| 4821 | return try cg.structFieldPtr(result_ptr_ty, struct_ptr_ty, struct_ptr, field_index); | 4824 | return try cg.structFieldPtr(result_ptr_ty, struct_ptr_ty, struct_ptr, field_index); |
| 4822 | } | 4825 | } |
| 4823 | 4826 | ||
| 4824 | const AllocOptions = struct { | 4827 | fn alloc(cg: *CodeGen, ty_id: Id, initializer: ?Id) !Id { |
| 4825 | initializer: ?Id = null, | 4828 | const ptr_ty_id = try cg.module.ptrType(ty_id, .function); |
| 4826 | /// The final storage class of the pointer. This may be either `.Generic` or `.Function`. | 4829 | const result_id = cg.module.allocId(); |
| 4827 | /// In either case, the local is allocated in the `.Function` storage class, and optionally | ||
| 4828 | /// cast back to `.Generic`. | ||
| 4829 | storage_class: StorageClass, | ||
| 4830 | }; | ||
| 4831 | |||
| 4832 | // Allocate a function-local variable, with possible initializer. | ||
| 4833 | // This function returns a pointer to a variable of type `ty`, | ||
| 4834 | // which is in the Generic address space. The variable is actually | ||
| 4835 | // placed in the Function address space. | ||
| 4836 | fn alloc( | ||
| 4837 | cg: *CodeGen, | ||
| 4838 | ty: Type, | ||
| 4839 | options: AllocOptions, | ||
| 4840 | ) !Id { | ||
| 4841 | const ty_id = try cg.resolveType(ty, .indirect); | ||
| 4842 | const ptr_fn_ty_id = try cg.module.ptrType(ty_id, .function); | ||
| 4843 | |||
| 4844 | // SPIR-V requires that OpVariable declarations for locals go into the first block, so we are just going to | ||
| 4845 | // directly generate them into func.prologue instead of the body. | ||
| 4846 | const var_id = cg.module.allocId(); | ||
| 4847 | try cg.prologue.emit(cg.module.gpa, .OpVariable, .{ | 4830 | try cg.prologue.emit(cg.module.gpa, .OpVariable, .{ |
| 4848 | .id_result_type = ptr_fn_ty_id, | 4831 | .id_result_type = ptr_ty_id, |
| 4849 | .id_result = var_id, | 4832 | .id_result = result_id, |
| 4850 | .storage_class = .function, | 4833 | .storage_class = .function, |
| 4851 | .initializer = options.initializer, | 4834 | .initializer = initializer, |
| 4852 | }); | 4835 | }); |
| 4853 | 4836 | return result_id; | |
| 4854 | return var_id; | ||
| 4855 | } | 4837 | } |
| 4856 | 4838 | ||
| 4857 | fn airAlloc(cg: *CodeGen, inst: Air.Inst.Index) !?Id { | 4839 | fn airAlloc(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 4858 | const zcu = cg.module.zcu; | 4840 | const zcu = cg.module.zcu; |
| 4841 | const target = zcu.getTarget(); | ||
| 4859 | const ptr_ty = cg.typeOfIndex(inst); | 4842 | const ptr_ty = cg.typeOfIndex(inst); |
| 4860 | const child_ty = ptr_ty.childType(zcu); | 4843 | const child_ty = ptr_ty.childType(zcu); |
| 4861 | return try cg.alloc(child_ty, .{ | 4844 | const child_ty_id = try cg.resolveType(child_ty, .indirect); |
| 4862 | .storage_class = cg.module.storageClass(ptr_ty.ptrAddressSpace(zcu)), | 4845 | const ptr_align = ptr_ty.ptrAlignment(zcu); |
| 4863 | }); | 4846 | const result_id = try cg.alloc(child_ty_id, null); |
| 4847 | if (ptr_align != child_ty.abiAlignment(zcu)) { | ||
| 4848 | if (target.os.tag != .opencl) return cg.fail("cannot apply alignment to variables", .{}); | ||
| 4849 | try cg.module.decorate(result_id, .{ | ||
| 4850 | .alignment = .{ .alignment = @intCast(ptr_align.toByteUnits().?) }, | ||
| 4851 | }); | ||
| 4852 | } | ||
| 4853 | return result_id; | ||
| 4864 | } | 4854 | } |
| 4865 | 4855 | ||
| 4866 | fn airArg(cg: *CodeGen) Id { | 4856 | fn airArg(cg: *CodeGen) Id { |
| ... | @@ -5087,7 +5077,8 @@ fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, body: []const Air.Inst.Index) | ... | @@ -5087,7 +5077,8 @@ fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, body: []const Air.Inst.Index) |
| 5087 | }; | 5077 | }; |
| 5088 | 5078 | ||
| 5089 | const maybe_block_result_var_id = if (have_block_result) blk: { | 5079 | const maybe_block_result_var_id = if (have_block_result) blk: { |
| 5090 | const block_result_var_id = try cg.alloc(ty, .{ .storage_class = .function }); | 5080 | const ty_id = try cg.resolveType(ty, .indirect); |
| 5081 | const block_result_var_id = try cg.alloc(ty_id, null); | ||
| 5091 | try cf.block_results.putNoClobber(gpa, inst, block_result_var_id); | 5082 | try cf.block_results.putNoClobber(gpa, inst, block_result_var_id); |
| 5092 | break :blk block_result_var_id; | 5083 | break :blk block_result_var_id; |
| 5093 | } else null; | 5084 | } else null; |
src/codegen/spirv/Module.zig+6-5| ... | @@ -676,8 +676,9 @@ pub fn structType( | ... | @@ -676,8 +676,9 @@ pub fn structType( |
| 676 | ip_index: InternPool.Index, | 676 | ip_index: InternPool.Index, |
| 677 | ) !Id { | 677 | ) !Id { |
| 678 | const target = module.zcu.getTarget(); | 678 | const target = module.zcu.getTarget(); |
| 679 | const actual_ip_index = if (module.zcu.comp.config.root_strip) .none else ip_index; | ||
| 679 | 680 | ||
| 680 | if (module.cache.struct_types.get(.{ .fields = types, .ip_index = ip_index })) |id| return id; | 681 | if (module.cache.struct_types.get(.{ .fields = types, .ip_index = actual_ip_index })) |id| return id; |
| 681 | const result_id = module.allocId(); | 682 | const result_id = module.allocId(); |
| 682 | const types_dup = try module.arena.dupe(Id, types); | 683 | const types_dup = try module.arena.dupe(Id, types); |
| 683 | try module.sections.globals.emit(module.gpa, .OpTypeStruct, .{ | 684 | try module.sections.globals.emit(module.gpa, .OpTypeStruct, .{ |
| ... | @@ -710,10 +711,7 @@ pub fn structType( | ... | @@ -710,10 +711,7 @@ pub fn structType( |
| 710 | 711 | ||
| 711 | try module.cache.struct_types.put( | 712 | try module.cache.struct_types.put( |
| 712 | module.gpa, | 713 | module.gpa, |
| 713 | .{ | 714 | .{ .fields = types_dup, .ip_index = actual_ip_index }, |
| 714 | .fields = types_dup, | ||
| 715 | .ip_index = if (module.zcu.comp.config.root_strip) .none else ip_index, | ||
| 716 | }, | ||
| 717 | result_id, | 715 | result_id, |
| 718 | ); | 716 | ); |
| 719 | return result_id; | 717 | return result_id; |
| ... | @@ -874,6 +872,7 @@ pub fn declareEntryPoint( | ... | @@ -874,6 +872,7 @@ pub fn declareEntryPoint( |
| 874 | } | 872 | } |
| 875 | 873 | ||
| 876 | pub fn debugName(module: *Module, target: Id, name: []const u8) !void { | 874 | pub fn debugName(module: *Module, target: Id, name: []const u8) !void { |
| 875 | if (module.zcu.comp.config.root_strip) return; | ||
| 877 | try module.sections.debug_names.emit(module.gpa, .OpName, .{ | 876 | try module.sections.debug_names.emit(module.gpa, .OpName, .{ |
| 878 | .target = target, | 877 | .target = target, |
| 879 | .name = name, | 878 | .name = name, |
| ... | @@ -881,12 +880,14 @@ pub fn debugName(module: *Module, target: Id, name: []const u8) !void { | ... | @@ -881,12 +880,14 @@ pub fn debugName(module: *Module, target: Id, name: []const u8) !void { |
| 881 | } | 880 | } |
| 882 | 881 | ||
| 883 | pub fn debugNameFmt(module: *Module, target: Id, comptime fmt: []const u8, args: anytype) !void { | 882 | pub fn debugNameFmt(module: *Module, target: Id, comptime fmt: []const u8, args: anytype) !void { |
| 883 | if (module.zcu.comp.config.root_strip) return; | ||
| 884 | const name = try std.fmt.allocPrint(module.gpa, fmt, args); | 884 | const name = try std.fmt.allocPrint(module.gpa, fmt, args); |
| 885 | defer module.gpa.free(name); | 885 | defer module.gpa.free(name); |
| 886 | try module.debugName(target, name); | 886 | try module.debugName(target, name); |
| 887 | } | 887 | } |
| 888 | 888 | ||
| 889 | pub fn memberDebugName(module: *Module, target: Id, member: u32, name: []const u8) !void { | 889 | pub fn memberDebugName(module: *Module, target: Id, member: u32, name: []const u8) !void { |
| 890 | if (module.zcu.comp.config.root_strip) return; | ||
| 890 | try module.sections.debug_names.emit(module.gpa, .OpMemberName, .{ | 891 | try module.sections.debug_names.emit(module.gpa, .OpMemberName, .{ |
| 891 | .type = target, | 892 | .type = target, |
| 892 | .member = member, | 893 | .member = member, |
src/link/SpirV.zig+4-11| ... | @@ -10,10 +10,11 @@ const Compilation = @import("../Compilation.zig"); | ... | @@ -10,10 +10,11 @@ const Compilation = @import("../Compilation.zig"); |
| 10 | const link = @import("../link.zig"); | 10 | const link = @import("../link.zig"); |
| 11 | const Air = @import("../Air.zig"); | 11 | const Air = @import("../Air.zig"); |
| 12 | const Type = @import("../Type.zig"); | 12 | const Type = @import("../Type.zig"); |
| 13 | const BinaryModule = @import("SpirV/BinaryModule.zig"); | ||
| 14 | const CodeGen = @import("../codegen/spirv/CodeGen.zig"); | 13 | const CodeGen = @import("../codegen/spirv/CodeGen.zig"); |
| 15 | const Module = @import("../codegen/spirv/Module.zig"); | 14 | const Module = @import("../codegen/spirv/Module.zig"); |
| 16 | const trace = @import("../tracy.zig").trace; | 15 | const trace = @import("../tracy.zig").trace; |
| 16 | const BinaryModule = @import("SpirV/BinaryModule.zig"); | ||
| 17 | const lower_invocation_globals = @import("SpirV/lower_invocation_globals.zig"); | ||
| 17 | 18 | ||
| 18 | const spec = @import("../codegen/spirv/spec.zig"); | 19 | const spec = @import("../codegen/spirv/spec.zig"); |
| 19 | const Id = spec.Id; | 20 | const Id = spec.Id; |
| ... | @@ -279,7 +280,7 @@ pub fn flush( | ... | @@ -279,7 +280,7 @@ pub fn flush( |
| 279 | const module = try linker.module.finalize(arena); | 280 | const module = try linker.module.finalize(arena); |
| 280 | errdefer arena.free(module); | 281 | errdefer arena.free(module); |
| 281 | 282 | ||
| 282 | const linked_module = linker.linkModule(arena, module, sub_prog_node) catch |err| switch (err) { | 283 | const linked_module = linkModule(arena, module, sub_prog_node) catch |err| switch (err) { |
| 283 | error.OutOfMemory => return error.OutOfMemory, | 284 | error.OutOfMemory => return error.OutOfMemory, |
| 284 | else => |other| return diags.fail("error while linking: {s}", .{@errorName(other)}), | 285 | else => |other| return diags.fail("error while linking: {s}", .{@errorName(other)}), |
| 285 | }; | 286 | }; |
| ... | @@ -288,18 +289,10 @@ pub fn flush( | ... | @@ -288,18 +289,10 @@ pub fn flush( |
| 288 | return diags.fail("failed to write: {s}", .{@errorName(err)}); | 289 | return diags.fail("failed to write: {s}", .{@errorName(err)}); |
| 289 | } | 290 | } |
| 290 | 291 | ||
| 291 | fn linkModule(linker: *Linker, arena: Allocator, module: []Word, progress: std.Progress.Node) ![]Word { | 292 | fn linkModule(arena: Allocator, module: []Word, progress: std.Progress.Node) ![]Word { |
| 292 | _ = linker; | ||
| 293 | |||
| 294 | const lower_invocation_globals = @import("SpirV/lower_invocation_globals.zig"); | ||
| 295 | const prune_unused = @import("SpirV/prune_unused.zig"); | ||
| 296 | |||
| 297 | var parser = try BinaryModule.Parser.init(arena); | 293 | var parser = try BinaryModule.Parser.init(arena); |
| 298 | defer parser.deinit(); | 294 | defer parser.deinit(); |
| 299 | var binary = try parser.parse(module); | 295 | var binary = try parser.parse(module); |
| 300 | |||
| 301 | try lower_invocation_globals.run(&parser, &binary, progress); | 296 | try lower_invocation_globals.run(&parser, &binary, progress); |
| 302 | try prune_unused.run(&parser, &binary, progress); | ||
| 303 | |||
| 304 | return binary.finalize(arena); | 297 | return binary.finalize(arena); |
| 305 | } | 298 | } |
src/link/SpirV/lower_invocation_globals.zig+10-1| ... | @@ -382,6 +382,15 @@ const ModuleBuilder = struct { | ... | @@ -382,6 +382,15 @@ const ModuleBuilder = struct { |
| 382 | var it = binary.iterateInstructions(); | 382 | var it = binary.iterateInstructions(); |
| 383 | while (it.next()) |inst| { | 383 | while (it.next()) |inst| { |
| 384 | switch (inst.opcode) { | 384 | switch (inst.opcode) { |
| 385 | .OpName => { | ||
| 386 | const id: ResultId = @enumFromInt(inst.operands[0]); | ||
| 387 | if (info.invocation_globals.contains(id)) continue; | ||
| 388 | }, | ||
| 389 | .OpExtInstImport => { | ||
| 390 | const set_id: ResultId = @enumFromInt(inst.operands[0]); | ||
| 391 | const set = binary.ext_inst_map.get(set_id).?; | ||
| 392 | if (set == .zig) continue; | ||
| 393 | }, | ||
| 385 | .OpExtInst => { | 394 | .OpExtInst => { |
| 386 | const set_id: ResultId = @enumFromInt(inst.operands[2]); | 395 | const set_id: ResultId = @enumFromInt(inst.operands[2]); |
| 387 | const set_inst = inst.operands[3]; | 396 | const set_inst = inst.operands[3]; |
| ... | @@ -482,7 +491,7 @@ const ModuleBuilder = struct { | ... | @@ -482,7 +491,7 @@ const ModuleBuilder = struct { |
| 482 | return entry.value_ptr.*; | 491 | return entry.value_ptr.*; |
| 483 | } | 492 | } |
| 484 | 493 | ||
| 485 | /// Rewrite the modules' functions and emit them with the new parameter types. | 494 | /// Rewrite the modules functions and emit them with the new parameter types. |
| 486 | fn rewriteFunctions( | 495 | fn rewriteFunctions( |
| 487 | self: *ModuleBuilder, | 496 | self: *ModuleBuilder, |
| 488 | parser: *BinaryModule.Parser, | 497 | parser: *BinaryModule.Parser, |
src/link/SpirV/prune_unused.zig deleted-362| ... | @@ -1,362 +0,0 @@ | ||
| 1 | //! This pass is used to simple pruning of unused things: | ||
| 2 | //! - Instructions at global scope | ||
| 3 | //! - Functions | ||
| 4 | //! Debug info and nonsemantic instructions are not handled; | ||
| 5 | //! this pass is mainly intended for cleaning up left over | ||
| 6 | //! stuff from codegen and other passes that is generated | ||
| 7 | //! but not actually used. | ||
| 8 | |||
| 9 | const std = @import("std"); | ||
| 10 | const Allocator = std.mem.Allocator; | ||
| 11 | const assert = std.debug.assert; | ||
| 12 | const log = std.log.scoped(.spirv_link); | ||
| 13 | |||
| 14 | const BinaryModule = @import("BinaryModule.zig"); | ||
| 15 | const Section = @import("../../codegen/spirv/Section.zig"); | ||
| 16 | const spec = @import("../../codegen/spirv/spec.zig"); | ||
| 17 | const Opcode = spec.Opcode; | ||
| 18 | const ResultId = spec.Id; | ||
| 19 | const Word = spec.Word; | ||
| 20 | |||
| 21 | /// Return whether a particular opcode's instruction can be pruned. | ||
| 22 | /// These are idempotent instructions at globals scope and instructions | ||
| 23 | /// within functions that do not have any side effects. | ||
| 24 | /// The opcodes that return true here do not necessarily need to | ||
| 25 | /// have an .Id. If they don't, then they are regarded | ||
| 26 | /// as 'decoration'-style instructions that don't keep their | ||
| 27 | /// operands alive, but will be emitted if they are. | ||
| 28 | fn canPrune(op: Opcode) bool { | ||
| 29 | // This list should be as worked out as possible, but just | ||
| 30 | // getting common instructions is a good effort/effect ratio. | ||
| 31 | // When adding items to this list, also check whether the | ||
| 32 | // instruction requires any special control flow rules (like | ||
| 33 | // with labels and control flow and stuff) and whether the | ||
| 34 | // instruction has any non-trivial side effects (like OpLoad | ||
| 35 | // with the Volatile memory semantics). | ||
| 36 | return switch (op.class()) { | ||
| 37 | .type_declaration, | ||
| 38 | .conversion, | ||
| 39 | .arithmetic, | ||
| 40 | .relational_and_logical, | ||
| 41 | .bit, | ||
| 42 | .annotation, | ||
| 43 | => true, | ||
| 44 | else => switch (op) { | ||
| 45 | .OpFunction, | ||
| 46 | .OpUndef, | ||
| 47 | .OpString, | ||
| 48 | .OpName, | ||
| 49 | .OpMemberName, | ||
| 50 | // Prune OpConstant* instructions but | ||
| 51 | // retain OpSpecConstant declaration instructions | ||
| 52 | .OpConstantTrue, | ||
| 53 | .OpConstantFalse, | ||
| 54 | .OpConstant, | ||
| 55 | .OpConstantComposite, | ||
| 56 | .OpConstantSampler, | ||
| 57 | .OpConstantNull, | ||
| 58 | .OpSpecConstantOp, | ||
| 59 | // Prune ext inst import instructions, but not | ||
| 60 | // ext inst instructions themselves, because | ||
| 61 | // we don't know if they might have side effects. | ||
| 62 | .OpExtInstImport, | ||
| 63 | => true, | ||
| 64 | else => false, | ||
| 65 | }, | ||
| 66 | }; | ||
| 67 | } | ||
| 68 | |||
| 69 | const ModuleInfo = struct { | ||
| 70 | const Fn = struct { | ||
| 71 | /// The index of the first callee in `callee_store`. | ||
| 72 | first_callee: usize, | ||
| 73 | }; | ||
| 74 | |||
| 75 | /// Maps function result-id -> Fn information structure. | ||
| 76 | functions: std.AutoArrayHashMapUnmanaged(ResultId, Fn), | ||
| 77 | /// For each function, a list of function result-ids that it calls. | ||
| 78 | callee_store: []const ResultId, | ||
| 79 | /// For each instruction, the offset at which it appears in the source module. | ||
| 80 | result_id_to_code_offset: std.AutoArrayHashMapUnmanaged(ResultId, usize), | ||
| 81 | |||
| 82 | /// Fetch the list of callees per function. Guaranteed to contain only unique IDs. | ||
| 83 | fn callees(self: ModuleInfo, fn_id: ResultId) []const ResultId { | ||
| 84 | const fn_index = self.functions.getIndex(fn_id).?; | ||
| 85 | const values = self.functions.values(); | ||
| 86 | const first_callee = values[fn_index].first_callee; | ||
| 87 | if (fn_index == values.len - 1) { | ||
| 88 | return self.callee_store[first_callee..]; | ||
| 89 | } else { | ||
| 90 | const next_first_callee = values[fn_index + 1].first_callee; | ||
| 91 | return self.callee_store[first_callee..next_first_callee]; | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | /// Extract the information required to run this pass from the binary. | ||
| 96 | // TODO: Should the contents of this function be merged with that of lower_invocation_globals.zig? | ||
| 97 | // Many of the contents are the same... | ||
| 98 | fn parse( | ||
| 99 | arena: Allocator, | ||
| 100 | parser: *BinaryModule.Parser, | ||
| 101 | binary: BinaryModule, | ||
| 102 | ) !ModuleInfo { | ||
| 103 | var functions = std.AutoArrayHashMap(ResultId, Fn).init(arena); | ||
| 104 | var calls = std.AutoArrayHashMap(ResultId, void).init(arena); | ||
| 105 | var callee_store = std.ArrayList(ResultId).init(arena); | ||
| 106 | var result_id_to_code_offset = std.AutoArrayHashMap(ResultId, usize).init(arena); | ||
| 107 | var maybe_current_function: ?ResultId = null; | ||
| 108 | var it = binary.iterateInstructions(); | ||
| 109 | while (it.next()) |inst| { | ||
| 110 | const inst_spec = parser.getInstSpec(inst.opcode).?; | ||
| 111 | |||
| 112 | // Result-id can only be the first or second operand | ||
| 113 | const maybe_result_id: ?ResultId = for (0..2) |i| { | ||
| 114 | if (inst_spec.operands.len > i and inst_spec.operands[i].kind == .id_result) { | ||
| 115 | break @enumFromInt(inst.operands[i]); | ||
| 116 | } | ||
| 117 | } else null; | ||
| 118 | |||
| 119 | // Only add result-ids of functions and anything outside a function. | ||
| 120 | // Result-ids declared inside functions cannot be reached outside anyway, | ||
| 121 | // and we don't care about the internals of functions anyway. | ||
| 122 | // Note that in the case of OpFunction, `maybe_current_function` is | ||
| 123 | // also `null`, because it is set below. | ||
| 124 | if (maybe_result_id) |result_id| { | ||
| 125 | try result_id_to_code_offset.put(result_id, inst.offset); | ||
| 126 | } | ||
| 127 | |||
| 128 | switch (inst.opcode) { | ||
| 129 | .OpFunction => { | ||
| 130 | if (maybe_current_function) |current_function| { | ||
| 131 | log.err("OpFunction {f} does not have an OpFunctionEnd", .{current_function}); | ||
| 132 | return error.InvalidPhysicalFormat; | ||
| 133 | } | ||
| 134 | |||
| 135 | maybe_current_function = @enumFromInt(inst.operands[1]); | ||
| 136 | }, | ||
| 137 | .OpFunctionCall => { | ||
| 138 | const callee: ResultId = @enumFromInt(inst.operands[2]); | ||
| 139 | try calls.put(callee, {}); | ||
| 140 | }, | ||
| 141 | .OpFunctionEnd => { | ||
| 142 | const current_function = maybe_current_function orelse { | ||
| 143 | log.err("encountered OpFunctionEnd without corresponding OpFunction", .{}); | ||
| 144 | return error.InvalidPhysicalFormat; | ||
| 145 | }; | ||
| 146 | const entry = try functions.getOrPut(current_function); | ||
| 147 | if (entry.found_existing) { | ||
| 148 | log.err("Function {f} has duplicate definition", .{current_function}); | ||
| 149 | return error.DuplicateId; | ||
| 150 | } | ||
| 151 | |||
| 152 | const first_callee = callee_store.items.len; | ||
| 153 | try callee_store.appendSlice(calls.keys()); | ||
| 154 | |||
| 155 | entry.value_ptr.* = .{ | ||
| 156 | .first_callee = first_callee, | ||
| 157 | }; | ||
| 158 | maybe_current_function = null; | ||
| 159 | calls.clearRetainingCapacity(); | ||
| 160 | }, | ||
| 161 | else => {}, | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | if (maybe_current_function) |current_function| { | ||
| 166 | log.err("OpFunction {f} does not have an OpFunctionEnd", .{current_function}); | ||
| 167 | return error.InvalidPhysicalFormat; | ||
| 168 | } | ||
| 169 | |||
| 170 | return .{ | ||
| 171 | .functions = functions.unmanaged, | ||
| 172 | .callee_store = callee_store.items, | ||
| 173 | .result_id_to_code_offset = result_id_to_code_offset.unmanaged, | ||
| 174 | }; | ||
| 175 | } | ||
| 176 | }; | ||
| 177 | |||
| 178 | const AliveMarker = struct { | ||
| 179 | parser: *BinaryModule.Parser, | ||
| 180 | binary: BinaryModule, | ||
| 181 | info: ModuleInfo, | ||
| 182 | result_id_offsets: std.ArrayList(u16), | ||
| 183 | alive: std.DynamicBitSetUnmanaged, | ||
| 184 | |||
| 185 | fn markAlive(self: *AliveMarker, result_id: ResultId) BinaryModule.ParseError!void { | ||
| 186 | const index = self.info.result_id_to_code_offset.getIndex(result_id) orelse { | ||
| 187 | log.err("undefined result-id {f}", .{result_id}); | ||
| 188 | return error.InvalidId; | ||
| 189 | }; | ||
| 190 | |||
| 191 | if (self.alive.isSet(index)) { | ||
| 192 | return; | ||
| 193 | } | ||
| 194 | self.alive.set(index); | ||
| 195 | |||
| 196 | const offset = self.info.result_id_to_code_offset.values()[index]; | ||
| 197 | const inst = self.binary.instructionAt(offset); | ||
| 198 | |||
| 199 | if (inst.opcode == .OpFunction) { | ||
| 200 | try self.markFunctionAlive(inst); | ||
| 201 | } else { | ||
| 202 | try self.markInstructionAlive(inst); | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | fn markFunctionAlive( | ||
| 207 | self: *AliveMarker, | ||
| 208 | func_inst: BinaryModule.Instruction, | ||
| 209 | ) !void { | ||
| 210 | // Go through the instruction and mark the | ||
| 211 | // operands of each instruction alive. | ||
| 212 | var it = self.binary.iterateInstructionsFrom(func_inst.offset); | ||
| 213 | try self.markInstructionAlive(it.next().?); | ||
| 214 | while (it.next()) |inst| { | ||
| 215 | if (inst.opcode == .OpFunctionEnd) { | ||
| 216 | break; | ||
| 217 | } | ||
| 218 | |||
| 219 | if (!canPrune(inst.opcode)) { | ||
| 220 | try self.markInstructionAlive(inst); | ||
| 221 | } | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | fn markInstructionAlive( | ||
| 226 | self: *AliveMarker, | ||
| 227 | inst: BinaryModule.Instruction, | ||
| 228 | ) !void { | ||
| 229 | const start_offset = self.result_id_offsets.items.len; | ||
| 230 | try self.parser.parseInstructionResultIds(self.binary, inst, &self.result_id_offsets); | ||
| 231 | const end_offset = self.result_id_offsets.items.len; | ||
| 232 | |||
| 233 | // Recursive calls to markInstructionAlive() might change the pointer in self.result_id_offsets, | ||
| 234 | // so we need to iterate it manually. | ||
| 235 | var i = start_offset; | ||
| 236 | while (i < end_offset) : (i += 1) { | ||
| 237 | const offset = self.result_id_offsets.items[i]; | ||
| 238 | try self.markAlive(@enumFromInt(inst.operands[offset])); | ||
| 239 | } | ||
| 240 | } | ||
| 241 | }; | ||
| 242 | |||
| 243 | fn removeIdsFromMap(a: Allocator, map: anytype, info: ModuleInfo, alive_marker: AliveMarker) !void { | ||
| 244 | var to_remove = std.ArrayList(ResultId).init(a); | ||
| 245 | var it = map.iterator(); | ||
| 246 | while (it.next()) |entry| { | ||
| 247 | const id = entry.key_ptr.*; | ||
| 248 | const index = info.result_id_to_code_offset.getIndex(id).?; | ||
| 249 | if (!alive_marker.alive.isSet(index)) { | ||
| 250 | try to_remove.append(id); | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | for (to_remove.items) |id| { | ||
| 255 | assert(map.remove(id)); | ||
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule, progress: std.Progress.Node) !void { | ||
| 260 | const sub_node = progress.start("Prune unused IDs", 0); | ||
| 261 | defer sub_node.end(); | ||
| 262 | |||
| 263 | var arena = std.heap.ArenaAllocator.init(parser.a); | ||
| 264 | defer arena.deinit(); | ||
| 265 | const a = arena.allocator(); | ||
| 266 | |||
| 267 | const info = try ModuleInfo.parse(a, parser, binary.*); | ||
| 268 | |||
| 269 | var alive_marker = AliveMarker{ | ||
| 270 | .parser = parser, | ||
| 271 | .binary = binary.*, | ||
| 272 | .info = info, | ||
| 273 | .result_id_offsets = std.ArrayList(u16).init(a), | ||
| 274 | .alive = try std.DynamicBitSetUnmanaged.initEmpty(a, info.result_id_to_code_offset.count()), | ||
| 275 | }; | ||
| 276 | |||
| 277 | // Mark initial stuff as alive | ||
| 278 | { | ||
| 279 | var it = binary.iterateInstructions(); | ||
| 280 | while (it.next()) |inst| { | ||
| 281 | if (inst.opcode == .OpFunction) { | ||
| 282 | // No need to process further. | ||
| 283 | break; | ||
| 284 | } else if (!canPrune(inst.opcode)) { | ||
| 285 | try alive_marker.markInstructionAlive(inst); | ||
| 286 | } | ||
| 287 | } | ||
| 288 | } | ||
| 289 | |||
| 290 | var section = Section{}; | ||
| 291 | |||
| 292 | sub_node.setEstimatedTotalItems(binary.instructions.len); | ||
| 293 | |||
| 294 | var new_functions_section: ?usize = null; | ||
| 295 | var it = binary.iterateInstructions(); | ||
| 296 | skip: while (it.next()) |inst| { | ||
| 297 | defer sub_node.setCompletedItems(inst.offset); | ||
| 298 | |||
| 299 | const inst_spec = parser.getInstSpec(inst.opcode).?; | ||
| 300 | |||
| 301 | reemit: { | ||
| 302 | if (!canPrune(inst.opcode)) { | ||
| 303 | break :reemit; | ||
| 304 | } | ||
| 305 | |||
| 306 | // Result-id can only be the first or second operand | ||
| 307 | const result_id: ResultId = for (0..2) |i| { | ||
| 308 | if (inst_spec.operands.len > i and inst_spec.operands[i].kind == .id_result) { | ||
| 309 | break @enumFromInt(inst.operands[i]); | ||
| 310 | } | ||
| 311 | } else { | ||
| 312 | // Instruction can be pruned but doesn't have a result id. | ||
| 313 | // Check all operands to see if they are alive, and emit it only if so. | ||
| 314 | alive_marker.result_id_offsets.items.len = 0; | ||
| 315 | try parser.parseInstructionResultIds(binary.*, inst, &alive_marker.result_id_offsets); | ||
| 316 | for (alive_marker.result_id_offsets.items) |offset| { | ||
| 317 | const id: ResultId = @enumFromInt(inst.operands[offset]); | ||
| 318 | const index = info.result_id_to_code_offset.getIndex(id).?; | ||
| 319 | |||
| 320 | if (!alive_marker.alive.isSet(index)) { | ||
| 321 | continue :skip; | ||
| 322 | } | ||
| 323 | } | ||
| 324 | |||
| 325 | break :reemit; | ||
| 326 | }; | ||
| 327 | |||
| 328 | const index = info.result_id_to_code_offset.getIndex(result_id).?; | ||
| 329 | if (alive_marker.alive.isSet(index)) { | ||
| 330 | break :reemit; | ||
| 331 | } | ||
| 332 | |||
| 333 | if (inst.opcode != .OpFunction) { | ||
| 334 | // Instruction can be pruned and its not alive, so skip it. | ||
| 335 | continue :skip; | ||
| 336 | } | ||
| 337 | |||
| 338 | // We're at the start of a function that can be pruned, so skip everything until | ||
| 339 | // we encounter an OpFunctionEnd. | ||
| 340 | while (it.next()) |body_inst| { | ||
| 341 | if (body_inst.opcode == .OpFunctionEnd) | ||
| 342 | break; | ||
| 343 | } | ||
| 344 | |||
| 345 | continue :skip; | ||
| 346 | } | ||
| 347 | |||
| 348 | if (inst.opcode == .OpFunction and new_functions_section == null) { | ||
| 349 | new_functions_section = section.instructions.items.len; | ||
| 350 | } | ||
| 351 | |||
| 352 | try section.emitRawInstruction(a, inst.opcode, inst.operands); | ||
| 353 | } | ||
| 354 | |||
| 355 | // This pass might have pruned ext inst imports or arith types, update | ||
| 356 | // those maps to main consistency. | ||
| 357 | try removeIdsFromMap(a, &binary.ext_inst_map, info, alive_marker); | ||
| 358 | try removeIdsFromMap(a, &binary.arith_type_width, info, alive_marker); | ||
| 359 | |||
| 360 | binary.instructions = try parser.a.dupe(Word, section.toWords()); | ||
| 361 | binary.sections.functions = new_functions_section orelse binary.instructions.len; | ||
| 362 | } | ||
test/behavior/align.zig+2| ... | @@ -561,6 +561,8 @@ test "function pointer @intFromPtr/@ptrFromInt roundtrip" { | ... | @@ -561,6 +561,8 @@ test "function pointer @intFromPtr/@ptrFromInt roundtrip" { |
| 561 | } | 561 | } |
| 562 | 562 | ||
| 563 | test "function pointer align mask" { | 563 | test "function pointer align mask" { |
| 564 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 565 | |||
| 564 | const int = if (builtin.cpu.arch.isArm() or builtin.cpu.arch.isMIPS()) 0x20202021 else 0x20202020; | 566 | const int = if (builtin.cpu.arch.isArm() or builtin.cpu.arch.isMIPS()) 0x20202021 else 0x20202020; |
| 565 | const unaligned: *const fn () callconv(.c) void = @ptrFromInt(int); | 567 | const unaligned: *const fn () callconv(.c) void = @ptrFromInt(int); |
| 566 | const aligned: *align(16) const fn () callconv(.c) void = @alignCast(unaligned); | 568 | const aligned: *align(16) const fn () callconv(.c) void = @alignCast(unaligned); |
test/behavior/array.zig+2| ... | @@ -1088,6 +1088,8 @@ test "pass pointer to empty array initializer to anytype parameter" { | ... | @@ -1088,6 +1088,8 @@ test "pass pointer to empty array initializer to anytype parameter" { |
| 1088 | } | 1088 | } |
| 1089 | 1089 | ||
| 1090 | test "initialize pointer to anyopaque with reference to empty array initializer" { | 1090 | test "initialize pointer to anyopaque with reference to empty array initializer" { |
| 1091 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 1092 | |||
| 1091 | const ptr: *const anyopaque = &.{}; | 1093 | const ptr: *const anyopaque = &.{}; |
| 1092 | // The above acts like an untyped initializer, since the `.{}` has no result type. | 1094 | // The above acts like an untyped initializer, since the `.{}` has no result type. |
| 1093 | // So, `ptr` points in memory to an empty tuple (`@TypeOf(.{})`). | 1095 | // So, `ptr` points in memory to an empty tuple (`@TypeOf(.{})`). |
test/behavior/cast.zig+10| ... | @@ -9,6 +9,8 @@ const maxInt = std.math.maxInt; | ... | @@ -9,6 +9,8 @@ const maxInt = std.math.maxInt; |
| 9 | const native_endian = builtin.target.cpu.arch.endian(); | 9 | const native_endian = builtin.target.cpu.arch.endian(); |
| 10 | 10 | ||
| 11 | test "int to ptr cast" { | 11 | test "int to ptr cast" { |
| 12 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 13 | |||
| 12 | const x = @as(usize, 13); | 14 | const x = @as(usize, 13); |
| 13 | const y = @as(*u8, @ptrFromInt(x)); | 15 | const y = @as(*u8, @ptrFromInt(x)); |
| 14 | const z = @intFromPtr(y); | 16 | const z = @intFromPtr(y); |
| ... | @@ -16,6 +18,8 @@ test "int to ptr cast" { | ... | @@ -16,6 +18,8 @@ test "int to ptr cast" { |
| 16 | } | 18 | } |
| 17 | 19 | ||
| 18 | test "integer literal to pointer cast" { | 20 | test "integer literal to pointer cast" { |
| 21 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 22 | |||
| 19 | const vga_mem = @as(*u16, @ptrFromInt(0xB8000)); | 23 | const vga_mem = @as(*u16, @ptrFromInt(0xB8000)); |
| 20 | try expect(@intFromPtr(vga_mem) == 0xB8000); | 24 | try expect(@intFromPtr(vga_mem) == 0xB8000); |
| 21 | } | 25 | } |
| ... | @@ -269,6 +273,8 @@ test "implicit cast from *[N]T to [*c]T" { | ... | @@ -269,6 +273,8 @@ test "implicit cast from *[N]T to [*c]T" { |
| 269 | } | 273 | } |
| 270 | 274 | ||
| 271 | test "*usize to *void" { | 275 | test "*usize to *void" { |
| 276 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 277 | |||
| 272 | var i = @as(usize, 0); | 278 | var i = @as(usize, 0); |
| 273 | const v: *void = @ptrCast(&i); | 279 | const v: *void = @ptrCast(&i); |
| 274 | v.* = {}; | 280 | v.* = {}; |
| ... | @@ -1481,6 +1487,8 @@ test "coerce between pointers of compatible differently-named floats" { | ... | @@ -1481,6 +1487,8 @@ test "coerce between pointers of compatible differently-named floats" { |
| 1481 | } | 1487 | } |
| 1482 | 1488 | ||
| 1483 | test "peer type resolution of const and non-const pointer to array" { | 1489 | test "peer type resolution of const and non-const pointer to array" { |
| 1490 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 1491 | |||
| 1484 | const a = @as(*[1024]u8, @ptrFromInt(42)); | 1492 | const a = @as(*[1024]u8, @ptrFromInt(42)); |
| 1485 | const b = @as(*const [1024]u8, @ptrFromInt(42)); | 1493 | const b = @as(*const [1024]u8, @ptrFromInt(42)); |
| 1486 | try std.testing.expect(@TypeOf(a, b) == *const [1024]u8); | 1494 | try std.testing.expect(@TypeOf(a, b) == *const [1024]u8); |
| ... | @@ -1543,6 +1551,8 @@ test "optional pointer coerced to optional allowzero pointer" { | ... | @@ -1543,6 +1551,8 @@ test "optional pointer coerced to optional allowzero pointer" { |
| 1543 | } | 1551 | } |
| 1544 | 1552 | ||
| 1545 | test "optional slice coerced to allowzero many pointer" { | 1553 | test "optional slice coerced to allowzero many pointer" { |
| 1554 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 1555 | |||
| 1546 | const a: ?[]const u32 = null; | 1556 | const a: ?[]const u32 = null; |
| 1547 | const b: [*]allowzero const u8 = @ptrCast(a); | 1557 | const b: [*]allowzero const u8 = @ptrCast(a); |
| 1548 | const c = @intFromPtr(b); | 1558 | const c = @intFromPtr(b); |
test/behavior/comptime_memory.zig+6| ... | @@ -406,6 +406,8 @@ test "mutate entire slice at comptime" { | ... | @@ -406,6 +406,8 @@ test "mutate entire slice at comptime" { |
| 406 | } | 406 | } |
| 407 | 407 | ||
| 408 | test "dereference undefined pointer to zero-bit type" { | 408 | test "dereference undefined pointer to zero-bit type" { |
| 409 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 410 | |||
| 409 | const p0: *void = undefined; | 411 | const p0: *void = undefined; |
| 410 | try testing.expectEqual({}, p0.*); | 412 | try testing.expectEqual({}, p0.*); |
| 411 | 413 | ||
| ... | @@ -421,6 +423,8 @@ test "type pun extern struct" { | ... | @@ -421,6 +423,8 @@ test "type pun extern struct" { |
| 421 | } | 423 | } |
| 422 | 424 | ||
| 423 | test "type pun @ptrFromInt" { | 425 | test "type pun @ptrFromInt" { |
| 426 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 427 | |||
| 424 | const p: *u8 = @ptrFromInt(42); | 428 | const p: *u8 = @ptrFromInt(42); |
| 425 | // note that expectEqual hides the bug | 429 | // note that expectEqual hides the bug |
| 426 | try testing.expect(@as(*const [*]u8, @ptrCast(&p)).* == @as([*]u8, @ptrFromInt(42))); | 430 | try testing.expect(@as(*const [*]u8, @ptrCast(&p)).* == @as([*]u8, @ptrFromInt(42))); |
| ... | @@ -511,6 +515,8 @@ fn fieldPtrTest() u32 { | ... | @@ -511,6 +515,8 @@ fn fieldPtrTest() u32 { |
| 511 | return a.value; | 515 | return a.value; |
| 512 | } | 516 | } |
| 513 | test "pointer in aggregate field can mutate comptime state" { | 517 | test "pointer in aggregate field can mutate comptime state" { |
| 518 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 519 | |||
| 514 | try comptime std.testing.expect(fieldPtrTest() == 2); | 520 | try comptime std.testing.expect(fieldPtrTest() == 2); |
| 515 | } | 521 | } |
| 516 | 522 |
test/behavior/generics.zig+1| ... | @@ -169,6 +169,7 @@ test "generic fn keeps non-generic parameter types" { | ... | @@ -169,6 +169,7 @@ test "generic fn keeps non-generic parameter types" { |
| 169 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 169 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 170 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 170 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 171 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | 171 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 172 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 172 | 173 | ||
| 173 | const A = 128; | 174 | const A = 128; |
| 174 | 175 |
test/behavior/pointers.zig+8| ... | @@ -267,6 +267,8 @@ test "implicit cast error unions with non-optional to optional pointer" { | ... | @@ -267,6 +267,8 @@ test "implicit cast error unions with non-optional to optional pointer" { |
| 267 | } | 267 | } |
| 268 | 268 | ||
| 269 | test "compare equality of optional and non-optional pointer" { | 269 | test "compare equality of optional and non-optional pointer" { |
| 270 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 271 | |||
| 270 | const a = @as(*const usize, @ptrFromInt(0x12345678)); | 272 | const a = @as(*const usize, @ptrFromInt(0x12345678)); |
| 271 | const b = @as(?*usize, @ptrFromInt(0x12345678)); | 273 | const b = @as(?*usize, @ptrFromInt(0x12345678)); |
| 272 | try expect(a == b); | 274 | try expect(a == b); |
| ... | @@ -453,6 +455,8 @@ test "pointer sentinel with +inf" { | ... | @@ -453,6 +455,8 @@ test "pointer sentinel with +inf" { |
| 453 | } | 455 | } |
| 454 | 456 | ||
| 455 | test "pointer to array at fixed address" { | 457 | test "pointer to array at fixed address" { |
| 458 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 459 | |||
| 456 | const array = @as(*volatile [2]u32, @ptrFromInt(0x10)); | 460 | const array = @as(*volatile [2]u32, @ptrFromInt(0x10)); |
| 457 | // Silly check just to reference `array` | 461 | // Silly check just to reference `array` |
| 458 | try expect(@intFromPtr(&array[0]) == 0x10); | 462 | try expect(@intFromPtr(&array[0]) == 0x10); |
| ... | @@ -494,6 +498,8 @@ test "pointer-integer arithmetic affects the alignment" { | ... | @@ -494,6 +498,8 @@ test "pointer-integer arithmetic affects the alignment" { |
| 494 | } | 498 | } |
| 495 | 499 | ||
| 496 | test "@intFromPtr on null optional at comptime" { | 500 | test "@intFromPtr on null optional at comptime" { |
| 501 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 502 | |||
| 497 | { | 503 | { |
| 498 | const pointer = @as(?*u8, @ptrFromInt(0x000)); | 504 | const pointer = @as(?*u8, @ptrFromInt(0x000)); |
| 499 | const x = @intFromPtr(pointer); | 505 | const x = @intFromPtr(pointer); |
| ... | @@ -704,6 +710,8 @@ test "pointer-to-array constness for zero-size elements, const" { | ... | @@ -704,6 +710,8 @@ test "pointer-to-array constness for zero-size elements, const" { |
| 704 | } | 710 | } |
| 705 | 711 | ||
| 706 | test "cast pointers with zero sized elements" { | 712 | test "cast pointers with zero sized elements" { |
| 713 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 714 | |||
| 707 | const a: *void = undefined; | 715 | const a: *void = undefined; |
| 708 | const b: *[1]void = a; | 716 | const b: *[1]void = a; |
| 709 | _ = b; | 717 | _ = b; |
test/behavior/ptrfromint.zig+1| ... | @@ -44,6 +44,7 @@ test "@ptrFromInt creates null pointer" { | ... | @@ -44,6 +44,7 @@ test "@ptrFromInt creates null pointer" { |
| 44 | test "@ptrFromInt creates allowzero zero pointer" { | 44 | test "@ptrFromInt creates allowzero zero pointer" { |
| 45 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 45 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 46 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 46 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 47 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 47 | 48 | ||
| 48 | const ptr = @as(*allowzero u32, @ptrFromInt(0)); | 49 | const ptr = @as(*allowzero u32, @ptrFromInt(0)); |
| 49 | try expectEqual(@as(usize, 0), @intFromPtr(ptr)); | 50 | try expectEqual(@as(usize, 0), @intFromPtr(ptr)); |
test/behavior/slice.zig+2| ... | @@ -238,6 +238,8 @@ test "slicing pointer by length" { | ... | @@ -238,6 +238,8 @@ test "slicing pointer by length" { |
| 238 | const x = @as([*]i32, @ptrFromInt(0x1000))[0..0x500]; | 238 | const x = @as([*]i32, @ptrFromInt(0x1000))[0..0x500]; |
| 239 | const y = x[0x100..]; | 239 | const y = x[0x100..]; |
| 240 | test "compile time slice of pointer to hard coded address" { | 240 | test "compile time slice of pointer to hard coded address" { |
| 241 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 242 | |||
| 241 | try expect(@intFromPtr(x) == 0x1000); | 243 | try expect(@intFromPtr(x) == 0x1000); |
| 242 | try expect(x.len == 0x500); | 244 | try expect(x.len == 0x500); |
| 243 | 245 |
test/behavior/struct.zig+4| ... | @@ -1347,6 +1347,8 @@ test "struct field has a pointer to an aligned version of itself" { | ... | @@ -1347,6 +1347,8 @@ test "struct field has a pointer to an aligned version of itself" { |
| 1347 | } | 1347 | } |
| 1348 | 1348 | ||
| 1349 | test "struct has only one reference" { | 1349 | test "struct has only one reference" { |
| 1350 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 1351 | |||
| 1350 | const S = struct { | 1352 | const S = struct { |
| 1351 | fn optionalStructParam(_: ?struct { x: u8 }) void {} | 1353 | fn optionalStructParam(_: ?struct { x: u8 }) void {} |
| 1352 | fn errorUnionStructParam(_: error{}!struct { x: u8 }) void {} | 1354 | fn errorUnionStructParam(_: error{}!struct { x: u8 }) void {} |
| ... | @@ -1553,6 +1555,7 @@ test "struct field pointer has correct alignment" { | ... | @@ -1553,6 +1555,7 @@ test "struct field pointer has correct alignment" { |
| 1553 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | 1555 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 1554 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1556 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1555 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 1557 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1558 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 1556 | 1559 | ||
| 1557 | const S = struct { | 1560 | const S = struct { |
| 1558 | fn doTheTest() !void { | 1561 | fn doTheTest() !void { |
| ... | @@ -1582,6 +1585,7 @@ test "extern struct field pointer has correct alignment" { | ... | @@ -1582,6 +1585,7 @@ test "extern struct field pointer has correct alignment" { |
| 1582 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | 1585 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 1583 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1586 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1584 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 1587 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1588 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 1585 | 1589 | ||
| 1586 | const S = struct { | 1590 | const S = struct { |
| 1587 | fn doTheTest() !void { | 1591 | fn doTheTest() !void { |
test/behavior/union.zig+2| ... | @@ -1481,6 +1481,7 @@ test "defined-layout union field pointer has correct alignment" { | ... | @@ -1481,6 +1481,7 @@ test "defined-layout union field pointer has correct alignment" { |
| 1481 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | 1481 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 1482 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1482 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1483 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 1483 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1484 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 1484 | 1485 | ||
| 1485 | const S = struct { | 1486 | const S = struct { |
| 1486 | fn doTheTest(comptime U: type) !void { | 1487 | fn doTheTest(comptime U: type) !void { |
| ... | @@ -1515,6 +1516,7 @@ test "undefined-layout union field pointer has correct alignment" { | ... | @@ -1515,6 +1516,7 @@ test "undefined-layout union field pointer has correct alignment" { |
| 1515 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | 1516 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 1516 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1517 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1517 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 1518 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1519 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 1518 | 1520 | ||
| 1519 | const S = struct { | 1521 | const S = struct { |
| 1520 | fn doTheTest(comptime U: type) !void { | 1522 | fn doTheTest(comptime U: type) !void { |