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.algorithmic.forward.instructions;
018    
019    import org.apache.commons.nabla.algorithmic.forward.analysis.InstructionsTransformer;
020    import org.apache.commons.nabla.algorithmic.forward.analysis.MethodDifferentiator;
021    import org.apache.commons.nabla.core.DifferentiationException;
022    import org.objectweb.asm.Opcodes;
023    import org.objectweb.asm.tree.AbstractInsnNode;
024    import org.objectweb.asm.tree.InsnList;
025    import org.objectweb.asm.tree.InsnNode;
026    import org.objectweb.asm.tree.MethodInsnNode;
027    import org.objectweb.asm.tree.TypeInsnNode;
028    import org.objectweb.asm.tree.VarInsnNode;
029    
030    /** Differentiation transformer for DRETURN instructions.
031     * <p>DRETURN instructions are replaced by instructions
032     * that build a {@link org.apache.commons.nabla.core.DifferentialPair
033     * DifferentialPair} instance from the expanded differential pair
034     * on operand stack head and an ARETURN instruction returning this
035     * instance.</p>
036     */
037    public class DReturnTransformer implements InstructionsTransformer {
038    
039        // CHECKSTYLE: stop HideUtilityClassConstructor
040        /** Holder for the singleton instance.*/
041        private static class LazyHolder  {
042            /** The singleton instance. */
043            private static final InstructionsTransformer INSTANCE = new DReturnTransformer();
044        }
045        // CHECKSTYLE: resume HideUtilityClassConstructor
046    
047        /** Hidden constructor.
048         */
049        private DReturnTransformer() {
050        }
051    
052        /** Get the singleton instance.
053         * <p>We use here the Initialization on Demand Holder idiom.</p>
054         * @return the singleton instance
055         */
056        public static InstructionsTransformer getInstance() {
057            return LazyHolder.INSTANCE;
058        }
059    
060        /** {@inheritDoc} */
061        public InsnList getReplacement(final AbstractInsnNode insn,
062                                       final MethodDifferentiator methodDifferentiator)
063            throws DifferentiationException {
064    
065            // reuse local variables slots 1, 2, 3 and 4 for temporary storage
066            // (this may reduce the number of needed local variables)
067            methodDifferentiator.useLocal(1, 4);
068    
069            final InsnList list = new InsnList();
070            // operand stack initial state: a0, a1
071            list.add(new VarInsnNode(Opcodes.DSTORE, 3));             // => a0
072            list.add(new VarInsnNode(Opcodes.DSTORE, 1));             // =>
073            list.add(new TypeInsnNode(Opcodes.NEW,
074                                      MethodDifferentiator.DP_NAME)); // => o,
075            list.add(new InsnNode(Opcodes.DUP));                      // => o, o
076            list.add(new VarInsnNode(Opcodes.DLOAD, 1));              // => o, o, a0
077            list.add(new VarInsnNode(Opcodes.DLOAD, 3));              // => o, o, a0, a1
078            list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
079                                        MethodDifferentiator.DP_NAME,
080                                        "<init>", "(DD)V"));          // => dp
081            list.add(new InsnNode(Opcodes.ARETURN));                  // =>
082            return list;
083    
084        }
085    
086    }