authorgravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2024-02-18 13:39:32+03:30
committergravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2024-04-06 08:50:02+03:30
log0f75143c621b239dd79abc32de0911a68fa7caa4
tree0972b885452801b0a4c79522c4121ddd31348790
parent23f729aec96cec68be15436db2a38e37d54fbe8f

spirv: implement `@divFloor`, `@floor` and `@mod`


5 files changed, 132 insertions(+), 25 deletions(-)

src/codegen/spirv.zig+130-18
...@@ -2317,6 +2317,9 @@ const DeclGen = struct {...@@ -2317,6 +2317,9 @@ const DeclGen = struct {
2317 .mul, .mul_wrap, .mul_optimized => try self.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul),2317 .mul, .mul_wrap, .mul_optimized => try self.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul),
23182318
2319 .abs => try self.airAbs(inst),2319 .abs => try self.airAbs(inst),
2320 .floor => try self.airFloor(inst),
2321
2322 .div_floor => try self.airDivFloor(inst),
23202323
2321 .div_float,2324 .div_float,
2322 .div_float_optimized,2325 .div_float_optimized,
...@@ -2328,6 +2331,11 @@ const DeclGen = struct {...@@ -2328,6 +2331,11 @@ const DeclGen = struct {
2328 .rem,2331 .rem,
2329 .rem_optimized,2332 .rem_optimized,
2330 => try self.airArithOp(inst, .OpFRem, .OpSRem, .OpSRem),2333 => try self.airArithOp(inst, .OpFRem, .OpSRem, .OpSRem),
2334 // TODO: Check if this is the right operation
2335 .mod,
2336 .mod_optimized,
2337 => try self.airArithOp(inst, .OpFMod, .OpSMod, .OpSMod),
2338
23312339
2332 .add_with_overflow => try self.airAddSubOverflow(inst, .OpIAdd, .OpULessThan, .OpSLessThan),2340 .add_with_overflow => try self.airAddSubOverflow(inst, .OpIAdd, .OpULessThan, .OpSLessThan),
2333 .sub_with_overflow => try self.airAddSubOverflow(inst, .OpISub, .OpUGreaterThan, .OpSGreaterThan),2341 .sub_with_overflow => try self.airAddSubOverflow(inst, .OpISub, .OpUGreaterThan, .OpSGreaterThan),
...@@ -2661,6 +2669,95 @@ const DeclGen = struct {...@@ -2661,6 +2669,95 @@ const DeclGen = struct {
2661 }2669 }
2662 }2670 }
26632671
2672 fn airDivFloor(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
2673 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2674 const lhs_id = try self.resolve(bin_op.lhs);
2675 const rhs_id = try self.resolve(bin_op.rhs);
2676 const ty = self.typeOfIndex(inst);
2677 const ty_ref = try self.resolveType(ty, .direct);
2678 const info = self.arithmeticTypeInfo(ty);
2679 switch (info.class) {
2680 .composite_integer => unreachable, // TODO
2681 .integer, .strange_integer => {
2682 const zero_id = try self.constInt(ty_ref, 0);
2683 const one_id = try self.constInt(ty_ref, 1);
2684
2685 // (a ^ b) > 0
2686 const bin_bitwise_id = try self.binOpSimple(ty, lhs_id, rhs_id, .OpBitwiseXor);
2687 const is_positive_id = try self.cmp(.gt, Type.bool, ty, bin_bitwise_id, zero_id);
2688
2689 // a / b
2690 const positive_div_id = try self.arithOp(ty, lhs_id, rhs_id, .OpFDiv, .OpSDiv, .OpUDiv);
2691
2692 // - (abs(a) + abs(b) - 1) / abs(b)
2693 const lhs_abs = try self.abs(ty, ty, lhs_id);
2694 const rhs_abs = try self.abs(ty, ty, rhs_id);
2695 const negative_div_lhs = try self.arithOp(
2696 ty,
2697 try self.arithOp(ty, lhs_abs, rhs_abs, .OpFAdd, .OpIAdd, .OpIAdd),
2698 one_id,
2699 .OpFSub,
2700 .OpISub,
2701 .OpISub,
2702 );
2703 const negative_div_id = try self.arithOp(ty, negative_div_lhs, rhs_abs, .OpFDiv, .OpSDiv, .OpUDiv);
2704 const negated_negative_div_id = self.spv.allocId();
2705 try self.func.body.emit(self.spv.gpa, .OpSNegate, .{
2706 .id_result_type = self.typeId(ty_ref),
2707 .id_result = negated_negative_div_id,
2708 .operand = negative_div_id,
2709 });
2710
2711 const result_id = self.spv.allocId();
2712 try self.func.body.emit(self.spv.gpa, .OpSelect, .{
2713 .id_result_type = self.typeId(ty_ref),
2714 .id_result = result_id,
2715 .condition = is_positive_id,
2716 .object_1 = positive_div_id,
2717 .object_2 = negated_negative_div_id,
2718 });
2719 return result_id;
2720 },
2721 .float => {
2722 const div_id = try self.arithOp(ty, lhs_id, rhs_id, .OpFDiv, .OpSDiv, .OpUDiv);
2723 return try self.floor(ty, div_id);
2724 },
2725 .bool => unreachable,
2726 }
2727 }
2728
2729 fn airFloor(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
2730 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
2731 const operand_id = try self.resolve(un_op);
2732 const result_ty = self.typeOfIndex(inst);
2733 return try self.floor(result_ty, operand_id);
2734 }
2735
2736 fn floor(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef {
2737 const target = self.getTarget();
2738 const ty_ref = try self.resolveType(ty, .direct);
2739 const ext_inst: Word = switch (target.os.tag) {
2740 .opencl => 25,
2741 .vulkan => 8,
2742 else => unreachable,
2743 };
2744 const set_id = switch (target.os.tag) {
2745 .opencl => try self.spv.importInstructionSet(.opencl),
2746 .vulkan => try self.spv.importInstructionSet(.glsl),
2747 else => unreachable,
2748 };
2749
2750 const result_id = self.spv.allocId();
2751 try self.func.body.emit(self.spv.gpa, .OpExtInst, .{
2752 .id_result_type = self.typeId(ty_ref),
2753 .id_result = result_id,
2754 .set = set_id,
2755 .instruction = .{ .inst = ext_inst },
2756 .id_ref_4 = &.{operand_id},
2757 });
2758 return result_id;
2759 }
2760
2664 fn airArithOp(2761 fn airArithOp(
2665 self: *DeclGen,2762 self: *DeclGen,
2666 inst: Air.Inst.Index,2763 inst: Air.Inst.Index,
...@@ -2668,7 +2765,6 @@ const DeclGen = struct {...@@ -2668,7 +2765,6 @@ const DeclGen = struct {
2668 comptime sop: Opcode,2765 comptime sop: Opcode,
2669 comptime uop: Opcode,2766 comptime uop: Opcode,
2670 ) !?IdRef {2767 ) !?IdRef {
2671
2672 // LHS and RHS are guaranteed to have the same type, and AIR guarantees2768 // LHS and RHS are guaranteed to have the same type, and AIR guarantees
2673 // the result to be the same as the LHS and RHS, which matches SPIR-V.2769 // the result to be the same as the LHS and RHS, which matches SPIR-V.
2674 const ty = self.typeOfIndex(inst);2770 const ty = self.typeOfIndex(inst);
...@@ -2737,12 +2833,16 @@ const DeclGen = struct {...@@ -2737,12 +2833,16 @@ const DeclGen = struct {
2737 }2833 }
27382834
2739 fn airAbs(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {2835 fn airAbs(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
2740 const target = self.getTarget();
2741 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;2836 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2742 const operand_id = try self.resolve(ty_op.operand);2837 const operand_id = try self.resolve(ty_op.operand);
2743 // Note: operand_ty may be signed, while ty is always unsigned!2838 // Note: operand_ty may be signed, while ty is always unsigned!
2744 const operand_ty = self.typeOf(ty_op.operand);2839 const operand_ty = self.typeOf(ty_op.operand);
2745 const result_ty = self.typeOfIndex(inst);2840 const result_ty = self.typeOfIndex(inst);
2841 return try self.abs(result_ty, operand_ty, operand_id);
2842 }
2843
2844 fn abs(self: *DeclGen, result_ty: Type, operand_ty: Type, operand_id: IdRef) !IdRef {
2845 const target = self.getTarget();
2746 const operand_info = self.arithmeticTypeInfo(operand_ty);2846 const operand_info = self.arithmeticTypeInfo(operand_ty);
27472847
2748 var wip = try self.elementWise(result_ty, false);2848 var wip = try self.elementWise(result_ty, false);
...@@ -3692,19 +3792,22 @@ const DeclGen = struct {...@@ -3692,19 +3792,22 @@ const DeclGen = struct {
3692 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3792 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3693 const operand_ty = self.typeOf(ty_op.operand);3793 const operand_ty = self.typeOf(ty_op.operand);
3694 const operand_id = try self.resolve(ty_op.operand);3794 const operand_id = try self.resolve(ty_op.operand);
3695 const operand_info = self.arithmeticTypeInfo(operand_ty);3795 const result_ty = self.typeOfIndex(inst);
3696 const dest_ty = self.typeOfIndex(inst);3796 const result_ty_ref = try self.resolveType(result_ty, .direct);
3697 const dest_ty_id = try self.resolveTypeId(dest_ty);3797 return try self.floatFromInt(result_ty_ref, operand_ty, operand_id);
3798 }
36983799
3800 fn floatFromInt(self: *DeclGen, result_ty_ref: CacheRef, operand_ty: Type, operand_id: IdRef) !IdRef {
3801 const operand_info = self.arithmeticTypeInfo(operand_ty);
3699 const result_id = self.spv.allocId();3802 const result_id = self.spv.allocId();
3700 switch (operand_info.signedness) {3803 switch (operand_info.signedness) {
3701 .signed => try self.func.body.emit(self.spv.gpa, .OpConvertSToF, .{3804 .signed => try self.func.body.emit(self.spv.gpa, .OpConvertSToF, .{
3702 .id_result_type = dest_ty_id,3805 .id_result_type = self.typeId(result_ty_ref),
3703 .id_result = result_id,3806 .id_result = result_id,
3704 .signed_value = operand_id,3807 .signed_value = operand_id,
3705 }),3808 }),
3706 .unsigned => try self.func.body.emit(self.spv.gpa, .OpConvertUToF, .{3809 .unsigned => try self.func.body.emit(self.spv.gpa, .OpConvertUToF, .{
3707 .id_result_type = dest_ty_id,3810 .id_result_type = self.typeId(result_ty_ref),
3708 .id_result = result_id,3811 .id_result = result_id,
3709 .unsigned_value = operand_id,3812 .unsigned_value = operand_id,
3710 }),3813 }),
...@@ -3715,19 +3818,22 @@ const DeclGen = struct {...@@ -3715,19 +3818,22 @@ const DeclGen = struct {
3715 fn airIntFromFloat(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {3818 fn airIntFromFloat(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
3716 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3819 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3717 const operand_id = try self.resolve(ty_op.operand);3820 const operand_id = try self.resolve(ty_op.operand);
3718 const dest_ty = self.typeOfIndex(inst);3821 const result_ty = self.typeOfIndex(inst);
3719 const dest_info = self.arithmeticTypeInfo(dest_ty);3822 return try self.intFromFloat(result_ty, operand_id);
3720 const dest_ty_id = try self.resolveTypeId(dest_ty);3823 }
37213824
3825 fn intFromFloat(self: *DeclGen, result_ty: Type, operand_id: IdRef) !IdRef {
3826 const result_info = self.arithmeticTypeInfo(result_ty);
3827 const result_ty_ref = try self.resolveType(result_ty, .direct);
3722 const result_id = self.spv.allocId();3828 const result_id = self.spv.allocId();
3723 switch (dest_info.signedness) {3829 switch (result_info.signedness) {
3724 .signed => try self.func.body.emit(self.spv.gpa, .OpConvertFToS, .{3830 .signed => try self.func.body.emit(self.spv.gpa, .OpConvertFToS, .{
3725 .id_result_type = dest_ty_id,3831 .id_result_type = self.typeId(result_ty_ref),
3726 .id_result = result_id,3832 .id_result = result_id,
3727 .float_value = operand_id,3833 .float_value = operand_id,
3728 }),3834 }),
3729 .unsigned => try self.func.body.emit(self.spv.gpa, .OpConvertFToU, .{3835 .unsigned => try self.func.body.emit(self.spv.gpa, .OpConvertFToU, .{
3730 .id_result_type = dest_ty_id,3836 .id_result_type = self.typeId(result_ty_ref),
3731 .id_result = result_id,3837 .id_result = result_id,
3732 .float_value = operand_id,3838 .float_value = operand_id,
3733 }),3839 }),
...@@ -5237,14 +5343,15 @@ const DeclGen = struct {...@@ -5237,14 +5343,15 @@ const DeclGen = struct {
52375343
5238 fn airSwitchBr(self: *DeclGen, inst: Air.Inst.Index) !void {5344 fn airSwitchBr(self: *DeclGen, inst: Air.Inst.Index) !void {
5239 const mod = self.module;5345 const mod = self.module;
5346 const target = self.getTarget();
5240 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;5347 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
5241 const cond_ty = self.typeOf(pl_op.operand);5348 const cond_ty = self.typeOf(pl_op.operand);
5242 const cond = try self.resolve(pl_op.operand);5349 const cond = try self.resolve(pl_op.operand);
5243 const cond_indirect = try self.convertToIndirect(cond_ty, cond);5350 var cond_indirect = try self.convertToIndirect(cond_ty, cond);
5244 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);5351 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
52455352
5246 const cond_words: u32 = switch (cond_ty.zigTypeTag(mod)) {5353 const cond_words: u32 = switch (cond_ty.zigTypeTag(mod)) {
5247 .Bool => 1,5354 .Bool, .ErrorSet => 1,
5248 .Int => blk: {5355 .Int => blk: {
5249 const bits = cond_ty.intInfo(mod).bits;5356 const bits = cond_ty.intInfo(mod).bits;
5250 const backing_bits = self.backingIntBits(bits) orelse {5357 const backing_bits = self.backingIntBits(bits) orelse {
...@@ -5260,8 +5367,12 @@ const DeclGen = struct {...@@ -5260,8 +5367,12 @@ const DeclGen = struct {
5260 };5367 };
5261 break :blk if (backing_bits <= 32) @as(u32, 1) else 2;5368 break :blk if (backing_bits <= 32) @as(u32, 1) else 2;
5262 },5369 },
5263 .ErrorSet => 1,5370 .Pointer => blk: {
5264 else => return self.todo("implement switch for type {s}", .{@tagName(cond_ty.zigTypeTag(mod))}), // TODO: Figure out which types apply here, and work around them as we can only do integers.5371 cond_indirect = try self.intFromPtr(cond_indirect);
5372 break :blk target.ptrBitWidth() / 32;
5373 },
5374 // TODO: Figure out which types apply here, and work around them as we can only do integers.
5375 else => return self.todo("implement switch for type {s}", .{@tagName(cond_ty.zigTypeTag(mod))}),
5265 };5376 };
52665377
5267 const num_cases = switch_br.data.cases_len;5378 const num_cases = switch_br.data.cases_len;
...@@ -5316,13 +5427,14 @@ const DeclGen = struct {...@@ -5316,13 +5427,14 @@ const DeclGen = struct {
53165427
5317 for (items) |item| {5428 for (items) |item| {
5318 const value = (try self.air.value(item, mod)) orelse unreachable;5429 const value = (try self.air.value(item, mod)) orelse unreachable;
5319 const int_val = switch (cond_ty.zigTypeTag(mod)) {5430 const int_val: u64 = switch (cond_ty.zigTypeTag(mod)) {
5320 .Bool, .Int => if (cond_ty.isSignedInt(mod)) @as(u64, @bitCast(value.toSignedInt(mod))) else value.toUnsignedInt(mod),5431 .Bool, .Int => if (cond_ty.isSignedInt(mod)) @as(u64, @bitCast(value.toSignedInt(mod))) else value.toUnsignedInt(mod),
5321 .Enum => blk: {5432 .Enum => blk: {
5322 // TODO: figure out of cond_ty is correct (something with enum literals)5433 // TODO: figure out of cond_ty is correct (something with enum literals)
5323 break :blk (try value.intFromEnum(cond_ty, mod)).toUnsignedInt(mod); // TODO: composite integer constants5434 break :blk (try value.intFromEnum(cond_ty, mod)).toUnsignedInt(mod); // TODO: composite integer constants
5324 },5435 },
5325 .ErrorSet => value.getErrorInt(mod),5436 .ErrorSet => value.getErrorInt(mod),
5437 .Pointer => value.toUnsignedInt(mod),
5326 else => unreachable,5438 else => unreachable,
5327 };5439 };
5328 const int_lit: spec.LiteralContextDependentNumber = switch (cond_words) {5440 const int_lit: spec.LiteralContextDependentNumber = switch (cond_words) {
src/codegen/spirv/Module.zig+2-2
...@@ -429,8 +429,8 @@ pub fn constInt(self: *Module, ty_ref: CacheRef, value: anytype) !IdRef {...@@ -429,8 +429,8 @@ pub fn constInt(self: *Module, ty_ref: CacheRef, value: anytype) !IdRef {
429 return try self.resolveId(.{ .int = .{429 return try self.resolveId(.{ .int = .{
430 .ty = ty_ref,430 .ty = ty_ref,
431 .value = switch (ty.signedness) {431 .value = switch (ty.signedness) {
432 .signed => Value{ .int64 = @as(i64, @intCast(value)) },432 .signed => Value{ .int64 = @intCast(value) },
433 .unsigned => Value{ .uint64 = @as(u64, @intCast(value)) },433 .unsigned => Value{ .uint64 = @intCast(value) },
434 },434 },
435 } });435 } });
436}436}
test/behavior/floatop.zig-3
...@@ -1089,7 +1089,6 @@ test "@floor f16" {...@@ -1089,7 +1089,6 @@ test "@floor f16" {
1089 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1089 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1090 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1090 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1091 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;1091 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
1092 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
10931092
1094 try testFloor(f16);1093 try testFloor(f16);
1095 try comptime testFloor(f16);1094 try comptime testFloor(f16);
...@@ -1100,7 +1099,6 @@ test "@floor f32/f64" {...@@ -1100,7 +1099,6 @@ test "@floor f32/f64" {
1100 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1099 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1101 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1100 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1102 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;1101 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
1103 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
11041102
1105 try testFloor(f32);1103 try testFloor(f32);
1106 try comptime testFloor(f32);1104 try comptime testFloor(f32);
...@@ -1162,7 +1160,6 @@ fn testFloor(comptime T: type) !void {...@@ -1162,7 +1160,6 @@ fn testFloor(comptime T: type) !void {
1162test "@floor with vectors" {1160test "@floor with vectors" {
1163 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1161 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1164 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1162 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1165 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1166 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO1163 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1167 if (builtin.zig_backend == .stage2_x86_64 and1164 if (builtin.zig_backend == .stage2_x86_64 and
1168 !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest;1165 !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest;
test/behavior/int_div.zig-1
...@@ -6,7 +6,6 @@ test "integer division" {...@@ -6,7 +6,6 @@ test "integer division" {
6 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;6 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
7 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;7 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
8 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO8 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
109
11 try testDivision();10 try testDivision();
12 try comptime testDivision();11 try comptime testDivision();
test/behavior/switch.zig-1
...@@ -640,7 +640,6 @@ test "switch prong pointer capture alignment" {...@@ -640,7 +640,6 @@ test "switch prong pointer capture alignment" {
640test "switch on pointer type" {640test "switch on pointer type" {
641 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO641 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
642 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO642 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
643 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
644643
645 const S = struct {644 const S = struct {
646 const X = struct {645 const X = struct {