[EmitC] Cache values instead of operations (#18507)

This changes the lookup table for local `iree_vm_ref_t*` variables to
work directly on the value instead of the operation defining it.

Signed-off-by: Simon Camphausen <simon.camphausen@iml.fraunhofer.de>
diff --git a/compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/ConvertVMToEmitC.cpp b/compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/ConvertVMToEmitC.cpp
index c57802d..24fb391 100644
--- a/compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/ConvertVMToEmitC.cpp
+++ b/compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/ConvertVMToEmitC.cpp
@@ -125,12 +125,10 @@
     auto [ref, refPtr] = emitc_builders::allocZeroInitializedVar(
         builder, loc, emitc::OpaqueType::get(ctx, "iree_vm_ref_t"));
 
-    auto refPtrOp = cast<emitc::ApplyOp>(refPtr.getDefiningOp());
-
     // Cache local refs so that we can release them before a return operation.
     // Here we rely on the fact that the register allocation maps arguments in
     // the first slots.
-    funcAnalysis.cacheLocalRef(i + numRefArgs, refPtrOp);
+    funcAnalysis.cacheLocalRef(i + numRefArgs, refPtr);
   }
 
   for (Block &block : llvm::drop_begin(newFuncOp.getBlocks(), 1)) {
@@ -332,13 +330,7 @@
 
   if (funcAnalysis.hasLocalRefs()) {
 
-    for (auto pair : funcAnalysis.localRefs()) {
-      Operation *op = pair.second;
-
-      assert(isa<emitc::ApplyOp>(op));
-
-      Value localRef = cast<emitc::ApplyOp>(op).getResult();
-
+    for (auto &[key, localRef] : funcAnalysis.localRefs()) {
       emitc_builders::ireeVmRefRelease(builder, location, localRef);
     }
   }
diff --git a/compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/VMAnalysis.h b/compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/VMAnalysis.h
index f5ebb30..cbfcc92 100644
--- a/compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/VMAnalysis.h
+++ b/compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/VMAnalysis.h
@@ -33,7 +33,7 @@
     valueLiveness = ValueLiveness(op);
     originalFunctionType = funcOp.getFunctionType();
     callingConvention = makeCallingConventionString(funcOp).value();
-    refs = DenseMap<int64_t, Operation *>{};
+    refs = DenseMap<int64_t, Value>{};
   }
   FuncAnalysis(mlir::emitc::FuncOp funcOp) {
     originalFunctionType = funcOp.getFunctionType();
@@ -101,22 +101,21 @@
     return lastUse && false;
   }
 
-  void cacheLocalRef(int64_t ordinal, emitc::ApplyOp applyOp) {
+  void cacheLocalRef(int64_t ordinal, Value ref) {
     assert(refs.has_value());
     assert(!refs.value().count(ordinal) && "ref was already cached");
-    refs.value()[ordinal] = applyOp.getOperation();
+    refs.value()[ordinal] = ref;
   }
 
-  emitc::ApplyOp lookupLocalRef(int64_t ordinal) {
+  Value lookupLocalRef(int64_t ordinal) {
     assert(refs.has_value());
     assert(refs.value().count(ordinal) && "ref not found in cache");
-    Operation *op = refs.value()[ordinal];
-    return cast<emitc::ApplyOp>(op);
+    return refs.value()[ordinal];
   }
 
   bool hasLocalRefs() { return refs.has_value(); }
 
-  DenseMap<int64_t, Operation *> &localRefs() {
+  DenseMap<int64_t, Value> &localRefs() {
     assert(refs.has_value());
     return refs.value();
   }
@@ -124,7 +123,7 @@
 private:
   std::optional<RegisterAllocation> registerAllocation;
   std::optional<ValueLiveness> valueLiveness;
-  std::optional<DenseMap<int64_t, Operation *>> refs;
+  std::optional<DenseMap<int64_t, Value>> refs;
   std::optional<FunctionType> originalFunctionType;
   std::optional<std::string> callingConvention;
   std::optional<std::string> exportName;
@@ -233,8 +232,7 @@
       }
     }
 
-    emitc::ApplyOp applyOp = analysis.lookupLocalRef(ordinal);
-    return applyOp.getResult();
+    return analysis.lookupLocalRef(ordinal);
   }
 
   std::vector<TypeDef> typeTable;