Handle TimeAMD in AmdExtensionToKhrPass. (#3168)

This adds support for replacing TimeAMD with OpReadClockKHR.  The scope
for OpReadClockKHR is fixed to be a subgroup because TimeAMD operates
only on subgroup.
diff --git a/source/opt/amd_ext_to_khr.cpp b/source/opt/amd_ext_to_khr.cpp
index e9b7f86..ccedc0b 100644
--- a/source/opt/amd_ext_to_khr.cpp
+++ b/source/opt/amd_ext_to_khr.cpp
@@ -53,12 +53,6 @@
   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
@@ -686,13 +680,13 @@
   return true;
 }
 
-// A folding rule that will replace the CubeFaceCoordAMD extended
+// A folding rule that will replace the CubeFaceIndexAMD extended
 // instruction in the SPV_AMD_gcn_shader_ballot.  Returns true if the folding
 // is successful.
 //
 // The instruction
 //
-//  %result = OpExtInst %v2float %1 CubeFaceCoordAMD %input
+//  %result = OpExtInst %float %1 CubeFaceIndexAMD %input
 //
 // with
 //
@@ -705,7 +699,7 @@
 //      %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
+//      %amax_x_y = OpExtInst %float %n_1 FMax %ax %ay
 //      %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
@@ -800,6 +794,37 @@
   return true;
 }
 
+// A folding rule that will replace the TimeAMD extended instruction in the
+// SPV_AMD_gcn_shader_ballot.  It returns true if the folding is successful.
+// It returns False, otherwise.
+//
+// The instruction
+//
+//  %result = OpExtInst %uint64 %1 TimeAMD
+//
+// with
+//
+//  %result = OpReadClockKHR %uint64 %uint_3
+//
+// NOTE: TimeAMD uses subgroup scope (it is not a real time clock).
+bool ReplaceTimeAMD(IRContext* ctx, Instruction* inst,
+                    const std::vector<const analysis::Constant*>&) {
+  InstructionBuilder ir_builder(
+      ctx, inst,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  ctx->AddExtension("SPV_KHR_shader_clock");
+  ctx->AddCapability(SpvCapabilityShaderClockKHR);
+
+  inst->SetOpcode(SpvOpReadClockKHR);
+  Instruction::OperandList args;
+  uint32_t subgroup_scope_id = ir_builder.GetUintConstantId(SpvScopeSubgroup);
+  args.push_back({SPV_OPERAND_TYPE_ID, {subgroup_scope_id}});
+  inst->SetInOperands(std::move(args));
+  ctx->UpdateDefUse(inst);
+
+  return true;
+}
+
 class AmdExtFoldingRules : public FoldingRules {
  public:
   explicit AmdExtFoldingRules(IRContext* ctx) : FoldingRules(ctx) {}
@@ -869,7 +894,7 @@
           ReplaceCubeFaceCoord);
       ext_rules_[{extension_id, CubeFaceIndexAMD}].push_back(
           ReplaceCubeFaceIndex);
-      ext_rules_[{extension_id, TimeAMD}].push_back(NotImplementedYet);
+      ext_rules_[{extension_id, TimeAMD}].push_back(ReplaceTimeAMD);
     }
   }
 };
diff --git a/test/opt/amd_ext_to_khr.cpp b/test/opt/amd_ext_to_khr.cpp
index d943d34..3340e89 100644
--- a/test/opt/amd_ext_to_khr.cpp
+++ b/test/opt/amd_ext_to_khr.cpp
@@ -15,7 +15,6 @@
 #include <vector>
 
 #include "gmock/gmock.h"
-
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 
@@ -913,6 +912,42 @@
   EXPECT_THAT(output, HasSubstr("Version: 1.4"));
 }
 
+TEST_F(AmdExtToKhrTest, TimeAMD) {
+  const std::string text = R"(
+               OpCapability Shader
+               OpCapability Int64
+               OpExtension "SPV_AMD_gcn_shader"
+; CHECK-NOT: OpExtension "SPV_AMD_gcn_shader"
+; CHECK: OpExtension "SPV_KHR_shader_clock"
+          %1 = OpExtInstImport "GLSL.std.450"
+          %2 = OpExtInstImport "SPV_AMD_gcn_shader"
+; CHECK-NOT: OpExtInstImport "SPV_AMD_gcn_shader"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpSource GLSL 450
+               OpSourceExtension "GL_AMD_gcn_shader"
+               OpSourceExtension "GL_ARB_gpu_shader_int64"
+               OpName %main "main"
+               OpName %time "time"
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+      %ulong = OpTypeInt 64 0
+%_ptr_Function_ulong = OpTypePointer Function %ulong
+       %main = OpFunction %void None %6
+          %9 = OpLabel
+       %time = OpVariable %_ptr_Function_ulong Function
+; CHECK: [[uint:%\w+]] = OpTypeInt 32 0
+; CHECK: [[uint_3:%\w+]] = OpConstant [[uint]] 3
+         %10 = OpExtInst %ulong %2 TimeAMD
+; CHECK: %10 = OpReadClockKHR %ulong [[uint_3]]
+               OpStore %time %10
+               OpReturn
+               OpFunctionEnd
+)";
+
+  SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
+}
 }  // namespace
 }  // namespace opt
 }  // namespace spvtools