rusty-javac 0.2.3

A Java compiler written in Rust.
Documentation
use crate::bytecode::codegen::CodegenCtx;
use crate::bytecode::expr_gen::{expr_ty, gen_expr};
use crate::classfile::{Label, MethodWriter};
use crate::hir::{BinaryOp, Body, Expr, ExprId, UnaryOp};
use crate::ty::Ty;
use rust_asm::opcodes;

pub(crate) fn emit_jump_if_false(
    mw: &mut MethodWriter,
    ctx: &mut CodegenCtx,
    body: &Body,
    expr_id: ExprId,
    target: Label,
) {
    match &body.exprs[expr_id] {
        Expr::BoolLiteral(false) => mw.visit_jump_insn(opcodes::GOTO, target),
        Expr::BoolLiteral(true) => {}
        Expr::Unary {
            op: UnaryOp::Not,
            operand,
        } => emit_jump_if_true(mw, ctx, body, *operand, target),
        Expr::Binary {
            op: BinaryOp::AndAnd,
            left,
            right,
        } => {
            emit_jump_if_false(mw, ctx, body, *left, target);
            emit_jump_if_false(mw, ctx, body, *right, target);
        }
        Expr::Binary {
            op: BinaryOp::OrOr,
            left,
            right,
        } => {
            let true_label = Label::new();
            emit_jump_if_true(mw, ctx, body, *left, true_label);
            emit_jump_if_false(mw, ctx, body, *right, target);
            mw.visit_label(true_label);
        }
        Expr::Binary { op, left, right } if is_comparison(op) => {
            emit_comparison_branch(mw, ctx, body, op, *left, *right, BranchSense::False, target);
        }
        _ => {
            gen_expr(mw, ctx, body, expr_id);
            mw.visit_jump_insn(opcodes::IFEQ, target);
        }
    }
}

pub(crate) fn emit_jump_if_true(
    mw: &mut MethodWriter,
    ctx: &mut CodegenCtx,
    body: &Body,
    expr_id: ExprId,
    target: Label,
) {
    match &body.exprs[expr_id] {
        Expr::BoolLiteral(true) => mw.visit_jump_insn(opcodes::GOTO, target),
        Expr::BoolLiteral(false) => {}
        Expr::Unary {
            op: UnaryOp::Not,
            operand,
        } => emit_jump_if_false(mw, ctx, body, *operand, target),
        Expr::Binary {
            op: BinaryOp::AndAnd,
            left,
            right,
        } => {
            let false_label = Label::new();
            emit_jump_if_false(mw, ctx, body, *left, false_label);
            emit_jump_if_true(mw, ctx, body, *right, target);
            mw.visit_label(false_label);
        }
        Expr::Binary {
            op: BinaryOp::OrOr,
            left,
            right,
        } => {
            emit_jump_if_true(mw, ctx, body, *left, target);
            emit_jump_if_true(mw, ctx, body, *right, target);
        }
        Expr::Binary { op, left, right } if is_comparison(op) => {
            emit_comparison_branch(mw, ctx, body, op, *left, *right, BranchSense::True, target);
        }
        _ => {
            gen_expr(mw, ctx, body, expr_id);
            mw.visit_jump_insn(opcodes::IFNE, target);
        }
    }
}

#[derive(Clone, Copy)]
enum BranchSense {
    True,
    False,
}

#[allow(clippy::too_many_arguments)]
fn emit_comparison_branch(
    mw: &mut MethodWriter,
    ctx: &mut CodegenCtx,
    body: &Body,
    op: &BinaryOp,
    left: ExprId,
    right: ExprId,
    sense: BranchSense,
    target: Label,
) {
    let left_ty = expr_ty(ctx, body, left);
    gen_expr(mw, ctx, body, left);
    gen_expr(mw, ctx, body, right);
    match left_ty.erasure() {
        Ty::Long => {
            mw.visit_insn(opcodes::LCMP);
            mw.visit_jump_insn(single_value_branch(op, sense), target);
        }
        Ty::Float => {
            mw.visit_insn(opcodes::FCMPG);
            mw.visit_jump_insn(single_value_branch(op, sense), target);
        }
        Ty::Double => {
            mw.visit_insn(opcodes::DCMPG);
            mw.visit_jump_insn(single_value_branch(op, sense), target);
        }
        Ty::Class(_) | Ty::Array(_) => {
            mw.visit_jump_insn(reference_branch(op, sense), target);
        }
        _ => {
            mw.visit_jump_insn(int_branch(op, sense), target);
        }
    }
}

fn is_comparison(op: &BinaryOp) -> bool {
    matches!(
        op,
        BinaryOp::Eq | BinaryOp::Ne | BinaryOp::Lt | BinaryOp::Gt | BinaryOp::Le | BinaryOp::Ge
    )
}

fn int_branch(op: &BinaryOp, sense: BranchSense) -> u8 {
    match (op, sense) {
        (BinaryOp::Eq, BranchSense::True) | (BinaryOp::Ne, BranchSense::False) => {
            opcodes::IF_ICMPEQ
        }
        (BinaryOp::Ne, BranchSense::True) | (BinaryOp::Eq, BranchSense::False) => {
            opcodes::IF_ICMPNE
        }
        (BinaryOp::Lt, BranchSense::True) | (BinaryOp::Ge, BranchSense::False) => {
            opcodes::IF_ICMPLT
        }
        (BinaryOp::Gt, BranchSense::True) | (BinaryOp::Le, BranchSense::False) => {
            opcodes::IF_ICMPGT
        }
        (BinaryOp::Le, BranchSense::True) | (BinaryOp::Gt, BranchSense::False) => {
            opcodes::IF_ICMPLE
        }
        (BinaryOp::Ge, BranchSense::True) | (BinaryOp::Lt, BranchSense::False) => {
            opcodes::IF_ICMPGE
        }
        _ => opcodes::IF_ICMPNE,
    }
}

fn single_value_branch(op: &BinaryOp, sense: BranchSense) -> u8 {
    match (op, sense) {
        (BinaryOp::Eq, BranchSense::True) | (BinaryOp::Ne, BranchSense::False) => opcodes::IFEQ,
        (BinaryOp::Ne, BranchSense::True) | (BinaryOp::Eq, BranchSense::False) => opcodes::IFNE,
        (BinaryOp::Lt, BranchSense::True) | (BinaryOp::Ge, BranchSense::False) => opcodes::IFLT,
        (BinaryOp::Gt, BranchSense::True) | (BinaryOp::Le, BranchSense::False) => opcodes::IFGT,
        (BinaryOp::Le, BranchSense::True) | (BinaryOp::Gt, BranchSense::False) => opcodes::IFLE,
        (BinaryOp::Ge, BranchSense::True) | (BinaryOp::Lt, BranchSense::False) => opcodes::IFGE,
        _ => opcodes::IFNE,
    }
}

fn reference_branch(op: &BinaryOp, sense: BranchSense) -> u8 {
    match (op, sense) {
        (BinaryOp::Eq, BranchSense::True) | (BinaryOp::Ne, BranchSense::False) => {
            opcodes::IF_ACMPEQ
        }
        (BinaryOp::Ne, BranchSense::True) | (BinaryOp::Eq, BranchSense::False) => {
            opcodes::IF_ACMPNE
        }
        _ => opcodes::IF_ACMPNE,
    }
}