/* ###
 * IP: GHIDRA
 *
 * Licensed 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 ghidra.pcode.emu.jit.gen.op;

import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;

import ghidra.lifecycle.Unfinished;
import ghidra.pcode.emu.jit.analysis.JitControlFlowModel.JitBlock;
import ghidra.pcode.emu.jit.analysis.JitType;
import ghidra.pcode.emu.jit.analysis.JitType.*;
import ghidra.pcode.emu.jit.gen.JitCodeGenerator;
import ghidra.pcode.emu.jit.gen.type.TypeConversions;
import ghidra.pcode.emu.jit.op.JitFloatTestOp;

/**
 * An extension for float comparison operators
 * 
 * @param <T> the class of p-code op node in the use-def graph
 */
public interface CompareFloatOpGen<T extends JitFloatTestOp> extends FloatBinOpGen<T> {

	/**
	 * The JVM opcode to perform the comparison with float operands on the stack.
	 * 
	 * @return the opcode
	 */
	int fcmpOpcode();

	/**
	 * The JVM opcode to perform the comparison with double operands on the stack.
	 * 
	 * @return the opcode
	 */
	int dcmpOpcode();

	/**
	 * The JVM opcode to perform the conditional jump.
	 * 
	 * <p>
	 * The condition should correspond to the true case of the p-code operator.
	 * 
	 * @return the opcode
	 */
	int condOpcode();

	/**
	 * {@inheritDoc}
	 * 
	 * <p>
	 * This implementation reduces the need to just a few opcodes: 1) the opcode for comparing in
	 * case of JVM {@code float}, 2) the opcode for comparing in the case of JVM {@code double}, and
	 * 3) the conditional jump on the result of that comparison. First, the comparison opcode is
	 * emitted. It should result in and int &lt;0, ==0, or &gt;0 on the stack, depending on whether
	 * L&lt;R, L==R, or L&gt;R, respectively. Then the conditional jump is emitted. We place labels
	 * in an if-else pattern to place either a 1 (true) or 0 (false) value of the appropriate p-code
	 * type on the stack.
	 * 
	 * @implNote This template is consistently generated by the Java compiler (Adoptium OpenJDK 21),
	 *           despite there being possible branchless implementations. That could indicate one of
	 *           a few things: 1) the HotSpot JIT knows how to optimize this pattern, perhaps using
	 *           branchless native instructions, 2) branchless optimizations don't yield the speedup
	 *           here we might expect, or 3) they didn't care to optimize. <b>TODO</b>: Investigate
	 *           in case it's thing 3. We might like to see if branchless JVM bytecodes can improve
	 *           performance.
	 */
	@Override
	default JitType generateBinOpRunCode(JitCodeGenerator gen, T op, JitBlock block, JitType lType,
			JitType rType, MethodVisitor rv) {
		assert rType == lType;
		JitType outType = op.type().resolve(gen.getTypeModel().typeOf(op.out()));
		Label lblTrue = new Label();
		Label lblDone = new Label();
		switch (rType) {
			case FloatJitType t -> rv.visitInsn(fcmpOpcode());
			case DoubleJitType t -> rv.visitInsn(dcmpOpcode());
			case MpFloatJitType t -> Unfinished.TODO("MpFloat");
			default -> throw new AssertionError();
		}
		rv.visitJumpInsn(condOpcode(), lblTrue);
		TypeConversions.generateLdcFalse(outType, rv);
		rv.visitJumpInsn(GOTO, lblDone);
		rv.visitLabel(lblTrue);
		TypeConversions.generateLdcTrue(outType, rv);
		rv.visitLabel(lblDone);

		return outType;
	}
}
