pub unsafe fn gen_binop(
    builder: LLVMBuilderRef,
    op: BinOpKind,
    left: LLVMValueRef,
    right: LLVMValueRef,
    ty: &Type,
) -> WeldResult<LLVMValueRef> {
    use self::llvm_sys::LLVMIntPredicate::*;
    use self::llvm_sys::LLVMRealPredicate::*;
    use crate::ast::BinOpKind::*;
    use crate::ast::Type::*;
    let name = c_str!("");
    let result = match *ty {
        Scalar(s) | Simd(s) => match op {
            Add if s.is_integer() => LLVMBuildAdd(builder, left, right, name),
            Add if s.is_float() => LLVMBuildFAdd(builder, left, right, name),

            Subtract if s.is_integer() => LLVMBuildSub(builder, left, right, name),
            Subtract if s.is_float() => LLVMBuildFSub(builder, left, right, name),

            Multiply if s.is_integer() => LLVMBuildMul(builder, left, right, name),
            Multiply if s.is_float() => LLVMBuildFMul(builder, left, right, name),

            Divide if s.is_signed_integer() => LLVMBuildSDiv(builder, left, right, name),
            Divide if s.is_unsigned_integer() => LLVMBuildUDiv(builder, left, right, name),
            Divide if s.is_float() => LLVMBuildFDiv(builder, left, right, name),

            Modulo if s.is_signed_integer() => LLVMBuildSRem(builder, left, right, name),
            Modulo if s.is_unsigned_integer() => LLVMBuildURem(builder, left, right, name),
            Modulo if s.is_float() => LLVMBuildFRem(builder, left, right, name),

            Equal if s.is_integer() || s.is_bool() => {
                LLVMBuildICmp(builder, LLVMIntEQ, left, right, name)
            }
            Equal if s.is_float() => LLVMBuildFCmp(builder, LLVMRealOEQ, left, right, name),

            NotEqual if s.is_integer() || s.is_bool() => {
                LLVMBuildICmp(builder, LLVMIntNE, left, right, name)
            }
            NotEqual if s.is_float() => LLVMBuildFCmp(builder, LLVMRealONE, left, right, name),

            LessThan if s.is_signed_integer() => {
                LLVMBuildICmp(builder, LLVMIntSLT, left, right, name)
            }
            LessThan if s.is_unsigned_integer() => {
                LLVMBuildICmp(builder, LLVMIntULT, left, right, name)
            }
            LessThan if s.is_float() => LLVMBuildFCmp(builder, LLVMRealOLT, left, right, name),

            LessThanOrEqual if s.is_signed_integer() => {
                LLVMBuildICmp(builder, LLVMIntSLE, left, right, name)
            }
            LessThanOrEqual if s.is_unsigned_integer() => {
                LLVMBuildICmp(builder, LLVMIntULE, left, right, name)
            }
            LessThanOrEqual if s.is_float() => {
                LLVMBuildFCmp(builder, LLVMRealOLE, left, right, name)
            }

            GreaterThan if s.is_signed_integer() => {
                LLVMBuildICmp(builder, LLVMIntSGT, left, right, name)
            }
            GreaterThan if s.is_unsigned_integer() => {
                LLVMBuildICmp(builder, LLVMIntUGT, left, right, name)
            }
            GreaterThan if s.is_float() => LLVMBuildFCmp(builder, LLVMRealOGT, left, right, name),

            GreaterThanOrEqual if s.is_signed_integer() => {
                LLVMBuildICmp(builder, LLVMIntSGE, left, right, name)
            }
            GreaterThanOrEqual if s.is_unsigned_integer() => {
                LLVMBuildICmp(builder, LLVMIntUGE, left, right, name)
            }
            GreaterThanOrEqual if s.is_float() => {
                LLVMBuildFCmp(builder, LLVMRealOGE, left, right, name)
            }

            LogicalAnd if s.is_bool() => LLVMBuildAnd(builder, left, right, name),
            BitwiseAnd if s.is_integer() || s.is_bool() => LLVMBuildAnd(builder, left, right, name),

            LogicalOr if s.is_bool() => LLVMBuildOr(builder, left, right, name),
            BitwiseOr if s.is_integer() || s.is_bool() => LLVMBuildOr(builder, left, right, name),

            Xor if s.is_integer() || s.is_bool() => LLVMBuildXor(builder, left, right, name),

            Max => {
                let compare = gen_binop(builder, GreaterThanOrEqual, left, right, ty)?;
                LLVMBuildSelect(builder, compare, left, right, c_str!(""))
            }

            Min => {
                let compare = gen_binop(builder, LessThanOrEqual, left, right, ty)?;
                LLVMBuildSelect(builder, compare, left, right, c_str!(""))
            }

            _ => return compile_err!("Unsupported binary op: {} on {}", op, ty),
        },
        _ => return compile_err!("Unsupported binary op: {} on {}", op, ty),
    };
    Ok(result)
}
Generates a binary op instruction without intrinsics.

This function supports code generation for both scalar and SIMD values.

# Return Types

If `op.is_comparison()` is true, this function returns a value with type `i1`. Otherwise, this
function returns a value of type `LLVMTypeOf(left)`.
weld/src/codegen/llvm2/numeric.rs