| author | |
| committer | |
| log | 6e3770e970dbf460271a0e0cb60c2bf40a7c861e |
| tree | fa880846415c07b0d153688a1e1a7b8c35971671 |
| parent | 7077e90b3f8991c844deb08a16ad3f4e0569398f |
| signature |
It turns out that the Khronos LLVM SPIRV translator does not support OpPtrEqual.
Therefore, this instruction is emitted using a series of conversions.
This commit breaks intToEnum, because enum was removed from the arithmetic type
info. The enum should be converted to an int before this function is called.4 files changed, 127 insertions(+), 87 deletions(-)
src/codegen/spirv.zig+114-61| ... | @@ -369,17 +369,6 @@ pub const DeclGen = struct { | ... | @@ -369,17 +369,6 @@ pub const DeclGen = struct { |
| 369 | .composite_integer, | 369 | .composite_integer, |
| 370 | }; | 370 | }; |
| 371 | }, | 371 | }, |
| 372 | .Enum => blk: { | ||
| 373 | var buffer: Type.Payload.Bits = undefined; | ||
| 374 | const int_ty = ty.intTagType(&buffer); | ||
| 375 | const int_info = int_ty.intInfo(target); | ||
| 376 | break :blk ArithmeticTypeInfo{ | ||
| 377 | .bits = int_info.bits, | ||
| 378 | .is_vector = false, | ||
| 379 | .signedness = int_info.signedness, | ||
| 380 | .class = .integer, | ||
| 381 | }; | ||
| 382 | }, | ||
| 383 | // As of yet, there is no vector support in the self-hosted compiler. | 372 | // As of yet, there is no vector support in the self-hosted compiler. |
| 384 | .Vector => self.todo("implement arithmeticTypeInfo for Vector", .{}), | 373 | .Vector => self.todo("implement arithmeticTypeInfo for Vector", .{}), |
| 385 | // TODO: For which types is this the case? | 374 | // TODO: For which types is this the case? |
| ... | @@ -1742,12 +1731,12 @@ pub const DeclGen = struct { | ... | @@ -1742,12 +1731,12 @@ pub const DeclGen = struct { |
| 1742 | .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), | 1731 | .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), |
| 1743 | .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), | 1732 | .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), |
| 1744 | 1733 | ||
| 1745 | .cmp_eq => try self.airCmp(inst, .OpFOrdEqual, .OpLogicalEqual, .OpIEqual), | 1734 | .cmp_eq => try self.airCmp(inst, .eq), |
| 1746 | .cmp_neq => try self.airCmp(inst, .OpFOrdNotEqual, .OpLogicalNotEqual, .OpINotEqual), | 1735 | .cmp_neq => try self.airCmp(inst, .neq), |
| 1747 | .cmp_gt => try self.airCmp(inst, .OpFOrdGreaterThan, .OpSGreaterThan, .OpUGreaterThan), | 1736 | .cmp_gt => try self.airCmp(inst, .gt), |
| 1748 | .cmp_gte => try self.airCmp(inst, .OpFOrdGreaterThanEqual, .OpSGreaterThanEqual, .OpUGreaterThanEqual), | 1737 | .cmp_gte => try self.airCmp(inst, .gte), |
| 1749 | .cmp_lt => try self.airCmp(inst, .OpFOrdLessThan, .OpSLessThan, .OpULessThan), | 1738 | .cmp_lt => try self.airCmp(inst, .lt), |
| 1750 | .cmp_lte => try self.airCmp(inst, .OpFOrdLessThanEqual, .OpSLessThanEqual, .OpULessThanEqual), | 1739 | .cmp_lte => try self.airCmp(inst, .lte), |
| 1751 | 1740 | ||
| 1752 | .arg => self.airArg(), | 1741 | .arg => self.airArg(), |
| 1753 | .alloc => try self.airAlloc(inst), | 1742 | .alloc => try self.airAlloc(inst), |
| ... | @@ -2039,58 +2028,122 @@ pub const DeclGen = struct { | ... | @@ -2039,58 +2028,122 @@ pub const DeclGen = struct { |
| 2039 | return result_id; | 2028 | return result_id; |
| 2040 | } | 2029 | } |
| 2041 | 2030 | ||
| 2042 | fn airCmp(self: *DeclGen, inst: Air.Inst.Index, comptime fop: Opcode, comptime sop: Opcode, comptime uop: Opcode) !?IdRef { | 2031 | fn cmp( |
| 2043 | if (self.liveness.isUnused(inst)) return null; | 2032 | self: *DeclGen, |
| 2044 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 2033 | comptime op: std.math.CompareOperator, |
| 2045 | var lhs_id = try self.resolve(bin_op.lhs); | 2034 | bool_ty_id: IdRef, |
| 2046 | var rhs_id = try self.resolve(bin_op.rhs); | 2035 | ty: Type, |
| 2047 | const result_id = self.spv.allocId(); | 2036 | lhs_id: IdRef, |
| 2048 | const result_type_id = try self.resolveTypeId(Type.bool); | 2037 | rhs_id: IdRef, |
| 2049 | const op_ty = self.air.typeOf(bin_op.lhs); | 2038 | ) !IdRef { |
| 2050 | assert(op_ty.eql(self.air.typeOf(bin_op.rhs), self.module)); | 2039 | var cmp_lhs_id = lhs_id; |
| 2040 | var cmp_rhs_id = rhs_id; | ||
| 2041 | const opcode: Opcode = opcode: { | ||
| 2042 | var int_buffer: Type.Payload.Bits = undefined; | ||
| 2043 | const op_ty = switch (ty.zigTypeTag()) { | ||
| 2044 | .Int, .Bool, .Float => ty, | ||
| 2045 | .Enum => ty.intTagType(&int_buffer), | ||
| 2046 | .ErrorSet => Type.u16, | ||
| 2047 | .Pointer => blk: { | ||
| 2048 | // Note that while SPIR-V offers OpPtrEqual and OpPtrNotEqual, they are | ||
| 2049 | // currently not implemented in the SPIR-V LLVM translator. Thus, we emit these using | ||
| 2050 | // OpConvertPtrToU... | ||
| 2051 | cmp_lhs_id = self.spv.allocId(); | ||
| 2052 | cmp_rhs_id = self.spv.allocId(); | ||
| 2053 | |||
| 2054 | const usize_ty_id = self.typeId(try self.sizeType()); | ||
| 2055 | |||
| 2056 | try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{ | ||
| 2057 | .id_result_type = usize_ty_id, | ||
| 2058 | .id_result = cmp_lhs_id, | ||
| 2059 | .pointer = lhs_id, | ||
| 2060 | }); | ||
| 2051 | 2061 | ||
| 2052 | // Comparisons are generally applicable to both scalar and vector operations in SPIR-V, | 2062 | try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{ |
| 2053 | // but int and float versions of operations require different opcodes. | 2063 | .id_result_type = usize_ty_id, |
| 2054 | const info = try self.arithmeticTypeInfo(op_ty); | 2064 | .id_result = cmp_rhs_id, |
| 2065 | .pointer = rhs_id, | ||
| 2066 | }); | ||
| 2055 | 2067 | ||
| 2056 | const opcode_index: usize = switch (info.class) { | 2068 | break :blk Type.usize; |
| 2057 | .composite_integer => { | 2069 | }, |
| 2058 | return self.todo("binary operations for composite integers", .{}); | 2070 | .Optional => unreachable, // TODO |
| 2059 | }, | 2071 | else => unreachable, |
| 2060 | .float => 0, | 2072 | }; |
| 2061 | .bool => 1, | ||
| 2062 | .strange_integer => blk: { | ||
| 2063 | const op_ty_ref = try self.resolveType(op_ty, .direct); | ||
| 2064 | lhs_id = try self.maskStrangeInt(op_ty_ref, lhs_id, info.bits); | ||
| 2065 | rhs_id = try self.maskStrangeInt(op_ty_ref, rhs_id, info.bits); | ||
| 2066 | break :blk switch (info.signedness) { | ||
| 2067 | .signed => @as(usize, 1), | ||
| 2068 | .unsigned => @as(usize, 2), | ||
| 2069 | }; | ||
| 2070 | }, | ||
| 2071 | .integer => switch (info.signedness) { | ||
| 2072 | .signed => @as(usize, 1), | ||
| 2073 | .unsigned => @as(usize, 2), | ||
| 2074 | }, | ||
| 2075 | }; | ||
| 2076 | 2073 | ||
| 2077 | const operands = .{ | 2074 | const info = try self.arithmeticTypeInfo(op_ty); |
| 2078 | .id_result_type = result_type_id, | 2075 | const signedness = switch (info.class) { |
| 2079 | .id_result = result_id, | 2076 | .composite_integer => { |
| 2080 | .operand_1 = lhs_id, | 2077 | return self.todo("binary operations for composite integers", .{}); |
| 2081 | .operand_2 = rhs_id, | 2078 | }, |
| 2082 | }; | 2079 | .float => break :opcode switch (op) { |
| 2080 | .eq => .OpFOrdEqual, | ||
| 2081 | .neq => .OpFOrdNotEqual, | ||
| 2082 | .lt => .OpFOrdLessThan, | ||
| 2083 | .lte => .OpFOrdLessThanEqual, | ||
| 2084 | .gt => .OpFOrdGreaterThan, | ||
| 2085 | .gte => .OpFOrdGreaterThanEqual, | ||
| 2086 | }, | ||
| 2087 | .bool => break :opcode switch (op) { | ||
| 2088 | .eq => .OpIEqual, | ||
| 2089 | .neq => .OpINotEqual, | ||
| 2090 | else => unreachable, | ||
| 2091 | }, | ||
| 2092 | .strange_integer => sign: { | ||
| 2093 | const op_ty_ref = try self.resolveType(op_ty, .direct); | ||
| 2094 | // Mask operands before performing comparison. | ||
| 2095 | cmp_lhs_id = try self.maskStrangeInt(op_ty_ref, cmp_lhs_id, info.bits); | ||
| 2096 | cmp_rhs_id = try self.maskStrangeInt(op_ty_ref, cmp_rhs_id, info.bits); | ||
| 2097 | break :sign info.signedness; | ||
| 2098 | }, | ||
| 2099 | .integer => info.signedness, | ||
| 2100 | }; | ||
| 2083 | 2101 | ||
| 2084 | switch (opcode_index) { | 2102 | break :opcode switch (signedness) { |
| 2085 | 0 => try self.func.body.emit(self.spv.gpa, fop, operands), | 2103 | .unsigned => switch (op) { |
| 2086 | 1 => try self.func.body.emit(self.spv.gpa, sop, operands), | 2104 | .eq => .OpIEqual, |
| 2087 | 2 => try self.func.body.emit(self.spv.gpa, uop, operands), | 2105 | .neq => .OpINotEqual, |
| 2088 | else => unreachable, | 2106 | .lt => .OpULessThan, |
| 2089 | } | 2107 | .lte => .OpULessThanEqual, |
| 2108 | .gt => .OpUGreaterThan, | ||
| 2109 | .gte => .OpUGreaterThanEqual, | ||
| 2110 | }, | ||
| 2111 | .signed => switch (op) { | ||
| 2112 | .eq => .OpIEqual, | ||
| 2113 | .neq => .OpINotEqual, | ||
| 2114 | .lt => .OpSLessThan, | ||
| 2115 | .lte => .OpSLessThanEqual, | ||
| 2116 | .gt => .OpSGreaterThan, | ||
| 2117 | .gte => .OpSGreaterThanEqual, | ||
| 2118 | }, | ||
| 2119 | }; | ||
| 2120 | }; | ||
| 2090 | 2121 | ||
| 2122 | const result_id = self.spv.allocId(); | ||
| 2123 | try self.func.body.emitRaw(self.spv.gpa, opcode, 4); | ||
| 2124 | self.func.body.writeOperand(spec.IdResultType, bool_ty_id); | ||
| 2125 | self.func.body.writeOperand(spec.IdResult, result_id); | ||
| 2126 | self.func.body.writeOperand(spec.IdResultType, cmp_lhs_id); | ||
| 2127 | self.func.body.writeOperand(spec.IdResultType, cmp_rhs_id); | ||
| 2091 | return result_id; | 2128 | return result_id; |
| 2092 | } | 2129 | } |
| 2093 | 2130 | ||
| 2131 | fn airCmp( | ||
| 2132 | self: *DeclGen, | ||
| 2133 | inst: Air.Inst.Index, | ||
| 2134 | comptime op: std.math.CompareOperator, | ||
| 2135 | ) !?IdRef { | ||
| 2136 | if (self.liveness.isUnused(inst)) return null; | ||
| 2137 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | ||
| 2138 | const lhs_id = try self.resolve(bin_op.lhs); | ||
| 2139 | const rhs_id = try self.resolve(bin_op.rhs); | ||
| 2140 | const bool_ty_id = try self.resolveTypeId(Type.bool); | ||
| 2141 | const ty = self.air.typeOf(bin_op.lhs); | ||
| 2142 | assert(ty.eql(self.air.typeOf(bin_op.rhs), self.module)); | ||
| 2143 | |||
| 2144 | return try self.cmp(op, bool_ty_id, ty, lhs_id, rhs_id); | ||
| 2145 | } | ||
| 2146 | |||
| 2094 | fn bitcast(self: *DeclGen, target_type_id: IdResultType, value_id: IdRef) !IdRef { | 2147 | fn bitcast(self: *DeclGen, target_type_id: IdResultType, value_id: IdRef) !IdRef { |
| 2095 | const result_id = self.spv.allocId(); | 2148 | const result_id = self.spv.allocId(); |
| 2096 | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ | 2149 | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ |
test/behavior/basic.zig-17| ... | @@ -134,21 +134,18 @@ fn first4KeysOfHomeRow() []const u8 { | ... | @@ -134,21 +134,18 @@ fn first4KeysOfHomeRow() []const u8 { |
| 134 | 134 | ||
| 135 | test "return string from function" { | 135 | test "return string from function" { |
| 136 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 136 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 137 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 138 | 137 | ||
| 139 | try expect(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); | 138 | try expect(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); |
| 140 | } | 139 | } |
| 141 | 140 | ||
| 142 | test "hex escape" { | 141 | test "hex escape" { |
| 143 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 142 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 144 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 145 | 143 | ||
| 146 | try expect(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); | 144 | try expect(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); |
| 147 | } | 145 | } |
| 148 | 146 | ||
| 149 | test "multiline string" { | 147 | test "multiline string" { |
| 150 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 148 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 151 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 152 | 149 | ||
| 153 | const s1 = | 150 | const s1 = |
| 154 | \\one | 151 | \\one |
| ... | @@ -161,7 +158,6 @@ test "multiline string" { | ... | @@ -161,7 +158,6 @@ test "multiline string" { |
| 161 | 158 | ||
| 162 | test "multiline string comments at start" { | 159 | test "multiline string comments at start" { |
| 163 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 160 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 164 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 165 | 161 | ||
| 166 | const s1 = | 162 | const s1 = |
| 167 | //\\one | 163 | //\\one |
| ... | @@ -174,7 +170,6 @@ test "multiline string comments at start" { | ... | @@ -174,7 +170,6 @@ test "multiline string comments at start" { |
| 174 | 170 | ||
| 175 | test "multiline string comments at end" { | 171 | test "multiline string comments at end" { |
| 176 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 172 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 177 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 178 | 173 | ||
| 179 | const s1 = | 174 | const s1 = |
| 180 | \\one | 175 | \\one |
| ... | @@ -187,7 +182,6 @@ test "multiline string comments at end" { | ... | @@ -187,7 +182,6 @@ test "multiline string comments at end" { |
| 187 | 182 | ||
| 188 | test "multiline string comments in middle" { | 183 | test "multiline string comments in middle" { |
| 189 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 184 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 190 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 191 | 185 | ||
| 192 | const s1 = | 186 | const s1 = |
| 193 | \\one | 187 | \\one |
| ... | @@ -200,7 +194,6 @@ test "multiline string comments in middle" { | ... | @@ -200,7 +194,6 @@ test "multiline string comments in middle" { |
| 200 | 194 | ||
| 201 | test "multiline string comments at multiple places" { | 195 | test "multiline string comments at multiple places" { |
| 202 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 196 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 203 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 204 | 197 | ||
| 205 | const s1 = | 198 | const s1 = |
| 206 | \\one | 199 | \\one |
| ... | @@ -214,14 +207,11 @@ test "multiline string comments at multiple places" { | ... | @@ -214,14 +207,11 @@ test "multiline string comments at multiple places" { |
| 214 | } | 207 | } |
| 215 | 208 | ||
| 216 | test "string concatenation simple" { | 209 | test "string concatenation simple" { |
| 217 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 218 | |||
| 219 | try expect(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED")); | 210 | try expect(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED")); |
| 220 | } | 211 | } |
| 221 | 212 | ||
| 222 | test "array mult operator" { | 213 | test "array mult operator" { |
| 223 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 214 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 224 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 225 | 215 | ||
| 226 | try expect(mem.eql(u8, "ab" ** 5, "ababababab")); | 216 | try expect(mem.eql(u8, "ab" ** 5, "ababababab")); |
| 227 | } | 217 | } |
| ... | @@ -387,7 +377,6 @@ test "take address of parameter" { | ... | @@ -387,7 +377,6 @@ test "take address of parameter" { |
| 387 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 377 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 388 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 378 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 389 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 379 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 390 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 391 | 380 | ||
| 392 | try testTakeAddressOfParameter(12.34); | 381 | try testTakeAddressOfParameter(12.34); |
| 393 | } | 382 | } |
| ... | @@ -690,8 +679,6 @@ test "explicit cast optional pointers" { | ... | @@ -690,8 +679,6 @@ test "explicit cast optional pointers" { |
| 690 | } | 679 | } |
| 691 | 680 | ||
| 692 | test "pointer comparison" { | 681 | test "pointer comparison" { |
| 693 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 694 | |||
| 695 | const a = @as([]const u8, "a"); | 682 | const a = @as([]const u8, "a"); |
| 696 | const b = &a; | 683 | const b = &a; |
| 697 | try expect(ptrEql(b, b)); | 684 | try expect(ptrEql(b, b)); |
| ... | @@ -892,8 +879,6 @@ test "catch in block has correct result location" { | ... | @@ -892,8 +879,6 @@ test "catch in block has correct result location" { |
| 892 | } | 879 | } |
| 893 | 880 | ||
| 894 | test "labeled block with runtime branch forwards its result location type to break statements" { | 881 | test "labeled block with runtime branch forwards its result location type to break statements" { |
| 895 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 896 | |||
| 897 | const E = enum { a, b }; | 882 | const E = enum { a, b }; |
| 898 | var a = false; | 883 | var a = false; |
| 899 | const e: E = blk: { | 884 | const e: E = blk: { |
| ... | @@ -1062,8 +1047,6 @@ test "switch inside @as gets correct type" { | ... | @@ -1062,8 +1047,6 @@ test "switch inside @as gets correct type" { |
| 1062 | } | 1047 | } |
| 1063 | 1048 | ||
| 1064 | test "inline call of function with a switch inside the return statement" { | 1049 | test "inline call of function with a switch inside the return statement" { |
| 1065 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 1066 | |||
| 1067 | const S = struct { | 1050 | const S = struct { |
| 1068 | inline fn foo(x: anytype) @TypeOf(x) { | 1051 | inline fn foo(x: anytype) @TypeOf(x) { |
| 1069 | return switch (x) { | 1052 | return switch (x) { |
test/behavior/enum.zig+2| ... | @@ -20,6 +20,8 @@ test "enum to int" { | ... | @@ -20,6 +20,8 @@ test "enum to int" { |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | fn testIntToEnumEval(x: i32) !void { | 22 | fn testIntToEnumEval(x: i32) !void { |
| 23 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 24 | |||
| 23 | try expect(@intToEnum(IntToEnumNumber, x) == IntToEnumNumber.Three); | 25 | try expect(@intToEnum(IntToEnumNumber, x) == IntToEnumNumber.Three); |
| 24 | } | 26 | } |
| 25 | const IntToEnumNumber = enum { Zero, One, Two, Three, Four }; | 27 | const IntToEnumNumber = enum { Zero, One, Two, Three, Four }; |
test/behavior/memcpy.zig+11-9| ... | @@ -67,14 +67,16 @@ fn testMemcpyDestManyPtr() !void { | ... | @@ -67,14 +67,16 @@ fn testMemcpyDestManyPtr() !void { |
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | comptime { | 69 | comptime { |
| 70 | const S = struct { | 70 | if (builtin.zig_backend != .stage2_spirv64) { |
| 71 | buffer: [8]u8 = undefined, | 71 | const S = struct { |
| 72 | fn set(self: *@This(), items: []const u8) void { | 72 | buffer: [8]u8 = undefined, |
| 73 | @memcpy(self.buffer[0..items.len], items); | 73 | fn set(self: *@This(), items: []const u8) void { |
| 74 | } | 74 | @memcpy(self.buffer[0..items.len], items); |
| 75 | }; | 75 | } |
| 76 | }; | ||
| 76 | 77 | ||
| 77 | var s = S{}; | 78 | var s = S{}; |
| 78 | s.set("hello"); | 79 | s.set("hello"); |
| 79 | if (!std.mem.eql(u8, s.buffer[0..5], "hello")) @compileError("bad"); | 80 | if (!std.mem.eql(u8, s.buffer[0..5], "hello")) @compileError("bad"); |
| 81 | } | ||
| 80 | } | 82 | } |