Replace CubeFaceCoord and CubeFaceIndexAMD (#2840)
Part of #2814.
diff --git a/source/opt/amd_ext_to_khr.cpp b/source/opt/amd_ext_to_khr.cpp
index 3a49858..e9b7f86 100644
--- a/source/opt/amd_ext_to_khr.cpp
+++ b/source/opt/amd_ext_to_khr.cpp
@@ -14,6 +14,9 @@
#include "source/opt/amd_ext_to_khr.h"
+#include <set>
+#include <string>
+
#include "ir_builder.h"
#include "source/opt/ir_context.h"
#include "spv-amd-shader-ballot.insts.inc"
@@ -43,11 +46,19 @@
SMid3AMD = 9
};
+enum AmdGcnShader { CubeFaceCoordAMD = 2, CubeFaceIndexAMD = 1, TimeAMD = 3 };
+
analysis::Type* GetUIntType(IRContext* ctx) {
analysis::Integer int_type(32, false);
return ctx->get_type_mgr()->GetRegisteredType(&int_type);
}
+bool NotImplementedYet(IRContext*, Instruction*,
+ const std::vector<const analysis::Constant*>&) {
+ assert(false && "Not implemented.");
+ return false;
+}
+
// Returns a folding rule that replaces |op(a,b,c)| by |op(op(a,b),c)|, where
// |op| is either min or max. |opcode| is the binary opcode in the GLSLstd450
// extended instruction set that corresponds to the trinary instruction being
@@ -322,7 +333,6 @@
analysis::DefUseManager* def_use_mgr = ctx->get_def_use_mgr();
analysis::ConstantManager* const_mgr = ctx->get_constant_mgr();
- // ctx->AddCapability(SpvCapabilitySubgroupBallotKHR);
ctx->AddCapability(SpvCapabilityGroupNonUniformBallot);
ctx->AddCapability(SpvCapabilityGroupNonUniformShuffle);
@@ -515,6 +525,281 @@
return true;
}
+// A folding rule that will replace the CubeFaceCoordAMD extended
+// instruction in the SPV_AMD_gcn_shader_ballot. Returns true if the folding is
+// successful.
+//
+// The instruction
+//
+// %result = OpExtInst %v2float %1 CubeFaceCoordAMD %input
+//
+// with
+//
+// %x = OpCompositeExtract %float %input 0
+// %y = OpCompositeExtract %float %input 1
+// %z = OpCompositeExtract %float %input 2
+// %nx = OpFNegate %float %x
+// %ny = OpFNegate %float %y
+// %nz = OpFNegate %float %z
+// %ax = OpExtInst %float %n_1 FAbs %x
+// %ay = OpExtInst %float %n_1 FAbs %y
+// %az = OpExtInst %float %n_1 FAbs %z
+// %amax_x_y = OpExtInst %float %n_1 FMax %ay %ax
+// %amax = OpExtInst %float %n_1 FMax %az %amax_x_y
+// %cubema = OpFMul %float %float_2 %amax
+// %is_z_max = OpFOrdGreaterThanEqual %bool %az %amax_x_y
+// %not_is_z_max = OpLogicalNot %bool %is_z_max
+// %y_gt_x = OpFOrdGreaterThanEqual %bool %ay %ax
+// %is_y_max = OpLogicalAnd %bool %not_is_z_max %y_gt_x
+// %is_z_neg = OpFOrdLessThan %bool %z %float_0
+// %cubesc_case_1 = OpSelect %float %is_z_neg %nx %x
+// %is_x_neg = OpFOrdLessThan %bool %x %float_0
+// %cubesc_case_2 = OpSelect %float %is_x_neg %z %nz
+// %sel = OpSelect %float %is_y_max %x %cubesc_case_2
+// %cubesc = OpSelect %float %is_z_max %cubesc_case_1 %sel
+// %is_y_neg = OpFOrdLessThan %bool %y %float_0
+// %cubetc_case_1 = OpSelect %float %is_y_neg %nz %z
+// %cubetc = OpSelect %float %is_y_max %cubetc_case_1 %ny
+// %cube = OpCompositeConstruct %v2float %cubesc %cubetc
+// %denom = OpCompositeConstruct %v2float %cubema %cubema
+// %div = OpFDiv %v2float %cube %denom
+// %result = OpFAdd %v2float %div %const
+//
+// Also adding the capabilities and builtins that are needed.
+bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst,
+ const std::vector<const analysis::Constant*>&) {
+ analysis::TypeManager* type_mgr = ctx->get_type_mgr();
+ analysis::ConstantManager* const_mgr = ctx->get_constant_mgr();
+
+ uint32_t float_type_id = type_mgr->GetFloatTypeId();
+ const analysis::Type* v2_float_type = type_mgr->GetFloatVectorType(2);
+ uint32_t v2_float_type_id = type_mgr->GetId(v2_float_type);
+ uint32_t bool_id = type_mgr->GetBoolTypeId();
+
+ InstructionBuilder ir_builder(
+ ctx, inst,
+ IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+
+ uint32_t input_id = inst->GetSingleWordInOperand(2);
+ uint32_t glsl405_ext_inst_id =
+ ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+ if (glsl405_ext_inst_id == 0) {
+ ctx->AddExtInstImport("GLSL.std.450");
+ glsl405_ext_inst_id =
+ ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+ }
+
+ // Get the constants that will be used.
+ uint32_t f0_const_id = const_mgr->GetFloatConst(0.0);
+ uint32_t f2_const_id = const_mgr->GetFloatConst(2.0);
+ uint32_t f0_5_const_id = const_mgr->GetFloatConst(0.5);
+ const analysis::Constant* vec_const =
+ const_mgr->GetConstant(v2_float_type, {f0_5_const_id, f0_5_const_id});
+ uint32_t vec_const_id =
+ const_mgr->GetDefiningInstruction(vec_const)->result_id();
+
+ // Extract the input values.
+ Instruction* x = ir_builder.AddCompositeExtract(float_type_id, input_id, {0});
+ Instruction* y = ir_builder.AddCompositeExtract(float_type_id, input_id, {1});
+ Instruction* z = ir_builder.AddCompositeExtract(float_type_id, input_id, {2});
+
+ // Negate the input values.
+ Instruction* nx =
+ ir_builder.AddUnaryOp(float_type_id, SpvOpFNegate, x->result_id());
+ Instruction* ny =
+ ir_builder.AddUnaryOp(float_type_id, SpvOpFNegate, y->result_id());
+ Instruction* nz =
+ ir_builder.AddUnaryOp(float_type_id, SpvOpFNegate, z->result_id());
+
+ // Get the abolsute values of the inputs.
+ Instruction* ax = ir_builder.AddNaryExtendedInstruction(
+ float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {x->result_id()});
+ Instruction* ay = ir_builder.AddNaryExtendedInstruction(
+ float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {y->result_id()});
+ Instruction* az = ir_builder.AddNaryExtendedInstruction(
+ float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {z->result_id()});
+
+ // Find which values are negative. Used in later computations.
+ Instruction* is_z_neg = ir_builder.AddBinaryOp(bool_id, SpvOpFOrdLessThan,
+ z->result_id(), f0_const_id);
+ Instruction* is_y_neg = ir_builder.AddBinaryOp(bool_id, SpvOpFOrdLessThan,
+ y->result_id(), f0_const_id);
+ Instruction* is_x_neg = ir_builder.AddBinaryOp(bool_id, SpvOpFOrdLessThan,
+ x->result_id(), f0_const_id);
+
+ // Compute cubema
+ Instruction* amax_x_y = ir_builder.AddNaryExtendedInstruction(
+ float_type_id, glsl405_ext_inst_id, GLSLstd450FMax,
+ {ax->result_id(), ay->result_id()});
+ Instruction* amax = ir_builder.AddNaryExtendedInstruction(
+ float_type_id, glsl405_ext_inst_id, GLSLstd450FMax,
+ {az->result_id(), amax_x_y->result_id()});
+ Instruction* cubema = ir_builder.AddBinaryOp(float_type_id, SpvOpFMul,
+ f2_const_id, amax->result_id());
+
+ // Do the comparisons needed for computing cubesc and cubetc.
+ Instruction* is_z_max =
+ ir_builder.AddBinaryOp(bool_id, SpvOpFOrdGreaterThanEqual,
+ az->result_id(), amax_x_y->result_id());
+ Instruction* not_is_z_max =
+ ir_builder.AddUnaryOp(bool_id, SpvOpLogicalNot, is_z_max->result_id());
+ Instruction* y_gr_x = ir_builder.AddBinaryOp(
+ bool_id, SpvOpFOrdGreaterThanEqual, ay->result_id(), ax->result_id());
+ Instruction* is_y_max = ir_builder.AddBinaryOp(
+ bool_id, SpvOpLogicalAnd, not_is_z_max->result_id(), y_gr_x->result_id());
+
+ // Select the correct value for cubesc.
+ Instruction* cubesc_case_1 = ir_builder.AddSelect(
+ float_type_id, is_z_neg->result_id(), nx->result_id(), x->result_id());
+ Instruction* cubesc_case_2 = ir_builder.AddSelect(
+ float_type_id, is_x_neg->result_id(), z->result_id(), nz->result_id());
+ Instruction* sel =
+ ir_builder.AddSelect(float_type_id, is_y_max->result_id(), x->result_id(),
+ cubesc_case_2->result_id());
+ Instruction* cubesc =
+ ir_builder.AddSelect(float_type_id, is_z_max->result_id(),
+ cubesc_case_1->result_id(), sel->result_id());
+
+ // Select the correct value for cubetc.
+ Instruction* cubetc_case_1 = ir_builder.AddSelect(
+ float_type_id, is_y_neg->result_id(), nz->result_id(), z->result_id());
+ Instruction* cubetc =
+ ir_builder.AddSelect(float_type_id, is_y_max->result_id(),
+ cubetc_case_1->result_id(), ny->result_id());
+
+ // Do the division
+ Instruction* cube = ir_builder.AddCompositeConstruct(
+ v2_float_type_id, {cubesc->result_id(), cubetc->result_id()});
+ Instruction* denom = ir_builder.AddCompositeConstruct(
+ v2_float_type_id, {cubema->result_id(), cubema->result_id()});
+ Instruction* div = ir_builder.AddBinaryOp(
+ v2_float_type_id, SpvOpFDiv, cube->result_id(), denom->result_id());
+
+ // Get the final result by adding 0.5 to |div|.
+ inst->SetOpcode(SpvOpFAdd);
+ Instruction::OperandList new_operands;
+ new_operands.push_back({SPV_OPERAND_TYPE_ID, {div->result_id()}});
+ new_operands.push_back({SPV_OPERAND_TYPE_ID, {vec_const_id}});
+
+ inst->SetInOperands(std::move(new_operands));
+ ctx->UpdateDefUse(inst);
+ return true;
+}
+
+// A folding rule that will replace the CubeFaceCoordAMD extended
+// instruction in the SPV_AMD_gcn_shader_ballot. Returns true if the folding
+// is successful.
+//
+// The instruction
+//
+// %result = OpExtInst %v2float %1 CubeFaceCoordAMD %input
+//
+// with
+//
+// %x = OpCompositeExtract %float %input 0
+// %y = OpCompositeExtract %float %input 1
+// %z = OpCompositeExtract %float %input 2
+// %ax = OpExtInst %float %n_1 FAbs %x
+// %ay = OpExtInst %float %n_1 FAbs %y
+// %az = OpExtInst %float %n_1 FAbs %z
+// %is_z_neg = OpFOrdLessThan %bool %z %float_0
+// %is_y_neg = OpFOrdLessThan %bool %y %float_0
+// %is_x_neg = OpFOrdLessThan %bool %x %float_0
+// %amax_x_y = OpExtInst %float %n_1 FMax %ay %ax
+// %is_z_max = OpFOrdGreaterThanEqual %bool %az %amax_x_y
+// %y_gt_x = OpFOrdGreaterThanEqual %bool %ay %ax
+// %case_z = OpSelect %float %is_z_neg %float_5 %float4
+// %case_y = OpSelect %float %is_y_neg %float_3 %float2
+// %case_x = OpSelect %float %is_x_neg %float_1 %float0
+// %sel = OpSelect %float %y_gt_x %case_y %case_x
+// %result = OpSelect %float %is_z_max %case_z %sel
+//
+// Also adding the capabilities and builtins that are needed.
+bool ReplaceCubeFaceIndex(IRContext* ctx, Instruction* inst,
+ const std::vector<const analysis::Constant*>&) {
+ analysis::TypeManager* type_mgr = ctx->get_type_mgr();
+ analysis::ConstantManager* const_mgr = ctx->get_constant_mgr();
+
+ uint32_t float_type_id = type_mgr->GetFloatTypeId();
+ uint32_t bool_id = type_mgr->GetBoolTypeId();
+
+ InstructionBuilder ir_builder(
+ ctx, inst,
+ IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+
+ uint32_t input_id = inst->GetSingleWordInOperand(2);
+ uint32_t glsl405_ext_inst_id =
+ ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+ if (glsl405_ext_inst_id == 0) {
+ ctx->AddExtInstImport("GLSL.std.450");
+ glsl405_ext_inst_id =
+ ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+ }
+
+ // Get the constants that will be used.
+ uint32_t f0_const_id = const_mgr->GetFloatConst(0.0);
+ uint32_t f1_const_id = const_mgr->GetFloatConst(1.0);
+ uint32_t f2_const_id = const_mgr->GetFloatConst(2.0);
+ uint32_t f3_const_id = const_mgr->GetFloatConst(3.0);
+ uint32_t f4_const_id = const_mgr->GetFloatConst(4.0);
+ uint32_t f5_const_id = const_mgr->GetFloatConst(5.0);
+
+ // Extract the input values.
+ Instruction* x = ir_builder.AddCompositeExtract(float_type_id, input_id, {0});
+ Instruction* y = ir_builder.AddCompositeExtract(float_type_id, input_id, {1});
+ Instruction* z = ir_builder.AddCompositeExtract(float_type_id, input_id, {2});
+
+ // Get the absolute values of the inputs.
+ Instruction* ax = ir_builder.AddNaryExtendedInstruction(
+ float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {x->result_id()});
+ Instruction* ay = ir_builder.AddNaryExtendedInstruction(
+ float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {y->result_id()});
+ Instruction* az = ir_builder.AddNaryExtendedInstruction(
+ float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {z->result_id()});
+
+ // Find which values are negative. Used in later computations.
+ Instruction* is_z_neg = ir_builder.AddBinaryOp(bool_id, SpvOpFOrdLessThan,
+ z->result_id(), f0_const_id);
+ Instruction* is_y_neg = ir_builder.AddBinaryOp(bool_id, SpvOpFOrdLessThan,
+ y->result_id(), f0_const_id);
+ Instruction* is_x_neg = ir_builder.AddBinaryOp(bool_id, SpvOpFOrdLessThan,
+ x->result_id(), f0_const_id);
+
+ // Find the max value.
+ Instruction* amax_x_y = ir_builder.AddNaryExtendedInstruction(
+ float_type_id, glsl405_ext_inst_id, GLSLstd450FMax,
+ {ax->result_id(), ay->result_id()});
+ Instruction* is_z_max =
+ ir_builder.AddBinaryOp(bool_id, SpvOpFOrdGreaterThanEqual,
+ az->result_id(), amax_x_y->result_id());
+ Instruction* y_gr_x = ir_builder.AddBinaryOp(
+ bool_id, SpvOpFOrdGreaterThanEqual, ay->result_id(), ax->result_id());
+
+ // Get the value for each case.
+ Instruction* case_z = ir_builder.AddSelect(
+ float_type_id, is_z_neg->result_id(), f5_const_id, f4_const_id);
+ Instruction* case_y = ir_builder.AddSelect(
+ float_type_id, is_y_neg->result_id(), f3_const_id, f2_const_id);
+ Instruction* case_x = ir_builder.AddSelect(
+ float_type_id, is_x_neg->result_id(), f1_const_id, f0_const_id);
+
+ // Select the correct case.
+ Instruction* sel =
+ ir_builder.AddSelect(float_type_id, y_gr_x->result_id(),
+ case_y->result_id(), case_x->result_id());
+
+ // Get the final result by adding 0.5 to |div|.
+ inst->SetOpcode(SpvOpSelect);
+ Instruction::OperandList new_operands;
+ new_operands.push_back({SPV_OPERAND_TYPE_ID, {is_z_max->result_id()}});
+ new_operands.push_back({SPV_OPERAND_TYPE_ID, {case_z->result_id()}});
+ new_operands.push_back({SPV_OPERAND_TYPE_ID, {sel->result_id()}});
+
+ inst->SetInOperands(std::move(new_operands));
+ ctx->UpdateDefUse(inst);
+ return true;
+}
+
class AmdExtFoldingRules : public FoldingRules {
public:
explicit AmdExtFoldingRules(IRContext* ctx) : FoldingRules(ctx) {}
@@ -575,6 +860,17 @@
ext_rules_[{extension_id, SMid3AMD}].push_back(
ReplaceTrinaryMid<GLSLstd450SMin, GLSLstd450SMax, GLSLstd450SClamp>);
}
+
+ extension_id =
+ context()->module()->GetExtInstImportId("SPV_AMD_gcn_shader");
+
+ if (extension_id != 0) {
+ ext_rules_[{extension_id, CubeFaceCoordAMD}].push_back(
+ ReplaceCubeFaceCoord);
+ ext_rules_[{extension_id, CubeFaceIndexAMD}].push_back(
+ ReplaceCubeFaceIndex);
+ ext_rules_[{extension_id, TimeAMD}].push_back(NotImplementedYet);
+ }
}
};
@@ -607,17 +903,15 @@
// Now that instruction that require the extensions have been removed, we can
// remove the extension instructions.
+ std::set<std::string> ext_to_remove = {"SPV_AMD_shader_ballot",
+ "SPV_AMD_shader_trinary_minmax",
+ "SPV_AMD_gcn_shader"};
+
std::vector<Instruction*> to_be_killed;
for (Instruction& inst : context()->module()->extensions()) {
if (inst.opcode() == SpvOpExtension) {
- if (strcmp("SPV_AMD_shader_ballot",
- reinterpret_cast<const char*>(
- &(inst.GetInOperand(0).words[0]))) == 0) {
- to_be_killed.push_back(&inst);
- }
- if (strcmp("SPV_AMD_shader_trinary_minmax",
- reinterpret_cast<const char*>(
- &(inst.GetInOperand(0).words[0]))) == 0) {
+ if (ext_to_remove.count(reinterpret_cast<const char*>(
+ &(inst.GetInOperand(0).words[0]))) != 0) {
to_be_killed.push_back(&inst);
}
}
@@ -625,14 +919,8 @@
for (Instruction& inst : context()->ext_inst_imports()) {
if (inst.opcode() == SpvOpExtInstImport) {
- if (strcmp("SPV_AMD_shader_ballot",
- reinterpret_cast<const char*>(
- &(inst.GetInOperand(0).words[0]))) == 0) {
- to_be_killed.push_back(&inst);
- }
- if (strcmp("SPV_AMD_shader_trinary_minmax",
- reinterpret_cast<const char*>(
- &(inst.GetInOperand(0).words[0]))) == 0) {
+ if (ext_to_remove.count(reinterpret_cast<const char*>(
+ &(inst.GetInOperand(0).words[0]))) != 0) {
to_be_killed.push_back(&inst);
}
}
diff --git a/source/opt/constants.cpp b/source/opt/constants.cpp
index 5c1468b..0887ec2 100644
--- a/source/opt/constants.cpp
+++ b/source/opt/constants.cpp
@@ -389,6 +389,13 @@
return cst ? RegisterConstant(std::move(cst)) : nullptr;
}
+uint32_t ConstantManager::GetFloatConst(float val) {
+ Type* float_type = context()->get_type_mgr()->GetFloatType();
+ utils::FloatProxy<float> v(val);
+ const Constant* c = GetConstant(float_type, v.GetWords());
+ return GetDefiningInstruction(c)->result_id();
+}
+
std::vector<const analysis::Constant*> Constant::GetVectorComponents(
analysis::ConstantManager* const_mgr) const {
std::vector<const analysis::Constant*> components;
diff --git a/source/opt/constants.h b/source/opt/constants.h
index 93d0847..5f2fdc7 100644
--- a/source/opt/constants.h
+++ b/source/opt/constants.h
@@ -626,6 +626,9 @@
}
}
+ // Returns the id of a 32-bit floating point constant with value |val|.
+ uint32_t GetFloatConst(float val);
+
private:
// Creates a Constant instance with the given type and a vector of constant
// defining words. Returns a unique pointer to the created Constant instance
diff --git a/test/opt/amd_ext_to_khr.cpp b/test/opt/amd_ext_to_khr.cpp
index cdf168a..d943d34 100644
--- a/test/opt/amd_ext_to_khr.cpp
+++ b/test/opt/amd_ext_to_khr.cpp
@@ -702,6 +702,153 @@
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
+TEST_F(AmdExtToKhrTest, ReplaceCubeFaceCoordAMD) {
+ // Sorry for the Check test. The code sequence is so long, I do not think
+ // that a match test would be anymore legible. This tests the replacement of
+ // the CubeFaceCoordAMD instruction.
+ const std::string before = R"(
+ OpCapability Shader
+ OpExtension "SPV_KHR_storage_buffer_storage_class"
+ OpExtension "SPV_AMD_gcn_shader"
+ %1 = OpExtInstImport "SPV_AMD_gcn_shader"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %2 "main"
+ OpExecutionMode %2 LocalSize 1 1 1
+ %void = OpTypeVoid
+ %4 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v2float = OpTypeVector %float 2
+ %v3float = OpTypeVector %float 3
+ %2 = OpFunction %void None %4
+ %8 = OpLabel
+ %9 = OpUndef %v3float
+ %10 = OpExtInst %v2float %1 CubeFaceCoordAMD %9
+ OpReturn
+ OpFunctionEnd
+)";
+
+ const std::string after = R"(OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+%12 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %2 "main"
+OpExecutionMode %2 LocalSize 1 1 1
+%void = OpTypeVoid
+%4 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%v3float = OpTypeVector %float 3
+%bool = OpTypeBool
+%float_0 = OpConstant %float 0
+%float_2 = OpConstant %float 2
+%float_0_5 = OpConstant %float 0.5
+%16 = OpConstantComposite %v2float %float_0_5 %float_0_5
+%2 = OpFunction %void None %4
+%8 = OpLabel
+%9 = OpUndef %v3float
+%17 = OpCompositeExtract %float %9 0
+%18 = OpCompositeExtract %float %9 1
+%19 = OpCompositeExtract %float %9 2
+%20 = OpFNegate %float %17
+%21 = OpFNegate %float %18
+%22 = OpFNegate %float %19
+%23 = OpExtInst %float %12 FAbs %17
+%24 = OpExtInst %float %12 FAbs %18
+%25 = OpExtInst %float %12 FAbs %19
+%26 = OpFOrdLessThan %bool %19 %float_0
+%27 = OpFOrdLessThan %bool %18 %float_0
+%28 = OpFOrdLessThan %bool %17 %float_0
+%29 = OpExtInst %float %12 FMax %23 %24
+%30 = OpExtInst %float %12 FMax %25 %29
+%31 = OpFMul %float %float_2 %30
+%32 = OpFOrdGreaterThanEqual %bool %25 %29
+%33 = OpLogicalNot %bool %32
+%34 = OpFOrdGreaterThanEqual %bool %24 %23
+%35 = OpLogicalAnd %bool %33 %34
+%36 = OpSelect %float %26 %20 %17
+%37 = OpSelect %float %28 %19 %22
+%38 = OpSelect %float %35 %17 %37
+%39 = OpSelect %float %32 %36 %38
+%40 = OpSelect %float %27 %22 %19
+%41 = OpSelect %float %35 %40 %21
+%42 = OpCompositeConstruct %v2float %39 %41
+%43 = OpCompositeConstruct %v2float %31 %31
+%44 = OpFDiv %v2float %42 %43
+%10 = OpFAdd %v2float %44 %16
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<AmdExtensionToKhrPass>(before, after, true);
+}
+
+TEST_F(AmdExtToKhrTest, ReplaceCubeFaceIndexAMD) {
+ // Sorry for the Check test. The code sequence is so long, I do not think
+ // that a match test would be anymore legible. This tests the replacement of
+ // the CubeFaceIndexAMD instruction.
+ const std::string before = R"(OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpExtension "SPV_AMD_gcn_shader"
+%1 = OpExtInstImport "SPV_AMD_gcn_shader"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %2 "main"
+OpExecutionMode %2 LocalSize 1 1 1
+%void = OpTypeVoid
+%4 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v3float = OpTypeVector %float 3
+%2 = OpFunction %void None %4
+%7 = OpLabel
+%8 = OpUndef %v3float
+%9 = OpExtInst %float %1 CubeFaceIndexAMD %8
+OpReturn
+OpFunctionEnd
+)";
+
+ const std::string after = R"(OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+%11 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %2 "main"
+OpExecutionMode %2 LocalSize 1 1 1
+%void = OpTypeVoid
+%4 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v3float = OpTypeVector %float 3
+%bool = OpTypeBool
+%float_0 = OpConstant %float 0
+%float_1 = OpConstant %float 1
+%float_2 = OpConstant %float 2
+%float_3 = OpConstant %float 3
+%float_4 = OpConstant %float 4
+%float_5 = OpConstant %float 5
+%2 = OpFunction %void None %4
+%7 = OpLabel
+%8 = OpUndef %v3float
+%18 = OpCompositeExtract %float %8 0
+%19 = OpCompositeExtract %float %8 1
+%20 = OpCompositeExtract %float %8 2
+%21 = OpExtInst %float %11 FAbs %18
+%22 = OpExtInst %float %11 FAbs %19
+%23 = OpExtInst %float %11 FAbs %20
+%24 = OpFOrdLessThan %bool %20 %float_0
+%25 = OpFOrdLessThan %bool %19 %float_0
+%26 = OpFOrdLessThan %bool %18 %float_0
+%27 = OpExtInst %float %11 FMax %21 %22
+%28 = OpFOrdGreaterThanEqual %bool %23 %27
+%29 = OpFOrdGreaterThanEqual %bool %22 %21
+%30 = OpSelect %float %24 %float_5 %float_4
+%31 = OpSelect %float %25 %float_3 %float_2
+%32 = OpSelect %float %26 %float_1 %float_0
+%33 = OpSelect %float %29 %31 %32
+%9 = OpSelect %float %28 %30 %33
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<AmdExtensionToKhrPass>(before, after, true);
+}
+
TEST_F(AmdExtToKhrTest, SetVersion) {
const std::string text = R"(
OpCapability Shader