aboutsummaryrefslogtreecommitdiff
path: root/exec/java-exec/src/main/java/org/apache/drill/exec/compile/bytecode/MethodAnalyzer.java
blob: ae900cf71c649d4ab800d9f27f7d15562a5c007a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.drill.exec.compile.bytecode;

import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.analysis.Analyzer;
import org.objectweb.asm.tree.analysis.AnalyzerException;
import org.objectweb.asm.tree.analysis.Frame;
import org.objectweb.asm.tree.analysis.Interpreter;
import org.objectweb.asm.tree.analysis.Value;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.Set;

/**
 * Analyzer that allows us to inject additional functionality into ASMs basic analysis.
 *
 * <p>We need to be able to keep track of local variables that are assigned to each other
 * so that we can infer their replaceability (for scalar replacement). In order to do that,
 * we need to know when local variables are assigned (with the old value being overwritten)
 * so that we can associate them with the new value, and hence determine whether they can
 * also be replaced, or not.
 *
 * <p>In order to capture the assignment operation, we have to provide our own Frame<>, but
 * ASM doesn't provide a direct way to do that. Here, we use the Analyzer's newFrame() methods
 * as factories that will provide our own derivative of Frame<> which we use to detect
 */
public class MethodAnalyzer<V extends Value> extends Analyzer <V> {

  // list of method instructions which is analyzed
  private InsnList insnList;

  public MethodAnalyzer(Interpreter<V> interpreter) {
    super(interpreter);
  }

  @Override
  protected Frame<V> newFrame(int maxLocals, int maxStack) {
    return new AssignmentTrackingFrame<>(maxLocals, maxStack);
  }

  @Override
  protected Frame<V> newFrame(Frame<? extends V> src) {
    return new AssignmentTrackingFrame<>(src);
  }

  @Override
  protected void newControlFlowEdge(int insnIndex, int successorIndex) {
    AssignmentTrackingFrame<V> oldFrame = (AssignmentTrackingFrame<V>) getFrames()[insnIndex];
    AbstractInsnNode insn = insnList.get(insnIndex);
    if (insn.getType() == AbstractInsnNode.LABEL) {
      // checks whether current label corresponds to the end of conditional block to restore previous
      // local variables set
      if (insn.equals(oldFrame.labelsStack.peekFirst())) {
        oldFrame.localVariablesSet.pop();
        oldFrame.labelsStack.pop();
      }
    }
  }

  @Override
  public Frame<V>[] analyze(String owner, MethodNode method) throws AnalyzerException {
    insnList = method.instructions;
    return super.analyze(owner, method);
  }

  /**
   * Custom Frame<> that captures setLocal() calls in order to associate values
   * that are assigned to the same local variable slot. Also it controls stack to determine whether
   * object was assigned to the value declared outside of conditional block.
   *
   * <p>Since this is almost a pass-through, the constructors' arguments match
   * those from Frame<>.
   */
  private static class AssignmentTrackingFrame<V extends Value> extends Frame<V> {

    // represents stack of variable sets declared inside current code block
    private final Deque<Set<Integer>> localVariablesSet;

    // stack of LabelNode instances which correspond to the end of conditional block
    private final Deque<LabelNode> labelsStack;

    /**
     * Constructor.
     *
     * @param nLocals the number of locals the frame should have
     * @param nStack the maximum size of the stack the frame should have
     */
    public AssignmentTrackingFrame(int nLocals, int nStack) {
      super(nLocals, nStack);
      localVariablesSet = new ArrayDeque<>();
      localVariablesSet.push(new HashSet<>());
      labelsStack = new ArrayDeque<>();
    }

    /**
     * Copy constructor.
     *
     * @param src the frame being copied
     */
    @SuppressWarnings("unchecked")
    public AssignmentTrackingFrame(Frame<? extends V> src) {
      super(src);
      AssignmentTrackingFrame trackingFrame = (AssignmentTrackingFrame) src;
      localVariablesSet = new ArrayDeque<>();
      for (Set<Integer> integers : (Deque<Set<Integer>>) trackingFrame.localVariablesSet) {
        localVariablesSet.addFirst(new HashSet<>(integers));
      }
      labelsStack = new ArrayDeque<>(trackingFrame.labelsStack);
    }

    @Override
    public void setLocal(int i, V value) {
      /*
       * If we're replacing one ReplacingBasicValue with another, we need to
       * associate them together so that they will have the same replaceability
       * attributes. We also track the local slot the new value will be stored in.
       */
      if (value instanceof ReplacingBasicValue) {
        ReplacingBasicValue replacingValue = (ReplacingBasicValue) value;
        replacingValue.setFrameSlot(i);
        V localValue = getLocal(i);
        Set<Integer> currentLocalVars = localVariablesSet.element();
        if (localValue instanceof ReplacingBasicValue) {
          if (!currentLocalVars.contains(i)) {
            // value is assigned to object declared outside of conditional block
            replacingValue.setAssignedInConditionalBlock();
          }
          ReplacingBasicValue localReplacingValue = (ReplacingBasicValue) localValue;
          localReplacingValue.associate(replacingValue);
        } else {
          currentLocalVars.add(i);
        }
      }

      super.setLocal(i, value);
    }

    @Override
    public void initJumpTarget(int opcode, LabelNode target) {
      if (target != null) {
        switch (opcode) {
          case IFEQ:
          case IFNE:
          case IFLT:
          case IFGE:
          case IFGT:
          case IFLE:
          case IF_ICMPEQ:
          case IF_ICMPNE:
          case IF_ICMPLT:
          case IF_ICMPGE:
          case IF_ICMPGT:
          case IF_ICMPLE:
          case IF_ACMPEQ:
          case IF_ACMPNE:
          case IFNONNULL:
            // for the case when conditional block is handled, creates new variables set
            // to store local variables declared inside current conditional block and
            // stores its target LabelNode to restore previous variables set after conditional block is ended
            localVariablesSet.push(new HashSet<>());
            labelsStack.push(target);
        }
      }
    }
  }
}