@Override
    public Object process(Node nd, Stack<Node> stack, NodeProcessorCtx procCtx,
                          Object... nodeOutputs) throws SemanticException {
      ReduceSinkOperator rs = (ReduceSinkOperator) nd;
      SemiJoinRemovalContext rCtx = (SemiJoinRemovalContext) procCtx;
      ParseContext pCtx = rCtx.parseContext;
      SemiJoinBranchInfo sjInfo = pCtx.getRsToSemiJoinBranchInfo().get(rs);
      if (sjInfo == null) {
        // nothing to do here.
        return null;
      }
      TableScanOperator targetTSOp = sjInfo.getTsOp();

      // This is a semijoin branch. The stack should look like,
      // <Parent Ops>-SEL-GB1-RS1-GB2-RS2
      GroupByOperator gbOp = (GroupByOperator) stack.get(stack.size() - 2);
      GroupByDesc gbDesc = gbOp.getConf();
      List<AggregationDesc> aggregationDescs = gbDesc.getAggregators();
      for (AggregationDesc agg : aggregationDescs) {
        if (!isBloomFilterAgg(agg)) {
          continue;
        }

        GenericUDAFBloomFilterEvaluator udafBloomFilterEvaluator =
            (GenericUDAFBloomFilterEvaluator) agg.getGenericUDAFEvaluator();
        if (udafBloomFilterEvaluator.hasHintEntries()) {
          return null; // Created using hint, skip it
        }

        if (removeBasedOnStats) {
          long expectedEntries = udafBloomFilterEvaluator.getExpectedEntries();
          if (expectedEntries == -1 || expectedEntries >
              pCtx.getConf().getLongVar(ConfVars.TEZ_MAX_BLOOM_FILTER_ENTRIES)) {
            if (sjInfo.getIsHint() && expectedEntries == -1) {
              throw new SemanticException("Removing hinted semijoin due to lack to stats" +
                  " or exceeding max bloom filter entries");
            } else if(sjInfo.getIsHint()) {
              // do not remove if hint is provided
              continue;
            }
            // Remove the semijoin optimization branch along with ALL the mappings
            // The parent GB2 has all the branches. Collect them and remove them.
            for (Node node : gbOp.getChildren()) {
              ReduceSinkOperator rsFinal = (ReduceSinkOperator) node;
              TableScanOperator ts = pCtx.getRsToSemiJoinBranchInfo().
                  get(rsFinal).getTsOp();
              if (LOG.isDebugEnabled()) {
                LOG.debug("expectedEntries=" + expectedEntries + ". "
                    + "Either stats unavailable or expectedEntries exceeded max allowable bloomfilter size. "
                    + "Removing semijoin "
                    + OperatorUtils.getOpNamePretty(rs) + " - " + OperatorUtils.getOpNamePretty(ts));
              }
              GenTezUtils.removeBranch(rsFinal);
              GenTezUtils.removeSemiJoinOperator(pCtx, rsFinal, ts);
            }
            return null;
          }
        }
      }

      if (removeBasedOnStats) {
        // At this point, hinted semijoin case has been handled already
        // Check if big table is big enough that runtime filtering is
        // worth it.
        TableScanOperator ts = sjInfo.getTsOp();
        if (ts.getStatistics() != null) {
          long numRows = ts.getStatistics().getNumRows();
          if (numRows < pCtx.getConf().getLongVar(ConfVars.TEZ_BIGTABLE_MIN_SIZE_SEMIJOIN_REDUCTION)) {
            if (sjInfo.getShouldRemove()) {
              if (LOG.isDebugEnabled()) {
                LOG.debug("Insufficient rows (" + numRows + ") to justify semijoin optimization. Removing semijoin "
                    + OperatorUtils.getOpNamePretty(rs) + " - " + OperatorUtils.getOpNamePretty(ts));
              }
              GenTezUtils.removeBranch(rs);
              GenTezUtils.removeSemiJoinOperator(pCtx, rs, ts);
            }
          }
        }
      }

      if (removeRedundant) {
        // Look for RS ops above the current semijoin branch
        Set<ReduceSinkOperator> rsOps = OperatorUtils.findOperators(
            ((Operator<?>) stack.get(stack.size() - 5)).getParentOperators().get(0),
            ReduceSinkOperator.class);
        for (Operator<?> otherRSOp : rsOps) {
          SemiJoinBranchInfo otherSjInfo = pCtx.getRsToSemiJoinBranchInfo().get(otherRSOp);
          // First conjunct prevents SJ RS from removing itself
          if (otherRSOp != rs && otherSjInfo != null && otherSjInfo.getTsOp() == targetTSOp) {
            if (rCtx.opsToRemove.containsKey(otherRSOp)) {
              // We found siblings, since we are removing the other operator, no need to remove this one
              continue;
            }
            List<ExprNodeDesc> thisTargetColumns = pCtx.getRsToRuntimeValuesInfoMap().get(rs).getTargetColumns();
            List<ExprNodeDesc> otherTargetColumns =
                pCtx.getRsToRuntimeValuesInfoMap().get(otherRSOp).getTargetColumns();
            if (!ExprNodeDescUtils.isSame(thisTargetColumns, otherTargetColumns)) {
              // Filter should be on the same columns, otherwise we do not proceed
              continue;
            }
            rCtx.opsToRemove.put(rs, targetTSOp);
            break;
          }
        }
      }

      return null;
    }
TezCompiler translates the operator plan into TezTasks.
ql/src/java/org/apache/hadoop/hive/ql/parse/TezCompiler.java