MSL: Support unordered relational operators.

The SPIR-V spec says that these check if the operands either are
unordered or satisfy the given condition. So that's just what we'll do,
using Metal's `isunordered()` stdlib function. Apple's optimizers ought
to be able to collapse that to a single unordered compare.
This commit is contained in:
Chip Davis 2018-08-31 13:46:02 -05:00
parent e14bf77b1a
commit d3233690cb
4 changed files with 147 additions and 0 deletions

View File

@ -0,0 +1,30 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
constant float a_tmp [[function_constant(1)]];
constant float a = is_function_constant_defined(a_tmp) ? a_tmp : 1.0;
constant float b_tmp [[function_constant(2)]];
constant float b = is_function_constant_defined(b_tmp) ? b_tmp : 2.0;
struct main0_out
{
float4 FragColor [[color(0)]];
};
fragment main0_out main0()
{
main0_out out = {};
float t0 = a;
float t1 = b;
bool c21 = (isunordered(a, b) || a == b);
bool c22 = (isunordered(a, b) || a != b);
bool c23 = (isunordered(a, b) || a < b);
bool c25 = (isunordered(a, b) || a > b);
bool c27 = (isunordered(a, b) || a <= b);
bool c29 = (isunordered(a, b) || a >= b);
out.FragColor = float4(t0 + t1);
return out;
}

View File

@ -0,0 +1,77 @@
; SPIR-V
; Version: 1.3
; Generator: Khronos Glslang Reference Front End; 7
; Bound: 33
; Schema: 0
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %FragColor
OpExecutionMode %main OriginUpperLeft
OpSource ESSL 310
OpName %main "main"
OpName %t0 "t0"
OpName %a "a"
OpName %t1 "t1"
OpName %b "b"
OpName %c21 "c21"
OpName %c22 "c22"
OpName %c23 "c23"
OpName %c25 "c25"
OpName %c27 "c27"
OpName %c29 "c29"
OpName %FragColor "FragColor"
OpDecorate %t0 RelaxedPrecision
OpDecorate %a RelaxedPrecision
OpDecorate %a SpecId 1
OpDecorate %t1 RelaxedPrecision
OpDecorate %b RelaxedPrecision
OpDecorate %b SpecId 2
OpDecorate %FragColor RelaxedPrecision
OpDecorate %FragColor Location 0
OpDecorate %29 RelaxedPrecision
OpDecorate %30 RelaxedPrecision
OpDecorate %31 RelaxedPrecision
OpDecorate %32 RelaxedPrecision
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%_ptr_Function_float = OpTypePointer Function %float
%a = OpSpecConstant %float 1
%b = OpSpecConstant %float 2
%bool = OpTypeBool
%_ptr_Function_bool = OpTypePointer Function %bool
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%FragColor = OpVariable %_ptr_Output_v4float Output
%main = OpFunction %void None %3
%5 = OpLabel
%t0 = OpVariable %_ptr_Function_float Function
%t1 = OpVariable %_ptr_Function_float Function
%c21 = OpVariable %_ptr_Function_bool Function
%c22 = OpVariable %_ptr_Function_bool Function
%c23 = OpVariable %_ptr_Function_bool Function
%c25 = OpVariable %_ptr_Function_bool Function
%c27 = OpVariable %_ptr_Function_bool Function
%c29 = OpVariable %_ptr_Function_bool Function
OpStore %t0 %a
OpStore %t1 %b
%15 = OpFUnordEqual %bool %a %b
OpStore %c21 %15
%17 = OpFUnordNotEqual %bool %a %b
OpStore %c22 %17
%19 = OpFUnordLessThan %bool %a %b
OpStore %c23 %19
%21 = OpFUnordGreaterThan %bool %a %b
OpStore %c25 %21
%23 = OpFUnordLessThanEqual %bool %a %b
OpStore %c27 %23
%25 = OpFUnordGreaterThanEqual %bool %a %b
OpStore %c29 %25
%29 = OpLoad %float %t0
%30 = OpLoad %float %t1
%31 = OpFAdd %float %29 %30
%32 = OpCompositeConstruct %v4float %31 %31 %31 %31
OpStore %FragColor %32
OpReturn
OpFunctionEnd

View File

@ -1562,6 +1562,20 @@ void CompilerMSL::emit_specialization_constants()
statement("");
}
void CompilerMSL::emit_binary_unord_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1,
const char *op)
{
bool forward = should_forward(op0) && should_forward(op1);
emit_op(result_type, result_id,
join("(isunordered(", to_enclosed_unpacked_expression(op0), ", ", to_enclosed_unpacked_expression(op1),
") || ", to_enclosed_unpacked_expression(op0), " ", op, " ", to_enclosed_unpacked_expression(op1),
")"),
forward);
inherit_expression_dependencies(result_id, op0);
inherit_expression_dependencies(result_id, op1);
}
// Override for MSL-specific syntax instructions
void CompilerMSL::emit_instruction(const Instruction &instruction)
{
@ -1576,6 +1590,7 @@ void CompilerMSL::emit_instruction(const Instruction &instruction)
#define MSL_BFOP_CAST(op, type) \
emit_binary_func_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, opcode_is_sign_invariant(opcode))
#define MSL_UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op)
#define MSL_UNORD_BOP(op) emit_binary_unord_op(ops[0], ops[1], ops[2], ops[3], #op)
auto ops = stream(instruction);
auto opcode = static_cast<Op>(instruction.op);
@ -1620,6 +1635,30 @@ void CompilerMSL::emit_instruction(const Instruction &instruction)
MSL_BOP(<=);
break;
case OpFUnordEqual:
MSL_UNORD_BOP(==);
break;
case OpFUnordNotEqual:
MSL_UNORD_BOP(!=);
break;
case OpFUnordGreaterThan:
MSL_UNORD_BOP(>);
break;
case OpFUnordGreaterThanEqual:
MSL_UNORD_BOP(>=);
break;
case OpFUnordLessThan:
MSL_UNORD_BOP(<);
break;
case OpFUnordLessThanEqual:
MSL_UNORD_BOP(<=);
break;
// Derivatives
case OpDPdx:
case OpDPdxFine:

View File

@ -276,6 +276,7 @@ public:
void remap_constexpr_sampler(uint32_t id, const MSLConstexprSampler &sampler);
protected:
void emit_binary_unord_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op);
void emit_instruction(const Instruction &instr) override;
void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args,
uint32_t count) override;