| ... | ... | @@ -632,11 +632,15 @@ const DeclGen = struct { |
| 632 | 632 | /// Checks whether the type can be directly translated to SPIR-V vectors |
| 633 | 633 | fn isVector(self: *DeclGen, ty: Type) bool { |
| 634 | 634 | const mod = self.module; |
| 635 | const target = self.getTarget(); |
| 635 | 636 | if (ty.zigTypeTag(mod) != .Vector) return false; |
| 636 | 637 | const elem_ty = ty.childType(mod); |
| 638 | |
| 637 | 639 | const len = ty.vectorLen(mod); |
| 638 | 640 | const is_scalar = elem_ty.isNumeric(mod) or elem_ty.toIntern() == .bool_type; |
| 639 | | return is_scalar and len > 1 and len <= 4; |
| 641 | const spirv_len = len > 1 and len <= 4; |
| 642 | const opencl_len = if (target.os.tag == .opencl) (len == 8 or len == 16) else false; |
| 643 | return is_scalar and (spirv_len or opencl_len); |
| 640 | 644 | } |
| 641 | 645 | |
| 642 | 646 | fn arithmeticTypeInfo(self: *DeclGen, ty: Type) ArithmeticTypeInfo { |
| ... | ... | @@ -1968,7 +1972,10 @@ const DeclGen = struct { |
| 1968 | 1972 | try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{ |
| 1969 | 1973 | .id_result_type = self.typeId(return_ty_ref), |
| 1970 | 1974 | .id_result = decl_id, |
| 1971 | | .function_control = .{}, // TODO: We can set inline here if the type requires it. |
| 1975 | .function_control = switch (fn_info.cc) { |
| 1976 | .Inline => .{ .Inline = true }, |
| 1977 | else => .{}, |
| 1978 | }, |
| 1972 | 1979 | .function_type = prototype_id, |
| 1973 | 1980 | }); |
| 1974 | 1981 | |
| ... | ... | @@ -2437,48 +2444,71 @@ const DeclGen = struct { |
| 2437 | 2444 | |
| 2438 | 2445 | fn minMax(self: *DeclGen, result_ty: Type, op: std.math.CompareOperator, lhs_id: IdRef, rhs_id: IdRef) !IdRef { |
| 2439 | 2446 | const info = self.arithmeticTypeInfo(result_ty); |
| 2447 | const target = self.getTarget(); |
| 2440 | 2448 | |
| 2441 | | var wip = try self.elementWise(result_ty, true); |
| 2449 | const use_backup_codegen = target.os.tag == .opencl and info.class != .float; |
| 2450 | var wip = try self.elementWise(result_ty, use_backup_codegen); |
| 2442 | 2451 | defer wip.deinit(); |
| 2452 | |
| 2443 | 2453 | for (wip.results, 0..) |*result_id, i| { |
| 2444 | 2454 | const lhs_elem_id = try wip.elementAt(result_ty, lhs_id, i); |
| 2445 | 2455 | const rhs_elem_id = try wip.elementAt(result_ty, rhs_id, i); |
| 2446 | 2456 | |
| 2447 | | // TODO: Use fmin for OpenCL |
| 2448 | | const cmp_id = try self.cmp(op, Type.bool, wip.ty, lhs_elem_id, rhs_elem_id); |
| 2449 | | const selection_id = switch (info.class) { |
| 2450 | | .float => blk: { |
| 2451 | | // cmp uses OpFOrd. When we have 0 [<>] nan this returns false, |
| 2452 | | // but we want it to pick lhs. Therefore we also have to check if |
| 2453 | | // rhs is nan. We don't need to care about the result when both |
| 2454 | | // are nan. |
| 2455 | | const rhs_is_nan_id = self.spv.allocId(); |
| 2456 | | const bool_ty_ref = try self.resolveType(Type.bool, .direct); |
| 2457 | | try self.func.body.emit(self.spv.gpa, .OpIsNan, .{ |
| 2458 | | .id_result_type = self.typeId(bool_ty_ref), |
| 2459 | | .id_result = rhs_is_nan_id, |
| 2460 | | .x = rhs_elem_id, |
| 2461 | | }); |
| 2462 | | const float_cmp_id = self.spv.allocId(); |
| 2463 | | try self.func.body.emit(self.spv.gpa, .OpLogicalOr, .{ |
| 2464 | | .id_result_type = self.typeId(bool_ty_ref), |
| 2465 | | .id_result = float_cmp_id, |
| 2466 | | .operand_1 = cmp_id, |
| 2467 | | .operand_2 = rhs_is_nan_id, |
| 2468 | | }); |
| 2469 | | break :blk float_cmp_id; |
| 2470 | | }, |
| 2471 | | else => cmp_id, |
| 2472 | | }; |
| 2457 | if (use_backup_codegen) { |
| 2458 | const cmp_id = try self.cmp(op, Type.bool, wip.ty, lhs_elem_id, rhs_elem_id); |
| 2459 | result_id.* = self.spv.allocId(); |
| 2460 | try self.func.body.emit(self.spv.gpa, .OpSelect, .{ |
| 2461 | .id_result_type = wip.ty_id, |
| 2462 | .id_result = result_id.*, |
| 2463 | .condition = cmp_id, |
| 2464 | .object_1 = lhs_elem_id, |
| 2465 | .object_2 = rhs_elem_id, |
| 2466 | }); |
| 2467 | } else { |
| 2468 | const ext_inst: Word = switch (target.os.tag) { |
| 2469 | .opencl => switch (op) { |
| 2470 | .lt => 28, // fmin |
| 2471 | .gt => 27, // fmax |
| 2472 | else => unreachable, |
| 2473 | }, |
| 2474 | .vulkan => switch (info.class) { |
| 2475 | .float => switch (op) { |
| 2476 | .lt => 37, // FMin |
| 2477 | .gt => 40, // FMax |
| 2478 | else => unreachable, |
| 2479 | }, |
| 2480 | .integer, .strange_integer => switch (info.signedness) { |
| 2481 | .signed => switch (op) { |
| 2482 | .lt => 39, // SMin |
| 2483 | .gt => 42, // SMax |
| 2484 | else => unreachable, |
| 2485 | }, |
| 2486 | .unsigned => switch (op) { |
| 2487 | .lt => 38, // UMin |
| 2488 | .gt => 41, // UMax |
| 2489 | else => unreachable, |
| 2490 | }, |
| 2491 | }, |
| 2492 | .composite_integer => unreachable, // TODO |
| 2493 | .bool => unreachable, |
| 2494 | }, |
| 2495 | else => unreachable, |
| 2496 | }; |
| 2497 | const set_id = switch (target.os.tag) { |
| 2498 | .opencl => try self.spv.importInstructionSet(.opencl), |
| 2499 | .vulkan => try self.spv.importInstructionSet(.glsl), |
| 2500 | else => unreachable, |
| 2501 | }; |
| 2473 | 2502 | |
| 2474 | | result_id.* = self.spv.allocId(); |
| 2475 | | try self.func.body.emit(self.spv.gpa, .OpSelect, .{ |
| 2476 | | .id_result_type = wip.ty_id, |
| 2477 | | .id_result = result_id.*, |
| 2478 | | .condition = selection_id, |
| 2479 | | .object_1 = lhs_elem_id, |
| 2480 | | .object_2 = rhs_elem_id, |
| 2481 | | }); |
| 2503 | result_id.* = self.spv.allocId(); |
| 2504 | try self.func.body.emit(self.spv.gpa, .OpExtInst, .{ |
| 2505 | .id_result_type = wip.ty_id, |
| 2506 | .id_result = result_id.*, |
| 2507 | .set = set_id, |
| 2508 | .instruction = .{ .inst = ext_inst }, |
| 2509 | .id_ref_4 = &.{ lhs_elem_id, rhs_elem_id }, |
| 2510 | }); |
| 2511 | } |
| 2482 | 2512 | } |
| 2483 | 2513 | return wip.finalize(); |
| 2484 | 2514 | } |
| ... | ... | @@ -2607,57 +2637,52 @@ const DeclGen = struct { |
| 2607 | 2637 | } |
| 2608 | 2638 | |
| 2609 | 2639 | fn airAbs(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 2610 | | const mod = self.module; |
| 2640 | const target = self.getTarget(); |
| 2611 | 2641 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 2612 | 2642 | const operand_id = try self.resolve(ty_op.operand); |
| 2613 | 2643 | // Note: operand_ty may be signed, while ty is always unsigned! |
| 2614 | 2644 | const operand_ty = self.typeOf(ty_op.operand); |
| 2615 | 2645 | const result_ty = self.typeOfIndex(inst); |
| 2616 | | const info = self.arithmeticTypeInfo(result_ty); |
| 2617 | | const operand_scalar_ty = operand_ty.scalarType(mod); |
| 2618 | | const operand_scalar_ty_ref = try self.resolveType(operand_scalar_ty, .direct); |
| 2646 | const operand_info = self.arithmeticTypeInfo(operand_ty); |
| 2619 | 2647 | |
| 2620 | | var wip = try self.elementWise(result_ty, true); |
| 2648 | var wip = try self.elementWise(result_ty, false); |
| 2621 | 2649 | defer wip.deinit(); |
| 2622 | 2650 | |
| 2623 | | const zero_id = switch (info.class) { |
| 2624 | | .float => try self.constFloat(operand_scalar_ty_ref, 0), |
| 2625 | | .integer, .strange_integer => try self.constInt(operand_scalar_ty_ref, 0), |
| 2626 | | .composite_integer => unreachable, // TODO |
| 2627 | | .bool => unreachable, |
| 2628 | | }; |
| 2629 | 2651 | for (wip.results, 0..) |*result_id, i| { |
| 2630 | 2652 | const elem_id = try wip.elementAt(operand_ty, operand_id, i); |
| 2631 | | // Idk why spir-v doesn't have a dedicated abs() instruction in the base |
| 2632 | | // instruction set. For now we're just going to negate and check to avoid |
| 2633 | | // importing the extinst. |
| 2634 | | // TODO: Make this a call to compiler rt / ext inst |
| 2635 | | const neg_id = self.spv.allocId(); |
| 2636 | | const args = .{ |
| 2637 | | .id_result_type = self.typeId(operand_scalar_ty_ref), |
| 2638 | | .id_result = neg_id, |
| 2639 | | .operand_1 = zero_id, |
| 2640 | | .operand_2 = elem_id, |
| 2653 | |
| 2654 | const ext_inst: Word = switch (target.os.tag) { |
| 2655 | .opencl => switch (operand_info.class) { |
| 2656 | .float => 23, // fabs |
| 2657 | .integer, .strange_integer => switch (operand_info.signedness) { |
| 2658 | .signed => 141, // s_abs |
| 2659 | .unsigned => 201, // u_abs |
| 2660 | }, |
| 2661 | .composite_integer => unreachable, // TODO |
| 2662 | .bool => unreachable, |
| 2663 | }, |
| 2664 | .vulkan => switch (operand_info.class) { |
| 2665 | .float => 4, // FAbs |
| 2666 | .integer, .strange_integer => 5, // SAbs |
| 2667 | .composite_integer => unreachable, // TODO |
| 2668 | .bool => unreachable, |
| 2669 | }, |
| 2670 | else => unreachable, |
| 2641 | 2671 | }; |
| 2642 | | switch (info.class) { |
| 2643 | | .float => try self.func.body.emit(self.spv.gpa, .OpFSub, args), |
| 2644 | | .integer, .strange_integer => try self.func.body.emit(self.spv.gpa, .OpISub, args), |
| 2645 | | .composite_integer => unreachable, // TODO |
| 2646 | | .bool => unreachable, |
| 2647 | | } |
| 2648 | | const neg_norm_id = try self.normalize(wip.ty_ref, neg_id, info); |
| 2649 | | |
| 2650 | | const gt_zero_id = try self.cmp(.gt, Type.bool, operand_scalar_ty, elem_id, zero_id); |
| 2651 | | const abs_id = self.spv.allocId(); |
| 2652 | | try self.func.body.emit(self.spv.gpa, .OpSelect, .{ |
| 2653 | | .id_result_type = self.typeId(operand_scalar_ty_ref), |
| 2654 | | .id_result = abs_id, |
| 2655 | | .condition = gt_zero_id, |
| 2656 | | .object_1 = elem_id, |
| 2657 | | .object_2 = neg_norm_id, |
| 2672 | const set_id = switch (target.os.tag) { |
| 2673 | .opencl => try self.spv.importInstructionSet(.opencl), |
| 2674 | .vulkan => try self.spv.importInstructionSet(.glsl), |
| 2675 | else => unreachable, |
| 2676 | }; |
| 2677 | |
| 2678 | result_id.* = self.spv.allocId(); |
| 2679 | try self.func.body.emit(self.spv.gpa, .OpExtInst, .{ |
| 2680 | .id_result_type = wip.ty_id, |
| 2681 | .id_result = result_id.*, |
| 2682 | .set = set_id, |
| 2683 | .instruction = .{ .inst = ext_inst }, |
| 2684 | .id_ref_4 = &.{elem_id}, |
| 2658 | 2685 | }); |
| 2659 | | // For Shader, we may need to cast from signed to unsigned here. |
| 2660 | | result_id.* = try self.bitCast(wip.ty, operand_scalar_ty, abs_id); |
| 2661 | 2686 | } |
| 2662 | 2687 | return try wip.finalize(); |
| 2663 | 2688 | } |