authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-05-18 02:45:21+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-05-20 17:30:21+02:00
log6e3770e970dbf460271a0e0cb60c2bf40a7c861e
treefa880846415c07b0d153688a1e1a7b8c35971671
parent7077e90b3f8991c844deb08a16ad3f4e0569398f
signaturelock-open Commit is signed but in an unrecognized format.

spirv: implement pointer comparison in for air cmp

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),
17441733
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),
17511740
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 }
20412030
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 });
20512061
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 });
20552067
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 };
20762073
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 };
20832101
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 };
20902121
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 }
20932130
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 {
134134
135test "return string from function" {135test "return string from function" {
136 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO136 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
137 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
138137
139 try expect(mem.eql(u8, first4KeysOfHomeRow(), "aoeu"));138 try expect(mem.eql(u8, first4KeysOfHomeRow(), "aoeu"));
140}139}
141140
142test "hex escape" {141test "hex escape" {
143 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO142 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
144 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
145143
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}
148146
149test "multiline string" {147test "multiline string" {
150 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO148 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
151 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
152149
153 const s1 =150 const s1 =
154 \\one151 \\one
...@@ -161,7 +158,6 @@ test "multiline string" {...@@ -161,7 +158,6 @@ test "multiline string" {
161158
162test "multiline string comments at start" {159test "multiline string comments at start" {
163 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO160 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
164 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
165161
166 const s1 =162 const s1 =
167 //\\one163 //\\one
...@@ -174,7 +170,6 @@ test "multiline string comments at start" {...@@ -174,7 +170,6 @@ test "multiline string comments at start" {
174170
175test "multiline string comments at end" {171test "multiline string comments at end" {
176 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO172 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
177 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
178173
179 const s1 =174 const s1 =
180 \\one175 \\one
...@@ -187,7 +182,6 @@ test "multiline string comments at end" {...@@ -187,7 +182,6 @@ test "multiline string comments at end" {
187182
188test "multiline string comments in middle" {183test "multiline string comments in middle" {
189 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO184 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
190 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
191185
192 const s1 =186 const s1 =
193 \\one187 \\one
...@@ -200,7 +194,6 @@ test "multiline string comments in middle" {...@@ -200,7 +194,6 @@ test "multiline string comments in middle" {
200194
201test "multiline string comments at multiple places" {195test "multiline string comments at multiple places" {
202 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO196 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
203 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
204197
205 const s1 =198 const s1 =
206 \\one199 \\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}
215208
216test "string concatenation simple" {209test "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}
221212
222test "array mult operator" {213test "array mult operator" {
223 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO214 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
224 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
225215
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; // TODO379 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
390 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
391380
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}
691680
692test "pointer comparison" {681test "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}
893880
894test "labeled block with runtime branch forwards its result location type to break statements" {881test "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}
10631048
1064test "inline call of function with a switch inside the return statement" {1049test "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}
2121
22fn testIntToEnumEval(x: i32) !void {22fn 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}
25const IntToEnumNumber = enum { Zero, One, Two, Three, Four };27const 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}
6868
69comptime {69comptime {
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 };
7677
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}