001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.nabla.forward.analysis;
018    
019    import java.util.ArrayList;
020    import java.util.HashSet;
021    import java.util.IdentityHashMap;
022    import java.util.Iterator;
023    import java.util.List;
024    import java.util.ListIterator;
025    import java.util.Map;
026    import java.util.Set;
027    
028    import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
029    import org.apache.commons.nabla.DifferentiationException;
030    import org.apache.commons.nabla.NablaMessages;
031    import org.apache.commons.nabla.forward.arithmetic.DAddTransformer;
032    import org.apache.commons.nabla.forward.arithmetic.DDivTransformer;
033    import org.apache.commons.nabla.forward.arithmetic.DMulTransformer;
034    import org.apache.commons.nabla.forward.arithmetic.DNegTransformer;
035    import org.apache.commons.nabla.forward.arithmetic.DRemTransformer;
036    import org.apache.commons.nabla.forward.arithmetic.DSubTransformer;
037    import org.apache.commons.nabla.forward.instructions.DLoadTransformer;
038    import org.apache.commons.nabla.forward.instructions.DReturnTransformer;
039    import org.apache.commons.nabla.forward.instructions.DStoreTransformer;
040    import org.apache.commons.nabla.forward.instructions.DcmpTransformer;
041    import org.apache.commons.nabla.forward.instructions.Dup2Transformer;
042    import org.apache.commons.nabla.forward.instructions.Dup2X1Transformer;
043    import org.apache.commons.nabla.forward.instructions.Dup2X2Transformer;
044    import org.apache.commons.nabla.forward.instructions.InvokeStaticTransformer;
045    import org.apache.commons.nabla.forward.instructions.NarrowingTransformer;
046    import org.apache.commons.nabla.forward.instructions.Pop2Transformer;
047    import org.apache.commons.nabla.forward.instructions.WideningTransformer;
048    import org.apache.commons.nabla.forward.trimming.DLoadPop2Trimmer;
049    import org.apache.commons.nabla.forward.trimming.SwappedDloadTrimmer;
050    import org.apache.commons.nabla.forward.trimming.SwappedDstoreTrimmer;
051    import org.objectweb.asm.Opcodes;
052    import org.objectweb.asm.Type;
053    import org.objectweb.asm.tree.AbstractInsnNode;
054    import org.objectweb.asm.tree.FieldInsnNode;
055    import org.objectweb.asm.tree.IincInsnNode;
056    import org.objectweb.asm.tree.InsnList;
057    import org.objectweb.asm.tree.InsnNode;
058    import org.objectweb.asm.tree.LdcInsnNode;
059    import org.objectweb.asm.tree.MethodInsnNode;
060    import org.objectweb.asm.tree.MethodNode;
061    import org.objectweb.asm.tree.TypeInsnNode;
062    import org.objectweb.asm.tree.VarInsnNode;
063    import org.objectweb.asm.tree.analysis.Analyzer;
064    import org.objectweb.asm.tree.analysis.AnalyzerException;
065    import org.objectweb.asm.tree.analysis.Frame;
066    import org.objectweb.asm.tree.analysis.Interpreter;
067    
068    /** Class transforming a method computing a value to a method
069     * computing both a value and its differential.
070     * @version $Id$
071     */
072    public class MethodDifferentiator {
073    
074        /** Maximal number of temporary size 2 variables. */
075        private static final int MAX_TEMP = 5;
076    
077        /** Math implementation classes. */
078        private final Set<String> mathClasses;
079    
080        /** Name of the derived class. */
081        private final String derivedName;
082    
083        /** Used locals variables array. */
084        private boolean[] usedLocals;
085    
086        /** Set of converted values. */
087        private final Set<TrackingValue> converted;
088    
089        /** Frames for the original method. */
090        private final Map<AbstractInsnNode, Frame<TrackingValue>> frames;
091    
092        /** Instructions successors array. */
093        private final Map<AbstractInsnNode, Set<AbstractInsnNode>> successors;
094    
095        /** Build a differentiator for a method.
096         * @param mathClasses math implementation classes
097         * @param derivedName name of the derived class
098         */
099        public MethodDifferentiator(final Set<String> mathClasses, final String derivedName) {
100            this.usedLocals   = null;
101            this.mathClasses  = mathClasses;
102            this.derivedName  = derivedName;
103            this.converted    = new HashSet<TrackingValue>();
104            this.frames       = new IdentityHashMap<AbstractInsnNode, Frame<TrackingValue>>();
105            this.successors   = new IdentityHashMap<AbstractInsnNode, Set<AbstractInsnNode>>();
106        }
107    
108        /** Get the index of the input {@link DerivativeStructure derivative structure} variable.
109         * @return index of the input {@link DerivativeStructure derivative structure} variable
110         */
111        public int getInputDSIndex() {
112            // TODO: this should be improved in the general case,
113            // as we are not sure the variable will always remain at index 1
114            return 1;
115        }
116    
117        /**
118         * Differentiate a method.
119         * @param primitiveName primitive class name
120         * @param method method to differentiate (<em>will</em> be modified)
121         * @exception DifferentiationException if method cannot be differentiated
122         */
123        public void differentiate(final String primitiveName, final MethodNode method)
124            throws DifferentiationException {
125            try {
126    
127                // at start, "this" and one DerivativeStructure are already used
128                method.maxLocals  = 2 * (method.maxLocals + MAX_TEMP) - 1;
129                usedLocals = new boolean[method.maxLocals];
130                useLocal(0, 1);
131                useLocal(1, 4);
132    
133                final Type dsType = Type.getType(DerivativeStructure.class);
134    
135                // add spare cells to hold new variables if needed
136                addSpareLocalVariables(method.instructions);
137    
138                // analyze the original code, tracing values production/consumption
139                final FlowAnalyzer analyzer =
140                    new FlowAnalyzer(new TrackingInterpreter(), method.instructions);
141                final Frame<TrackingValue>[] array = analyzer.analyze(primitiveName, method);
142    
143                // convert the array into a map, since code changes will shift all indices
144                for (int i = 0; i < array.length; ++i) {
145                    frames.put(method.instructions.get(i), array[i]);
146                }
147    
148                // identify the needed changes
149                final Set<AbstractInsnNode> changes = identifyChanges(method.instructions);
150    
151                if (changes.isEmpty()) {
152    
153                    // TODO: merge this case with the general case: DRETURN must always be
154                    // changed when the function signature changes, and the change is either
155                    // converting DRETURN to ARETURN, possibly with a prepended call to
156                    // convertDoubleToDerivativeStructure before the ARETURN when stack0converted
157                    // is false
158    
159                    // the method does not depend on the parameter at all!
160                    // we replace all "return d;" by "return new DerivativeStructure(parameters, order, d);"
161                    for (final Iterator<AbstractInsnNode> i = method.instructions.iterator(); i.hasNext();) {
162                        final AbstractInsnNode insn = i.next();
163                        if (insn.getOpcode() == Opcodes.DRETURN) {
164                            final InsnList list = new DReturnTransformer(false).getReplacement(insn, this);
165                            method.instructions.insert(insn, list);
166                            method.instructions.remove(insn);
167                        }
168                    }
169    
170                } else {
171    
172                    // perform the code changes
173                    for (final AbstractInsnNode insn : changes) {
174                        method.instructions.insert(insn, getReplacement(insn));
175                        method.instructions.remove(insn);
176                    }
177    
178                    // trim generated instructions list
179                    new SwappedDloadTrimmer().trim(method.instructions);
180                    new SwappedDstoreTrimmer().trim(method.instructions);
181                    new DLoadPop2Trimmer().trim(method.instructions);
182    
183                }
184    
185                // remove the local variables added at the beginning and not used
186                removeUnusedSpareLocalVariables(method.instructions);
187    
188                // set the method properties
189                method.desc       = Type.getMethodDescriptor(dsType, dsType);
190                method.access    |= Opcodes.ACC_SYNTHETIC;
191                method.maxLocals  = maxVariables();
192    
193            } catch (AnalyzerException ae) {
194                ae.printStackTrace(System.err);
195                if ((ae.getCause() != null) && ae.getCause() instanceof DifferentiationException) {
196                    throw (DifferentiationException) ae.getCause();
197                } else {
198                    throw new DifferentiationException(NablaMessages.UNABLE_TO_ANALYZE_METHOD,
199                                                       primitiveName, method.name, ae.getMessage());
200                }
201            }
202        }
203    
204        /** Add spare cells for new local variables.
205         * <p>In order to ease conversion from double values to derivative structures,
206         * we start by reserving one spare cell between each original local variables.
207         * So we have to modify the indices in all instructions referencing local
208         * variables in the original code, to take into account the renumbering
209         * introduced by these spare cells. The spare cells by themselves will
210         * be referenced by the converted instructions in the following passes.</p>
211         * <p>The spare cells that will not be used will be reclaimed after
212         * conversion, to avoid wasting memory.</p>
213         * @param instructions instructions of the method
214         * @exception DifferentiationException if local variables array has not been
215         * expanded appropriately beforehand
216         * @see #removeUnusedSpareLocalVariables()
217         */
218        private void addSpareLocalVariables(final InsnList instructions)
219            throws DifferentiationException {
220            for (final Iterator<AbstractInsnNode> i = instructions.iterator(); i.hasNext();) {
221                final AbstractInsnNode insn = i.next();
222                if (insn.getType() == AbstractInsnNode.VAR_INSN) {
223                    final VarInsnNode varInsn = (VarInsnNode) insn;
224                    if (varInsn.var > 2) {
225                        varInsn.var = 2 * varInsn.var - 1;
226                        final int opcode = varInsn.getOpcode();
227                        if ((opcode == Opcodes.ILOAD)  || (opcode == Opcodes.FLOAD)  ||
228                            (opcode == Opcodes.ALOAD)  || (opcode == Opcodes.ISTORE) ||
229                            (opcode == Opcodes.FSTORE) || (opcode == Opcodes.ASTORE)) {
230                            useLocal(varInsn.var, 1);
231                        } else {
232                            useLocal(varInsn.var, 2);
233                        }
234                    }
235                } else if (insn.getOpcode() == Opcodes.IINC) {
236                    final IincInsnNode iincInsn = (IincInsnNode) insn;
237                    if (iincInsn.var > 2) {
238                        iincInsn.var = 2 * iincInsn.var - 1;
239                        useLocal(iincInsn.var, 1);
240                    }
241                }
242            }
243        }
244    
245        /** Remove the unused spare cells introduced at conversion start.
246         * @param instructions instructions of the method
247         * @see #addSpareLocalVariables()
248         */
249        private void removeUnusedSpareLocalVariables(final InsnList instructions) {
250            for (final Iterator<AbstractInsnNode> i = instructions.iterator(); i.hasNext();) {
251                final AbstractInsnNode insn = i.next();
252                if (insn.getType() == AbstractInsnNode.VAR_INSN) {
253                    shiftVariable((VarInsnNode) insn);
254                }
255            }
256        }
257    
258        /** Identify the instructions that must be changed.
259         * <p>Identification is based on data flow analysis. We start by changing
260         * the local variables in the initial frame to match the parameters of
261         * the derivative method, and propagate these variables following the
262         * instructions path, updating stack cells and local variables as needed.
263         * Instructions that must be changed are the ones that consume changed
264         * variables or stack cells.</p>
265         * @param instructions instructions of the method
266         * @return set containing all the instructions that must be changed
267         */
268        private Set<AbstractInsnNode> identifyChanges(final InsnList instructions) {
269    
270            // the pending set contains the values (local variables or stack cells)
271            // that have been changed, they will trigger changes on the instructions
272            // that consume them
273            final Set<TrackingValue> pending = new HashSet<TrackingValue>();
274    
275            // the changes set contains the instructions that must be changed
276            final Set<AbstractInsnNode> changes = new HashSet<AbstractInsnNode>();
277    
278            // start by converting the parameter of the method,
279            // which is kept in local variable 1 of the initial frame
280            final TrackingValue dpParameter = frames.get(instructions.get(0)).getLocal(1);
281            pending.add(dpParameter);
282    
283            // propagate the values conversions throughout the method
284            while (!pending.isEmpty()) {
285    
286                // pop one element from the set of changed values
287                final Iterator<TrackingValue> iterator = pending.iterator();
288                final TrackingValue value = iterator.next();
289                iterator.remove();
290    
291                // this value is converted
292                converted.add(value);
293    
294                // check the consumers instructions for this value
295                for (final AbstractInsnNode consumer : value.getConsumers()) {
296    
297                    // an instruction consuming a converted value and producing
298                    // a double must be changed to produce a derivative structure,
299                    // get the double values produced and add them to the changed set
300                    pending.addAll(getProducedAndNotConvertedDoubleValues(consumer));
301    
302                    // as a consumer of a converted value, the instruction must be changed
303                    changes.add(consumer);
304    
305                }
306    
307                // check the producers instructions for this value
308                for (final AbstractInsnNode producer : value.getProducers()) {
309    
310                    // an instruction producing a converted value must be changed
311                    changes.add(producer);
312    
313                }
314            }
315    
316            // the various GETFIELD/PUTFIELD instructions must also be changed
317            // to retrieve the field from the outer class
318            final ListIterator<AbstractInsnNode> iterator = instructions.iterator();
319            while (iterator.hasNext()) {
320                final AbstractInsnNode ins = iterator.next();
321                if ((ins.getOpcode() == Opcodes.GETFIELD) || (ins.getOpcode() == Opcodes.PUTFIELD)) {
322                    changes.add(ins);
323                }
324            }
325    
326            return changes;
327    
328        }
329    
330        /** Get the list of double values produced by an instruction and not yet converted.
331         * @param instruction instruction producing the values
332         * @return list of double values produced
333         */
334        private List<TrackingValue> getProducedAndNotConvertedDoubleValues(final AbstractInsnNode instruction) {
335    
336            final List<TrackingValue> values = new ArrayList<TrackingValue>();
337    
338            // get the frame before instruction execution
339            final Frame<TrackingValue> before = frames.get(instruction);
340            final int beforeStackSize = before.getStackSize();
341            final int locals = before.getLocals();
342    
343            // check the frames produced by this instruction
344            // (they correspond to the input frames of its successors)
345            final Set<AbstractInsnNode> set = successors.get(instruction);
346            if (set != null) {
347    
348                // loop over the successors of this instruction
349                for (final AbstractInsnNode successor : set) {
350                    final Frame<TrackingValue> produced = frames.get(successor);
351    
352                    // check the stack cells
353                    for (int i = 0; i < produced.getStackSize(); ++i) {
354                        final TrackingValue value = produced.getStack(i);
355                        if (((i >= beforeStackSize) || (value != before.getStack(i))) &&
356                            value.getType().equals(Type.DOUBLE_TYPE) &&
357                            !converted.contains(value)) {
358                            values.add(value);
359                        }
360                    }
361    
362                    // check the local variables
363                    for (int i = 0; i < locals; ++i) {
364                        final TrackingValue value = (TrackingValue) produced.getLocal(i);
365                        if ((value != before.getLocal(i)) &&
366                            value.getType().equals(Type.DOUBLE_TYPE) &&
367                            !converted.contains(value)) {
368                            values.add(value);
369                        }
370                    }
371                }
372            }
373    
374            return values;
375    
376        }
377    
378        /** Get the replacement list for an instruction.
379         * @param insn instruction to replace
380         * @return replacement instructions list
381         * @exception DifferentiationException if some instruction cannot be handled
382         * or if no temporary variable can be reserved
383         */
384        private InsnList getReplacement(final AbstractInsnNode insn)
385            throws DifferentiationException {
386    
387            // get the frame at the start of the instruction
388            final Frame<TrackingValue> frame = frames.get(insn);
389            final int size = frame.getStackSize();
390            final boolean stack1Converted = (size > 0) && converted.contains(frame.getStack(size - 2));
391            final boolean stack0Converted = (size > 1) && converted.contains(frame.getStack(size - 1));
392    
393            switch(insn.getOpcode()) {
394                case Opcodes.DLOAD :
395                    useLocal(((VarInsnNode) insn).var, 4);
396                    return new DLoadTransformer().getReplacement(insn, this);
397                case Opcodes.DALOAD :
398                    // TODO: add support for DALOAD differentiation
399                    throw new RuntimeException("DALOAD not handled yet");
400                case Opcodes.DSTORE :
401                    useLocal(((VarInsnNode) insn).var, 4);
402                    return new DStoreTransformer().getReplacement(insn, this);
403                case Opcodes.DASTORE :
404                    // TODO: add support for DASTORE differentiation
405                    throw new RuntimeException("DASTORE not handled yet");
406                case Opcodes.DUP2 :
407                    return new Dup2Transformer().getReplacement(insn, this);
408                case Opcodes.POP2 :
409                    return new Pop2Transformer().getReplacement(insn, this);
410                case Opcodes.DUP2_X1 :
411                    return new Dup2X1Transformer().getReplacement(insn, this);
412                case Opcodes.DUP2_X2 :
413                    return new Dup2X2Transformer(stack0Converted, stack1Converted).getReplacement(insn, this);
414                case Opcodes.DADD :
415                    return new DAddTransformer(stack0Converted, stack1Converted).getReplacement(insn, this);
416                case Opcodes.DSUB :
417                    return new DSubTransformer(stack0Converted, stack1Converted).getReplacement(insn, this);
418                case Opcodes.DMUL :
419                    return new DMulTransformer(stack0Converted, stack1Converted).getReplacement(insn, this);
420                case Opcodes.DDIV :
421                    return new DDivTransformer(stack0Converted, stack1Converted).getReplacement(insn, this);
422                case Opcodes.DREM :
423                    return new DRemTransformer(stack0Converted, stack1Converted).getReplacement(insn, this);
424                case Opcodes.DNEG :
425                    return new DNegTransformer().getReplacement(insn, this);
426                case Opcodes.DCONST_0 :
427                case Opcodes.DCONST_1 :
428                case Opcodes.LDC :
429                case Opcodes.I2D :
430                case Opcodes.L2D :
431                case Opcodes.F2D :
432                    return new WideningTransformer().getReplacement(insn, this);
433                case Opcodes.D2I :
434                case Opcodes.D2L :
435                case Opcodes.D2F :
436                    return new NarrowingTransformer().getReplacement(insn, this);
437                case Opcodes.DCMPL :
438                case Opcodes.DCMPG :
439                    return new DcmpTransformer(stack0Converted, stack1Converted).getReplacement(insn, this);
440                case Opcodes.DRETURN :
441                    // TODO: the constructor parameter forced to true seems strange...
442                    return new DReturnTransformer(true).getReplacement(insn, this);
443                case Opcodes.GETSTATIC :
444                    // TODO: add support for GETSTATIC differentiation
445                    throw new RuntimeException("GETSTATIC not handled yet");
446                case Opcodes.PUTSTATIC :
447                    // TODO: add support for PUTSTATIC differentiation
448                    throw new RuntimeException("PUTSTATIC not handled yet");
449                case Opcodes.GETFIELD :
450                    return replaceGetField((FieldInsnNode) insn);
451                case Opcodes.PUTFIELD :
452                    // TODO: add support for PUTFIELD differentiation
453                    throw new RuntimeException("PUTFIELD not handled yet");
454                case Opcodes.INVOKEVIRTUAL :
455                    // TODO: add support for INVOKEVIRTUAL differentiation
456                    throw new RuntimeException("INVOKEVIRTUAL not handled yet");
457                case Opcodes.INVOKESPECIAL :
458                    // TODO: add support for INVOKESPECIAL differentiation
459                    throw new RuntimeException("INVOKESPECIAL not handled yet");
460                case Opcodes.INVOKESTATIC :
461                    return new InvokeStaticTransformer(stack0Converted, stack1Converted).getReplacement(insn, this);
462                case Opcodes.INVOKEINTERFACE :
463                    // TODO: add support for INVOKEINTERFACE differentiation
464                    throw new RuntimeException("INVOKEINTERFACE not handled yet");
465                case Opcodes.INVOKEDYNAMIC :
466                    // TODO: add support for INVOKEDYNAMIC differentiation
467                    throw new RuntimeException("INVOKEDYNAMIC not handled yet");
468                case Opcodes.NEWARRAY :
469                    // TODO: add support for NEWARRAY differentiation
470                    throw new RuntimeException("NEWARRAY not handled yet");
471                case Opcodes.ANEWARRAY :
472                    // TODO: add support for ANEWARRAY differentiation
473                    throw new RuntimeException("ANEWARRAY not handled yet");
474                case Opcodes.MULTIANEWARRAY :
475                    // TODO: add support for MULTIANEWARRAY differentiation
476                    throw new RuntimeException("MULTIANEWARRAY not handled yet");
477                default:
478                    throw new DifferentiationException(NablaMessages.UNABLE_TO_HANDLE_INSTRUCTION, insn.getOpcode());
479            }
480    
481        }
482    
483        /** Replace a GETFIELD instruction.
484         * @param fieldInsn field instruction
485         * @return replacement instructions list
486         * @exception DifferentiationException if instruction cannot be replaced
487         */
488        private InsnList replaceGetField(final FieldInsnNode fieldInsn) throws DifferentiationException {
489    
490            final InsnList list = new InsnList();
491    
492            // get the field as an object
493            list.add(new LdcInsnNode(fieldInsn.name));
494            list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, derivedName, "getPrimitiveField",
495                                        Type.getMethodDescriptor(Type.getType(Object.class),
496                                                                 Type.getType(String.class))));
497    
498            // convert it to the expected type
499            final Type type = Type.getType(fieldInsn.desc);
500            final Type boxedType;
501            final String valueMethodName;
502            switch (type.getSort()) {
503                case Type.VOID:
504                    throw new DifferentiationException(NablaMessages.CANNOT_GET_VOID_FIELD, fieldInsn.name);
505                case Type.BOOLEAN:
506                    valueMethodName = "booleanValue";
507                    boxedType       = Type.getType(Boolean.class);
508                    break;
509                case Type.CHAR:
510                    valueMethodName = "charValue";
511                    boxedType       = Type.getType(Character.class);
512                    break;
513                case Type.BYTE:
514                    valueMethodName = "byteValue";
515                    boxedType       = Type.getType(Byte.class);
516                    break;
517                case Type.SHORT:
518                    valueMethodName = "shortValue";
519                    boxedType       = Type.getType(Short.class);
520                    break;
521                case Type.INT:
522                    valueMethodName = "intValue";
523                    boxedType       = Type.getType(Integer.class);
524                    break;
525                case Type.FLOAT:
526                    valueMethodName = "floatValue";
527                    boxedType       = Type.getType(Float.class);
528                    break;
529                case Type.LONG:
530                    valueMethodName = "longValue";
531                    boxedType       = Type.getType(Long.class);
532                    break;
533                case Type.DOUBLE:
534                    valueMethodName = "doubleValue";
535                    boxedType       = Type.getType(Double.class);
536                    break;
537                default :
538                    // do nothing for Type.ARRAY and Type.OBJECT
539                    valueMethodName = null;
540                    boxedType       = null;
541    
542            }
543            if (boxedType != null) {
544                list.add(new TypeInsnNode(Opcodes.CHECKCAST, boxedType.getInternalName()));
545            }
546            if (valueMethodName != null) {
547                list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, boxedType.getInternalName(),
548                                            valueMethodName,
549                                            Type.getMethodDescriptor(type, new Type[0])));
550            }
551    
552            return list;
553    
554        }
555    
556        /** Test if a class is a math implementation class.
557         * @param name name of the class to test
558         * @return true if the named class is a math implementation class
559         */
560        public boolean isMathImplementationClass(final String name) {
561            return mathClasses.contains(name);
562        }
563    
564        /** Create instructions to convert a double on top of stack to a {@link DerivativeStructure}.
565         * @return list of conversion instructions
566         */
567        public InsnList doubleToDerivativeStructureConversion() {
568    
569            final InsnList list = new InsnList();
570    
571            // operand stack initial state: d
572            list.add(new TypeInsnNode(Opcodes.NEW,
573                                      Type.getInternalName(DerivativeStructure.class))); // => d y_ds
574            list.add(new InsnNode(Opcodes.DUP_X2));                                      // => y_ds d y_ds
575            list.add(new InsnNode(Opcodes.DUP_X2));                                      // => y_ds y_ds d y_ds
576            list.add(new InsnNode(Opcodes.POP));                                         // => y_ds y_ds d
577            list.add(new VarInsnNode(Opcodes.ALOAD, getInputDSIndex()));                 // => y_ds y_ds d x_ds
578            list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
579                                        Type.getInternalName(DerivativeStructure.class),
580                                        "getFreeParameters",
581                                        Type.getMethodDescriptor(Type.INT_TYPE)));       // => y_ds y_ds d params
582            list.add(new VarInsnNode(Opcodes.ALOAD, 1));                                 // => y_ds y_ds d params x_ds
583            list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
584                                        Type.getInternalName(DerivativeStructure.class),
585                                        "getOrder",
586                                        Type.getMethodDescriptor(Type.INT_TYPE)));       // => y_ds y_ds d params order
587            list.add(new InsnNode(Opcodes.DUP2_X2));                                     // => y_ds y_ds params order d params order
588            list.add(new InsnNode(Opcodes.POP2));                                        // => y_ds y_ds params order d
589            list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
590                                        Type.getInternalName(DerivativeStructure.class),
591                                        "<init>",
592                                        Type.getMethodDescriptor(Type.VOID_TYPE,
593                                                                 Type.INT_TYPE,
594                                                                 Type.INT_TYPE,
595                                                                 Type.DOUBLE_TYPE)));    // => y_ds
596    
597            return list;
598    
599        }
600    
601        /** Set a local variable as used by the modified code.
602         * @param index index of the variable
603         * @param size size of the variable (1 or 2 for standard variables,
604         * 4 for special expanded derivative structures)
605         * @exception DifferentiationException if the number of the
606         * temporary variable lies outside of the allowed range
607         */
608        public void useLocal(final int index, final int size)
609            throws DifferentiationException {
610            if ((index < 0) || ((index + size) > usedLocals.length)) {
611                throw new DifferentiationException(NablaMessages.INDEX_OF_LOCAL_VARIABLE_OUT_OF_RANGE,
612                                                   size, index, 1, MAX_TEMP);
613            }
614            for (int i = index; i < index + size; ++i) {
615                usedLocals[i] = true;
616            }
617        }
618    
619        /** Get index of a size 2 temporary variable.
620         * <p>Temporary variables can be used for very short duration
621         * storage of size 2 values (i.e one double, or one long or two
622         * integers). These variables are reused in many replacement
623         * instructions sequences, so their content may be overridden
624         * at any time: they should be considered to have a scope
625         * limited to one replacement sequence only. This means that
626         * one should <em>not</em> store a value in a variable in one
627         * replacement sequence and retrieve it later in another
628         * replacement sequence as it may have been overridden in
629         * between.</p>
630         * <p>At most 5 temporary variables may be used.</p>
631         * @param number number of the temporary variable (must be
632         * between 1 and the maximal number of available temporary
633         * variables)
634         * @return index of the variable within the local variables
635         * array
636         * @exception DifferentiationException if the number of the
637         * temporary variable lies outside of the allowed range
638         */
639        public int getTmp(final int number) throws DifferentiationException {
640            if ((number < 0) || (number > MAX_TEMP)) {
641                throw new DifferentiationException(NablaMessages.NUMBER_OF_TEMPORARY_VARIABLES_OUT_OF_RANGE,
642                                                   number, 1, MAX_TEMP);
643            }
644            final int index = usedLocals.length - 2 * number;
645            useLocal(index, 2);
646            return index;
647        }
648    
649        /** Shifted the index of a variable instruction.
650         * @param insn variable instruction
651         */
652        private void shiftVariable(final VarInsnNode insn) {
653            int shifted = 0;
654            for (int i = 0; i < insn.var; ++i) {
655                if (usedLocals[i]) {
656                    ++shifted;
657                }
658            }
659            insn.var = shifted;
660        }
661    
662        /** Compute the maximal number of used local variables.
663         * @return maximal number of used local variables
664         */
665        private int maxVariables() {
666            int max = 0;
667            for (final boolean isUsed : usedLocals) {
668                if (isUsed) {
669                    ++max;
670                }
671            }
672            return max;
673        }
674    
675        /** Analyzer preserving instructions successors information. */
676        private class FlowAnalyzer extends Analyzer<TrackingValue> {
677    
678            /** Instructions of the method. */
679            private final InsnList instructions;
680    
681            /** Simple constructor.
682             * @param interpreter associated interpreter
683             * @param instructions instructions of the method
684             */
685            public FlowAnalyzer(final Interpreter<TrackingValue> interpreter,
686                                final InsnList instructions) {
687                super(interpreter);
688                this.instructions = instructions;
689            }
690    
691            /** Store a new edge.
692             * @param insn current instruction
693             * @param successor successor instruction
694             */
695            protected void newControlFlowEdge(final int insn, final int successor) {
696                // store the successor information
697                final AbstractInsnNode node = instructions.get(insn);
698                Set<AbstractInsnNode> set = successors.get(node);
699                if (set == null) {
700                    set = new HashSet<AbstractInsnNode>();
701                    successors.put(node, set);
702                }
703                set.add(instructions.get(successor));
704            }
705    
706        }
707    
708    }