authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2024-03-30 18:30:28+01:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2024-03-30 19:47:55+01:00
log12350f53bf5e39403e11b9902ca85bea97f65722
treef08dfc69c2baa89782d8d75683a0eb3b6b7d0dad
parentf5ab3c93c9a083b730e91e362001da7b32668938
signaturebadge-check Signed by SSH key SHA256:ZS52FNyUv2WUXvO4njmVaFVO46RHojFuOrxRc4LuKzg

spirv: clz, ctz for opencl

This instruction seems common in compiler_rt.

2 files changed, 80 insertions(+), 3 deletions(-)

src/codegen/spirv.zig+80
...@@ -2332,6 +2332,9 @@ const DeclGen = struct {...@@ -2332,6 +2332,9 @@ const DeclGen = struct {
23322332
2333 .mul_add => try self.airMulAdd(inst),2333 .mul_add => try self.airMulAdd(inst),
23342334
2335 .ctz => try self.airClzCtz(inst, .ctz),
2336 .clz => try self.airClzCtz(inst, .clz),
2337
2335 .splat => try self.airSplat(inst),2338 .splat => try self.airSplat(inst),
2336 .reduce, .reduce_optimized => try self.airReduce(inst),2339 .reduce, .reduce_optimized => try self.airReduce(inst),
2337 .shuffle => try self.airShuffle(inst),2340 .shuffle => try self.airShuffle(inst),
...@@ -3029,6 +3032,83 @@ const DeclGen = struct {...@@ -3029,6 +3032,83 @@ const DeclGen = struct {
3029 return try wip.finalize();3032 return try wip.finalize();
3030 }3033 }
30313034
3035 fn airClzCtz(self: *DeclGen, inst: Air.Inst.Index, op: enum { clz, ctz }) !?IdRef {
3036 if (self.liveness.isUnused(inst)) return null;
3037
3038 const mod = self.module;
3039 const target = self.getTarget();
3040 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3041 const result_ty = self.typeOfIndex(inst);
3042 const operand_ty = self.typeOf(ty_op.operand);
3043 const operand = try self.resolve(ty_op.operand);
3044
3045 const info = self.arithmeticTypeInfo(operand_ty);
3046 switch (info.class) {
3047 .composite_integer => unreachable, // TODO
3048 .integer, .strange_integer => {},
3049 .float, .bool => unreachable,
3050 }
3051
3052 var wip = try self.elementWise(result_ty, false);
3053 defer wip.deinit();
3054
3055 const elem_ty = if (wip.is_array) operand_ty.scalarType(mod) else operand_ty;
3056 const elem_ty_ref = try self.resolveType(elem_ty, .direct);
3057 const elem_ty_id = self.typeId(elem_ty_ref);
3058
3059 for (wip.results, 0..) |*result_id, i| {
3060 const elem = try wip.elementAt(operand_ty, operand, i);
3061
3062 switch (target.os.tag) {
3063 .opencl => {
3064 const set = try self.spv.importInstructionSet(.@"OpenCL.std");
3065 const ext_inst: u32 = switch (op) {
3066 .clz => 151, // clz
3067 .ctz => 152, // ctz
3068 };
3069
3070 // Note: result of OpenCL ctz/clz returns operand_ty, and we want result_ty.
3071 // result_ty is always large enough to hold the result, so we might have to down
3072 // cast it.
3073 const tmp = self.spv.allocId();
3074 try self.func.body.emit(self.spv.gpa, .OpExtInst, .{
3075 .id_result_type = elem_ty_id,
3076 .id_result = tmp,
3077 .set = set,
3078 .instruction = .{ .inst = ext_inst },
3079 .id_ref_4 = &.{elem},
3080 });
3081
3082 if (wip.ty_id == elem_ty_id) {
3083 result_id.* = tmp;
3084 continue;
3085 }
3086
3087 result_id.* = self.spv.allocId();
3088 if (result_ty.scalarType(mod).isSignedInt(mod)) {
3089 assert(elem_ty.scalarType(mod).isSignedInt(mod));
3090 try self.func.body.emit(self.spv.gpa, .OpSConvert, .{
3091 .id_result_type = wip.ty_id,
3092 .id_result = result_id.*,
3093 .signed_value = tmp,
3094 });
3095 } else {
3096 assert(elem_ty.scalarType(mod).isUnsignedInt(mod));
3097 try self.func.body.emit(self.spv.gpa, .OpUConvert, .{
3098 .id_result_type = wip.ty_id,
3099 .id_result = result_id.*,
3100 .unsigned_value = tmp,
3101 });
3102 }
3103 },
3104 .vulkan => unreachable, // TODO
3105 else => unreachable,
3106 }
3107 }
3108
3109 return try wip.finalize();
3110 }
3111
3032 fn airSplat(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {3112 fn airSplat(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
3033 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3113 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3034 const operand_id = try self.resolve(ty_op.operand);3114 const operand_id = try self.resolve(ty_op.operand);
test/behavior/math.zig-3
...@@ -65,7 +65,6 @@ test "@clz" {...@@ -65,7 +65,6 @@ test "@clz" {
65 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO65 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
66 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO66 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
67 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO67 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
68 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
6968
70 try testClz();69 try testClz();
71 try comptime testClz();70 try comptime testClz();
...@@ -148,7 +147,6 @@ test "@ctz" {...@@ -148,7 +147,6 @@ test "@ctz" {
148 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO147 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
149 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO148 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
150 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO149 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
151 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
152150
153 try testCtz();151 try testCtz();
154 try comptime testCtz();152 try comptime testCtz();
...@@ -1752,7 +1750,6 @@ test "@clz works on both vector and scalar inputs" {...@@ -1752,7 +1750,6 @@ test "@clz works on both vector and scalar inputs" {
1752 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1750 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1753 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1751 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1754 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1752 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1755 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
17561753
1757 var x: u32 = 0x1;1754 var x: u32 = 0x1;
1758 _ = &x;1755 _ = &x;