[ODG] Use getODSOperands() and getODSResults() to back accessors
authorLei Zhang <antiagainst@google.com>
Sun, 9 Jun 2019 12:50:09 +0000 (05:50 -0700)
committerMehdi Amini <joker.eph@gmail.com>
Sun, 9 Jun 2019 23:24:18 +0000 (16:24 -0700)
This CL added getODSOperands() and getODSResults() as fallback getter methods for
getting all the dynamic values corresponding to a static operand/result (which
can be variadic). It should provide a uniform way of calculating the value ranges.
All named getter methods are layered on top of these methods now.

PiperOrigin-RevId: 252284270

mlir/test/mlir-tblgen/op-decl.td
mlir/test/mlir-tblgen/op-operand.td
mlir/test/mlir-tblgen/op-result.td
mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp

index 336efc1..7136aaf 100644 (file)
@@ -42,6 +42,7 @@ def NS_AOp : NS_Op<"a_op", [NoSideEffect]> {
 // CHECK:  class AOpOperandAdaptor {
 // CHECK:  public:
 // CHECK:    AOpOperandAdaptor(ArrayRef<Value *> values);
+// CHECK:    ArrayRef<Value *> getODSOperands(unsigned index);
 // CHECK:    Value *a();
 // CHECK:    ArrayRef<Value *> b();
 // CHECK:  private:
@@ -53,8 +54,10 @@ def NS_AOp : NS_Op<"a_op", [NoSideEffect]> {
 // CHECK:   using Op::Op;
 // CHECK:   using OperandAdaptor = AOpOperandAdaptor;
 // CHECK:   static StringRef getOperationName();
+// CHECK:   Operation::operand_range getODSOperands(unsigned index);
 // CHECK:   Value *a();
 // CHECK:   Operation::operand_range b();
+// CHECK:   Operation::result_range getODSResults(unsigned index);
 // CHECK:   Value *r();
 // CHECK:   APInt attr1();
 // CHECK:   Optional< APFloat > attr2();
index 506c018..6055081 100644 (file)
@@ -17,9 +17,6 @@ def OpA : NS_Op<"one_normal_operand_op", []> {
 // CHECK:      OpAOperandAdaptor::OpAOperandAdaptor
 // CHECK-NEXT: tblgen_operands = values
 
-// CHECK:      OpAOperandAdaptor::input
-// CHECK-NEXT: return tblgen_operands[0]
-
 // CHECK:      void OpA::build
 // CHECK-SAME:   Value *input
 // CHECK:        tblgen_state->operands.push_back(input);
@@ -42,103 +39,40 @@ def OpB : NS_Op<"one_variadic_operand_op", []> {
 // CHECK-NOT:     assert
 // CHECK:         tblgen_state->addOperands(input);
 
-def OpC : NS_Op<"all_variadic_inputs_op", [SameVariadicOperandSize]> {
-  let arguments = (ins Variadic<AnyTensor>:$input1, Variadic<AnyTensor>:$input2);
+def OpD : NS_Op<"mix_variadic_and_normal_inputs_op", [SameVariadicOperandSize]> {
+  let arguments = (ins Variadic<AnyTensor>:$input1, AnyTensor:$input2, Variadic<AnyTensor>:$input3);
 }
 
-// CHECK-LABEL: OpCOperandAdaptor::input1
-// CHECK-NEXT:    variadicOperandSize = (tblgen_operands.size() - 0) / 2;
-// CHECK-NEXT:    offset = 0 + variadicOperandSize * 0;
-// CHECK-NEXT:    return {std::next(tblgen_operands.begin(), offset), std::next(tblgen_operands.begin(), offset + variadicOperandSize)};
+// CHECK-LABEL: ArrayRef<Value *> OpDOperandAdaptor::input1
+// CHECK-NEXT:    return getODSOperands(0);
 
-// CHECK-LABEL: OpCOperandAdaptor::input2
-// CHECK-NEXT:    variadicOperandSize = (tblgen_operands.size() - 0) / 2;
-// CHECK-NEXT:    offset = 0 + variadicOperandSize * 1;
-// CHECK-NEXT:    return {std::next(tblgen_operands.begin(), offset), std::next(tblgen_operands.begin(), offset + variadicOperandSize)};
+// CHECK-LABEL: Value *OpDOperandAdaptor::input2
+// CHECK-NEXT:    return *getODSOperands(1).begin();
 
-// CHECK-LABEL: Operation::operand_range OpC::input1()
-// CHECK-NEXT:    variadicOperandSize = (this->getNumOperands() - 0) / 2;
-// CHECK-NEXT:    offset = 0 + variadicOperandSize * 0;
-// CHECK-NEXT:    return {std::next(operand_begin(), offset), std::next(operand_begin(), offset + variadicOperandSize)};
+// CHECK-LABEL: ArrayRef<Value *> OpDOperandAdaptor::input3
+// CHECK-NEXT:    return getODSOperands(2);
 
-// CHECK-LABEL: Operation::operand_range OpC::input2()
-// CHECK-NEXT:    variadicOperandSize = (this->getNumOperands() - 0) / 2;
-// CHECK-NEXT:    offset = 0 + variadicOperandSize * 1;
-// CHECK-NEXT:    return {std::next(operand_begin(), offset), std::next(operand_begin(), offset + variadicOperandSize)};
+// TODO(b/134305899): Move to use TestDialect after fixing verification.
 
-// CHECK-LABEL: OpC::build
-// CHECK-NEXT: tblgen_state->addOperands(input1);
-// CHECK-NEXT: tblgen_state->addOperands(input2);
+// CHECK-LABEL: Operation::operand_range OpD::getODSOperands(unsigned index)
+// CHECK-NEXT:    bool isVariadic[] = {true, false, true};
+// CHECK-NEXT:    int prevVariadicCount = 0;
+// CHECK-NEXT:    for (int i = 0; i < index; ++i)
+// CHECK-NEXT:      if (isVariadic[i]) ++prevVariadicCount;
 
-def OpD : NS_Op<"mix_variadic_and_normal_inputs_op", [SameVariadicOperandSize]> {
-  let arguments = (ins Variadic<AnyTensor>:$input1, AnyTensor:$input2, Variadic<AnyTensor>:$input3);
-}
-
-// CHECK-LABEL: OpDOperandAdaptor::input1() {
-// CHECK-NEXT:    variadicOperandSize = (tblgen_operands.size() - 1) / 2;
-// CHECK-NEXT:    offset = 0 + variadicOperandSize * 0;
-// CHECK-NEXT:    return {std::next(tblgen_operands.begin(), offset), std::next(tblgen_operands.begin(), offset + variadicOperandSize)};
-
-// CHECK-LABEL: Value *OpDOperandAdaptor::input2() {
-// CHECK-NEXT:    variadicOperandSize = (tblgen_operands.size() - 1) / 2;
-// CHECK-NEXT:    offset = 0 + variadicOperandSize * 1;
-// CHECK-NEXT:    return tblgen_operands[offset];
+// CHECK:         int variadicSize = (getOperation()->getNumOperands() - 1) / 2;
+// CHECK:         int offset = index + (variadicSize - 1) * prevVariadicCount;
+// CHECK-NEXT:    int size = isVariadic[index] ? variadicSize : 1;
 
-// CHECK-LABEL: OpDOperandAdaptor::input3() {
-// CHECK-NEXT:    variadicOperandSize = (tblgen_operands.size() - 1) / 2;
-// CHECK-NEXT:    offset = 1 + variadicOperandSize * 1;
-// CHECK-NEXT:    return {std::next(tblgen_operands.begin(), offset), std::next(tblgen_operands.begin(), offset + variadicOperandSize)};
+// CHECK:         return {std::next(getOperation()->operand_begin(), offset), std::next(getOperation()->operand_begin(), offset + size)};
 
-// CHECK-LABEL: Operation::operand_range OpD::input1()
-// CHECK-NEXT:    variadicOperandSize = (this->getNumOperands() - 1) / 2;
-// CHECK-NEXT:    offset = 0 + variadicOperandSize * 0;
-// CHECK-NEXT:    return {std::next(operand_begin(), offset), std::next(operand_begin(), offset + variadicOperandSize)};
+// CHECK-LABEL: Operation::operand_range OpD::input1
+// CHECK-NEXT: return getODSOperands(0);
 
-// CHECK-LABEL: Value *OpD::input2()
-// CHECK-NEXT:    variadicOperandSize = (this->getNumOperands() - 1) / 2;
-// CHECK-NEXT:    offset = 0 + variadicOperandSize * 1;
-// CHECK-NEXT:    return this->getOperation()->getOperand(offset);
-
-// CHECK-LABEL: Operation::operand_range OpD::input3()
-// CHECK-NEXT:    variadicOperandSize = (this->getNumOperands() - 1) / 2;
-// CHECK-NEXT:    offset = 1 + variadicOperandSize * 1;
-// CHECK-NEXT:    return {std::next(operand_begin(), offset), std::next(operand_begin(), offset + variadicOperandSize)};
+// CHECK-LABEL: Value *OpD::input2
+// CHECK-NEXT: return *getODSOperands(1).begin();
 
 // CHECK-LABEL: OpD::build
 // CHECK-NEXT: tblgen_state->addOperands(input1);
 // CHECK-NEXT: tblgen_state->operands.push_back(input2);
 // CHECK-NEXT: tblgen_state->addOperands(input3);
-
-def OpE : NS_Op<"one_variadic_among_multi_normal_inputs_op", []> {
-  let arguments = (ins AnyTensor:$input1, AnyTensor:$input2, Variadic<AnyTensor>:$input3, AnyTensor:$input4, AnyTensor:$input5);
-}
-
-// CHECK-LABEL:  Value *OpEOperandAdaptor::input1() {
-// CHECK-NEXT:    return tblgen_operands[0];
-
-// CHECK-LABEL:  Value *OpEOperandAdaptor::input2() {
-// CHECK-NEXT:    return tblgen_operands[1];
-
-// CHECK-LABEL:  OpEOperandAdaptor::input3() {
-// CHECK-NEXT:    return {std::next(tblgen_operands.begin(), 2), std::next(tblgen_operands.begin(), 2 + tblgen_operands.size() - 4)};
-
-// CHECK-LABEL:  Value *OpEOperandAdaptor::input4() {
-// CHECK-NEXT:    return tblgen_operands[tblgen_operands.size() - 2];
-
-// CHECK-LABEL:  Value *OpEOperandAdaptor::input5() {
-// CHECK-NEXT:    return tblgen_operands[tblgen_operands.size() - 1];
-
-// CHECK-LABEL: Value *OpE::input1()
-// CHECK-NEXT:    return this->getOperation()->getOperand(0);
-
-// CHECK-LABEL: Value *OpE::input2()
-// CHECK-NEXT:    return this->getOperation()->getOperand(1);
-
-// CHECK-LABEL: Operation::operand_range OpE::input3()
-// CHECK-NEXT:    return {std::next(operand_begin(), 2), std::next(operand_begin(), 2 + this->getNumOperands() - 4)};
-
-// CHECK-LABEL: Value *OpE::input4()
-// CHECK-NEXT:    return this->getOperation()->getOperand(this->getNumOperands() - 2);
-
-// CHECK-LABEL: Value *OpE::input5()
-// CHECK-NEXT:    return this->getOperation()->getOperand(this->getNumOperands() - 1);
index bc356b8..e0f14e4 100644 (file)
@@ -12,9 +12,6 @@ def OpA : NS_Op<"one_normal_result_op", []> {
   let results = (outs I32:$result);
 }
 
-// CHECK-LABEL: Value *OpA::result()
-// CHECK-NEXT:    return this->getOperation()->getResult(0)
-
 // CHECK-LABEL: void OpA::build
 // CHECK:         ArrayRef<Type> resultTypes, ArrayRef<Value *> operands
 // CHECK:         assert(resultTypes.size() == 1u && "mismatched number of return types");
@@ -68,9 +65,6 @@ def OpF : NS_Op<"one_variadic_result_op", []> {
   let results = (outs Variadic<I32>:$x);
 }
 
-// CHECK-LABEL: Operation::result_range OpF::x()
-// CHECK-NEXT:    return {std::next(result_begin(), 0), std::next(result_begin(), 0 + this->getNumResults() - 0)};
-
 // CHECK-LABEL: void OpF::build
 // CHECK-SAME:    ArrayRef<Type> x
 // CHECK-NOT:     assert
@@ -87,74 +81,40 @@ def OpG : NS_Op<"one_normal_and_one_variadic_result_op", []> {
 // CHECK-NEXT:   tblgen_state->types.push_back(x);
 // CHECK-NEXT:   tblgen_state->addTypes(y);
 
-// CHECK:                      void OpG::build
+// CHECK:       void OpG::build
 // CHECK:         ArrayRef<Type> resultTypes
 // CHECK:         assert(resultTypes.size() >= 1u && "mismatched number of return types");
 // CHECK-NEXT:    tblgen_state->addTypes(resultTypes);
 
-
-def OpH : NS_Op<"all_variadic_results_op", [SameVariadicResultSize]> {
-  let results = (outs Variadic<AnyTensor>:$output1, Variadic<AnyTensor>:$output2);
+def OpI : NS_Op<"mix_variadic_and_normal_results_op", [SameVariadicResultSize]> {
+  let results = (outs Variadic<AnyTensor>:$output1, AnyTensor:$output2, Variadic<AnyTensor>:$output3);
 }
 
-// CHECK-LABEL: Operation::result_range OpH::output1()
-// CHECK-NEXT:      variadicResultSize = (this->getNumResults() - 0) / 2;
-// CHECK-NEXT:      offset = 0 + variadicResultSize * 0;
-// CHECK-NEXT:      return {std::next(result_begin(), offset), std::next(result_begin(), offset + variadicResultSize)};
+// TODO(b/134305899): Move to use TestDialect after fixing verification.
 
-// CHECK-LABEL: Operation::result_range OpH::output2()
-// CHECK-NEXT:      variadicResultSize = (this->getNumResults() - 0) / 2;
-// CHECK-NEXT:      offset = 0 + variadicResultSize * 1;
-// CHECK-NEXT:      return {std::next(result_begin(), offset), std::next(result_begin(), offset + variadicResultSize)};
+// CHECK-LABEL: Operation::result_range OpI::getODSResults(unsigned index)
+// CHECK-NEXT:   bool isVariadic[] = {true, false, true};
+// CHECK-NEXT:   int prevVariadicCount = 0;
+// CHECK-NEXT:   for (int i = 0; i < index; ++i)
+// CHECK-NEXT:     if (isVariadic[i]) ++prevVariadicCount;
 
+// CHECK:        int variadicSize = (getOperation()->getNumResults() - 1) / 2;
+// CHECK:        int offset = index + (variadicSize - 1) * prevVariadicCount;
+// CHECK-NEXT:   int size = isVariadic[index] ? variadicSize : 1;
 
-// CHECK-LABEL: OpH::build
-// CHECK-NEXT:    tblgen_state->addTypes(output1);
-// CHECK-NEXT:    tblgen_state->addTypes(output2);
-
-def OpI : NS_Op<"mix_variadic_and_normal_results_op", [SameVariadicResultSize]> {
-  let results = (outs Variadic<AnyTensor>:$output1, AnyTensor:$output2, Variadic<AnyTensor>:$output3);
-}
-
-// CHECK-LABEL: Operation::result_range OpI::output1()
-// CHECK-NEXT:    variadicResultSize = (this->getNumResults() - 1) / 2;
-// CHECK-NEXT:    offset = 0 + variadicResultSize * 0;
-// CHECK-NEXT:    return {std::next(result_begin(), offset), std::next(result_begin(), offset + variadicResultSize)};
+// CHECK:        return {std::next(getOperation()->result_begin(), offset), std::next(getOperation()->result_begin(), offset + size)};
 
-// CHECK-LABEL: Value *OpI::output2()
-// CHECK-NEXT:    variadicResultSize = (this->getNumResults() - 1) / 2;
-// CHECK-NEXT:    offset = 0 + variadicResultSize * 1;
-// CHECK-NEXT:    return this->getResult(offset);
+// CHECK-LABEL: Operation::result_range OpI::output1
+// CHECK-NEXT:    return getODSResults(0);
 
-// CHECK-LABEL: Operation::result_range OpI::output3()
-// CHECK-NEXT:    variadicResultSize = (this->getNumResults() - 1) / 2;
-// CHECK-NEXT:    offset = 1 + variadicResultSize * 1;
-// CHECK-NEXT:    return {std::next(result_begin(), offset), std::next(result_begin(), offset + variadicResultSize)};
+// CHECK-LABEL: Value *OpI::output2
+// CHECK-NEXT:    return *getODSResults(1).begin();
 
 // CHECK-LABEL: OpI::build
 // CHECK-NEXT:    tblgen_state->addTypes(output1);
 // CHECK-NEXT:    tblgen_state->types.push_back(output2);
 // CHECK-NEXT:    tblgen_state->addTypes(output3);
 
-def OpJ : NS_Op<"one_variadic_among_multi_normal_results_op", []> {
-  let results = (outs AnyTensor:$output1, AnyTensor:$output2, Variadic<AnyTensor>:$output3, AnyTensor:$output4, AnyTensor:$output5);
-}
-
-// CHECK-LABEL: Value *OpJ::output1()
-// CHECK-NEXT:    return this->getOperation()->getResult(0);
-
-// CHECK-LABEL: Value *OpJ::output2()
-// CHECK-NEXT:    return this->getOperation()->getResult(1);
-
-// CHECK-LABEL: Operation::result_range OpJ::output3()
-// CHECK-NEXT:    return {std::next(result_begin(), 2), std::next(result_begin(), 2 + this->getNumResults() - 4)};
-
-// CHECK-LABEL: Value *OpJ::output4()
-// CHECK-NEXT:    return this->getOperation()->getResult(this->getNumResults() - 2);
-
-// CHECK-LABEL: Value *OpJ::output5()
-// CHECK-NEXT:    return this->getOperation()->getResult(this->getNumResults() - 1);
-
 // Test that if the only operand is variadic, we acess the first value in the
 // pack to set result type
 // ---
index 9d4ab12..7183f34 100644 (file)
@@ -39,6 +39,36 @@ static const char *const tblgenNamePrefix = "tblgen_";
 static const char *const generatedArgName = "tblgen_arg";
 static const char *const builderOpState = "tblgen_state";
 
+// The logic to calculate the dynamic value range for an static operand/result
+// of an op with variadic operands/results. Note that this logic is not for
+// general use; it assumes all variadic operands/results must have the same
+// number of values.
+//
+// {0}: The list of whether each static operand/result is variadic.
+// {1}: The total number of non-variadic operands/results.
+// {2}: The total number of variadic operands/results.
+// {3}: The total number of dynamic values.
+// {4}: The begin iterator of the dynamic values.
+// {5}: "operand" or "result"
+const char *valueRangeCalcCode = R"(
+  bool isVariadic[] = {{{0}};
+  int prevVariadicCount = 0;
+  for (int i = 0; i < index; ++i)
+    if (isVariadic[i]) ++prevVariadicCount;
+
+  // Calculate how many dynamic values a static variadic {5} corresponds to.
+  // This assumes all static variadic {5}s have the same dynamic value count.
+  int variadicSize = ({3} - {1}) / {2};
+  // `index` passed in as the parameter is the static index which counts each
+  // {5} (variadic or not) as size 1. So here for each previous static variadic
+  // {5}, we need to offset by (variadicSize - 1) to get where the dynamic
+  // value pack for this static {5} starts.
+  int offset = index + (variadicSize - 1) * prevVariadicCount;
+  int size = isVariadic[index] ? variadicSize : 1;
+
+  return {{std::next({4}, offset), std::next({4}, offset + size)};
+)";
+
 static const char *const opCommentHeader = R"(
 //===----------------------------------------------------------------------===//
 // {0} {1}
@@ -548,76 +578,49 @@ static void generateNamedOperandGetters(const Operator &op, Class &opClass,
   const int numVariadicOperands = op.getNumVariadicOperands();
   const int numNormalOperands = numOperands - numVariadicOperands;
 
-  // Special case for ops without variadic operands: the i-th value is for the
-  // i-th operand defined in the op.
-  // Special case for ops with one variadic operand: the variadic operand can
-  // appear at any place, so the i-th value may not necessarily belong to the
-  // i-th operand definition. we need to calculate the index (range) for each
-  // operand.
-  if (numVariadicOperands <= 1) {
-    bool emittedVariadicOperand = false;
-    for (int i = 0; i != numOperands; ++i) {
-      const auto &operand = op.getOperand(i);
-      if (operand.name.empty())
-        continue;
-
-      if (operand.isVariadic()) {
-        auto &m = opClass.newMethod(rangeType, operand.name);
-        m.body() << formatv(
-            "  return {{std::next({2}, {0}), std::next({2}, {0} + {3} - {1})};",
-            i, numNormalOperands, rangeBeginCall, rangeSizeCall);
-        emittedVariadicOperand = true;
-      } else {
-        auto &m = opClass.newMethod("Value *", operand.name);
+  if (numVariadicOperands > 1 && !op.hasTrait("SameVariadicOperandSize")) {
+    PrintFatalError(op.getLoc(), "op has multiple variadic operands but no "
+                                 "specification over their sizes");
+  }
 
-        auto operandIndex =
-            emittedVariadicOperand
-                ? formatv("{0} - {1}", rangeSizeCall, numOperands - i).str()
-                : std::to_string(i);
+  // First emit a "sink" getter method upon which we layer all nicer named
+  // getter methods.
+  auto &m = opClass.newMethod(rangeType, "getODSOperands", "unsigned index");
 
-        m.body() << "  return "
-                 << formatv(getOperandCallPattern.data(), operandIndex)
-                 << ";\n";
-      }
+  if (numVariadicOperands == 0) {
+    // We still need to match the return type, which is a range.
+    m.body() << "return {std::next(" << rangeBeginCall << ", index), std::next("
+             << rangeBeginCall << ", index + 1)};";
+  } else {
+    // Because the op can have arbitrarily interleaved variadic and non-variadic
+    // operands, we need to embed a list in the "sink" getter method for
+    // calculation at run-time.
+    llvm::SmallVector<StringRef, 4> isVariadic;
+    isVariadic.reserve(numOperands);
+    for (int i = 0; i < numOperands; ++i) {
+      isVariadic.push_back(llvm::toStringRef(op.getOperand(i).isVariadic()));
     }
-    return;
-  }
-
-  // If we have more than one variadic operands, we need more complicated logic
-  // to calculate the value range for each operand.
+    std::string isVariadicList = llvm::join(isVariadic, ", ");
 
-  if (!op.hasTrait("SameVariadicOperandSize")) {
-    PrintFatalError(op.getLoc(), "op has multiple variadic operands but no "
-                                 "specification over their sizes");
+    m.body() << formatv(valueRangeCalcCode, isVariadicList, numNormalOperands,
+                        numVariadicOperands, rangeSizeCall, rangeBeginCall,
+                        "operand");
   }
 
-  int emittedNormalOperands = 0;
-  int emittedVariadicOperands = 0;
+  // Then we emit nicer named getter methods by redirecting to the "sink" getter
+  // method.
 
   for (int i = 0; i != numOperands; ++i) {
     const auto &operand = op.getOperand(i);
     if (operand.name.empty())
       continue;
 
-    const char *code = R"(
-  int variadicOperandSize = ({4} - {0}) / {1};
-  int offset = {2} + variadicOperandSize * {3};
-  return )";
-    auto sizeAndOffset =
-        formatv(code, numNormalOperands, numVariadicOperands,
-                emittedNormalOperands, emittedVariadicOperands, rangeSizeCall);
-
     if (operand.isVariadic()) {
       auto &m = opClass.newMethod(rangeType, operand.name);
-      m.body() << sizeAndOffset << "{std::next(" << rangeBeginCall
-               << ", offset), std::next(" << rangeBeginCall
-               << ", offset + variadicOperandSize)};";
-      ++emittedVariadicOperands;
+      m.body() << "return getODSOperands(" << i << ");";
     } else {
       auto &m = opClass.newMethod("Value *", operand.name);
-      m.body() << sizeAndOffset
-               << formatv(getOperandCallPattern.data(), "offset") << ";";
-      ++emittedNormalOperands;
+      m.body() << "return *getODSOperands(" << i << ").begin();";
     }
   }
 }
@@ -625,9 +628,9 @@ static void generateNamedOperandGetters(const Operator &op, Class &opClass,
 void OpEmitter::genNamedOperandGetters() {
   generateNamedOperandGetters(
       op, opClass, /*rangeType=*/"Operation::operand_range",
-      /*rangeBeginCall=*/"operand_begin()",
-      /*rangeSizeCall=*/"this->getNumOperands()",
-      /*getOperandCallPattern=*/"this->getOperation()->getOperand({0})");
+      /*rangeBeginCall=*/"getOperation()->operand_begin()",
+      /*rangeSizeCall=*/"getOperation()->getNumOperands()",
+      /*getOperandCallPattern=*/"getOperation()->getOperand({0})");
 }
 
 void OpEmitter::genNamedResultGetters() {
@@ -635,72 +638,44 @@ void OpEmitter::genNamedResultGetters() {
   const int numVariadicResults = op.getNumVariadicResults();
   const int numNormalResults = numResults - numVariadicResults;
 
-  // Special case for ops without variadic results: the i-th value is for the
-  // i-th result defined in the op.
-  // Special case for ops with one variadic result: the variadic result can
-  // appear at any place, so the i-th value may not necessarily belong to the
-  // i-th result definition. we need to calculate the index (range) for each
-  // result.
-  if (numVariadicResults <= 1) {
-    bool emittedVariadicResult = false;
-    for (int i = 0; i != numResults; ++i) {
-      const auto &result = op.getResult(i);
-      if (result.name.empty())
-        continue;
-
-      if (result.isVariadic()) {
-        auto &m = opClass.newMethod("Operation::result_range", result.name);
-        m.body() << formatv(
-            "  return {{std::next(result_begin(), {0}), "
-            "std::next(result_begin(), {0} + this->getNumResults() - {1})};",
-            i, numNormalResults);
-        emittedVariadicResult = true;
-      } else {
-        auto &m = opClass.newMethod("Value *", result.name);
-        m.body() << "  return this->getOperation()->getResult(";
-        if (emittedVariadicResult)
-          m.body() << "this->getNumResults() - " << numResults - i;
-        else
-          m.body() << i;
-        m.body() << ");\n";
-      }
-    }
-    return;
-  }
-
   // If we have more than one variadic results, we need more complicated logic
   // to calculate the value range for each result.
 
-  if (!op.hasTrait("SameVariadicResultSize")) {
+  if (numVariadicResults > 1 && !op.hasTrait("SameVariadicResultSize")) {
     PrintFatalError(op.getLoc(), "op has multiple variadic results but no "
                                  "specification over their sizes");
   }
 
-  int emittedNormalResults = 0;
-  int emittedVariadicResults = 0;
+  auto &m = opClass.newMethod("Operation::result_range", "getODSResults",
+                              "unsigned index");
+
+  if (numVariadicResults == 0) {
+    m.body() << "return {std::next(getOperation()->result_begin(), index), "
+                "std::next(getOperation()->result_begin(), index + 1)};";
+  } else {
+    llvm::SmallVector<StringRef, 4> isVariadic;
+    isVariadic.reserve(numResults);
+    for (int i = 0; i < numResults; ++i) {
+      isVariadic.push_back(llvm::toStringRef(op.getResult(i).isVariadic()));
+    }
+    std::string isVariadicList = llvm::join(isVariadic, ", ");
+
+    m.body() << formatv(valueRangeCalcCode, isVariadicList, numNormalResults,
+                        numVariadicResults, "getOperation()->getNumResults()",
+                        "getOperation()->result_begin()", "result");
+  }
 
   for (int i = 0; i != numResults; ++i) {
     const auto &result = op.getResult(i);
     if (result.name.empty())
       continue;
 
-    const char *code = R"(
-  int variadicResultSize = (this->getNumResults() - {0}) / {1};
-  int offset = {2} + variadicResultSize * {3};
-  return )";
-    auto sizeAndOffset = formatv(code, numNormalResults, numVariadicResults,
-                                 emittedNormalResults, emittedVariadicResults);
-
     if (result.isVariadic()) {
       auto &m = opClass.newMethod("Operation::result_range", result.name);
-      m.body() << sizeAndOffset
-               << "{std::next(result_begin(), offset), "
-                  "std::next(result_begin(), offset + variadicResultSize)};";
-      ++emittedVariadicResults;
+      m.body() << "return getODSResults(" << i << ");";
     } else {
       auto &m = opClass.newMethod("Value *", result.name);
-      m.body() << sizeAndOffset << "this->getResult(offset);";
-      ++emittedNormalResults;
+      m.body() << "return *getODSResults(" << i << ").begin();";
     }
   }
 }