genMachOp :: EOption -> MachOp -> [CmmExpr] -> LlvmM ExprData

-- Unary Machop
genMachOp _ op [x] = case op of

    MO_Not w ->
        let all1 = mkIntLit (widthToLlvmInt w) (-1)
        in negate (widthToLlvmInt w) all1 LM_MO_Xor

    MO_S_Neg w ->
        let all0 = mkIntLit (widthToLlvmInt w) 0
        in negate (widthToLlvmInt w) all0 LM_MO_Sub

    MO_F_Neg w ->
        let all0 = LMLitVar $ LMFloatLit (-0) (widthToLlvmFloat w)
        in negate (widthToLlvmFloat w) all0 LM_MO_FSub

    MO_SF_Conv _ w -> fiConv (widthToLlvmFloat w) LM_Sitofp
    MO_FS_Conv _ w -> fiConv (widthToLlvmInt w) LM_Fptosi

    MO_SS_Conv from to
        -> sameConv from (widthToLlvmInt to) LM_Trunc LM_Sext

    MO_UU_Conv from to
        -> sameConv from (widthToLlvmInt to) LM_Trunc LM_Zext

    MO_FF_Conv from to
        -> sameConv from (widthToLlvmFloat to) LM_Fptrunc LM_Fpext

    MO_VS_Neg len w ->
        let ty    = widthToLlvmInt w
            vecty = LMVector len ty
            all0  = LMIntLit (-0) ty
            all0s = LMLitVar $ LMVectorLit (replicate len all0)
        in negateVec vecty all0s LM_MO_Sub

    MO_VF_Neg len w ->
        let ty    = widthToLlvmFloat w
            vecty = LMVector len ty
            all0  = LMFloatLit (-0) ty
            all0s = LMLitVar $ LMVectorLit (replicate len all0)
        in negateVec vecty all0s LM_MO_FSub

    MO_AlignmentCheck _ _ -> panic "-falignment-sanitisation is not supported by -fllvm"

    -- Handle unsupported cases explicitly so we get a warning
    -- of missing case when new MachOps added
    MO_Add _          -> panicOp
    MO_Mul _          -> panicOp
    MO_Sub _          -> panicOp
    MO_S_MulMayOflo _ -> panicOp
    MO_S_Quot _       -> panicOp
    MO_S_Rem _        -> panicOp
    MO_U_MulMayOflo _ -> panicOp
    MO_U_Quot _       -> panicOp
    MO_U_Rem _        -> panicOp

    MO_Eq  _          -> panicOp
    MO_Ne  _          -> panicOp
    MO_S_Ge _         -> panicOp
    MO_S_Gt _         -> panicOp
    MO_S_Le _         -> panicOp
    MO_S_Lt _         -> panicOp
    MO_U_Ge _         -> panicOp
    MO_U_Gt _         -> panicOp
    MO_U_Le _         -> panicOp
    MO_U_Lt _         -> panicOp

    MO_F_Add        _ -> panicOp
    MO_F_Sub        _ -> panicOp
    MO_F_Mul        _ -> panicOp
    MO_F_Quot       _ -> panicOp
    MO_F_Eq         _ -> panicOp
    MO_F_Ne         _ -> panicOp
    MO_F_Ge         _ -> panicOp
    MO_F_Gt         _ -> panicOp
    MO_F_Le         _ -> panicOp
    MO_F_Lt         _ -> panicOp

    MO_And          _ -> panicOp
    MO_Or           _ -> panicOp
    MO_Xor          _ -> panicOp
    MO_Shl          _ -> panicOp
    MO_U_Shr        _ -> panicOp
    MO_S_Shr        _ -> panicOp

    MO_V_Insert   _ _ -> panicOp
    MO_V_Extract  _ _ -> panicOp

    MO_V_Add      _ _ -> panicOp
    MO_V_Sub      _ _ -> panicOp
    MO_V_Mul      _ _ -> panicOp

    MO_VS_Quot    _ _ -> panicOp
    MO_VS_Rem     _ _ -> panicOp

    MO_VU_Quot    _ _ -> panicOp
    MO_VU_Rem     _ _ -> panicOp

    MO_VF_Insert  _ _ -> panicOp
    MO_VF_Extract _ _ -> panicOp

    MO_VF_Add     _ _ -> panicOp
    MO_VF_Sub     _ _ -> panicOp
    MO_VF_Mul     _ _ -> panicOp
    MO_VF_Quot    _ _ -> panicOp

    where
        negate ty v2 negOp = do
            (vx, stmts, top) <- exprToVar x
            (v1, s1) <- doExpr ty $ LlvmOp negOp v2 vx
            return (v1, stmts `snocOL` s1, top)

        negateVec ty v2 negOp = do
            (vx, stmts1, top) <- exprToVar x
            ([vx'], stmts2) <- castVars [(vx, ty)]
            (v1, s1) <- doExpr ty $ LlvmOp negOp v2 vx'
            return (v1, stmts1 `appOL` stmts2 `snocOL` s1, top)

        fiConv ty convOp = do
            (vx, stmts, top) <- exprToVar x
            (v1, s1) <- doExpr ty $ Cast convOp vx ty
            return (v1, stmts `snocOL` s1, top)

        sameConv from ty reduce expand = do
            x'@(vx, stmts, top) <- exprToVar x
            let sameConv' op = do
                    (v1, s1) <- doExpr ty $ Cast op vx ty
                    return (v1, stmts `snocOL` s1, top)
            dflags <- getDynFlags
            let toWidth = llvmWidthInBits dflags ty
            -- LLVM doesn't like trying to convert to same width, so
            -- need to check for that as we do get Cmm code doing it.
            case widthInBits from  of
                 w | w < toWidth -> sameConv' expand
                 w | w > toWidth -> sameConv' reduce
                 _w              -> return x'

        panicOp = panic $ "LLVM.CodeGen.genMachOp: non unary op encountered"
                       ++ "with one argument! (" ++ show op ++ ")"

-- Handle GlobalRegs pointers
genMachOp :: EOption -> MachOp -> [CmmExpr] -> LlvmM ExprData
genMachOp _ op [x] = case op of

    MO_Not w ->
        let all1 = mkIntLit (widthToLlvmInt w) (-1)
        in negate (widthToLlvmInt w) all1 LM_MO_Xor

    MO_S_Neg w ->
        let all0 = mkIntLit (widthToLlvmInt w) 0
        in negate (widthToLlvmInt w) all0 LM_MO_Sub

    MO_F_Neg w ->
        let all0 = LMLitVar $ LMFloatLit (-0) (widthToLlvmFloat w)
        in negate (widthToLlvmFloat w) all0 LM_MO_FSub

    MO_SF_Conv _ w -> fiConv (widthToLlvmFloat w) LM_Sitofp
    MO_FS_Conv _ w -> fiConv (widthToLlvmInt w) LM_Fptosi

    MO_SS_Conv from to
        -> sameConv from (widthToLlvmInt to) LM_Trunc LM_Sext

    MO_UU_Conv from to
        -> sameConv from (widthToLlvmInt to) LM_Trunc LM_Zext

    MO_FF_Conv from to
        -> sameConv from (widthToLlvmFloat to) LM_Fptrunc LM_Fpext

    MO_VS_Neg len w ->
        let ty    = widthToLlvmInt w
            vecty = LMVector len ty
            all0  = LMIntLit (-0) ty
            all0s = LMLitVar $ LMVectorLit (replicate len all0)
        in negateVec vecty all0s LM_MO_Sub

    MO_VF_Neg len w ->
        let ty    = widthToLlvmFloat w
            vecty = LMVector len ty
            all0  = LMFloatLit (-0) ty
            all0s = LMLitVar $ LMVectorLit (replicate len all0)
        in negateVec vecty all0s LM_MO_FSub

    MO_AlignmentCheck _ _ -> panic "-falignment-sanitisation is not supported by -fllvm"

    -- Handle unsupported cases explicitly so we get a warning
    -- of missing case when new MachOps added
    MO_Add _          -> panicOp
    MO_Mul _          -> panicOp
    MO_Sub _          -> panicOp
    MO_S_MulMayOflo _ -> panicOp
    MO_S_Quot _       -> panicOp
    MO_S_Rem _        -> panicOp
    MO_U_MulMayOflo _ -> panicOp
    MO_U_Quot _       -> panicOp
    MO_U_Rem _        -> panicOp

    MO_Eq  _          -> panicOp
    MO_Ne  _          -> panicOp
    MO_S_Ge _         -> panicOp
    MO_S_Gt _         -> panicOp
    MO_S_Le _         -> panicOp
    MO_S_Lt _         -> panicOp
    MO_U_Ge _         -> panicOp
    MO_U_Gt _         -> panicOp
    MO_U_Le _         -> panicOp
    MO_U_Lt _         -> panicOp

    MO_F_Add        _ -> panicOp
    MO_F_Sub        _ -> panicOp
    MO_F_Mul        _ -> panicOp
    MO_F_Quot       _ -> panicOp
    MO_F_Eq         _ -> panicOp
    MO_F_Ne         _ -> panicOp
    MO_F_Ge         _ -> panicOp
    MO_F_Gt         _ -> panicOp
    MO_F_Le         _ -> panicOp
    MO_F_Lt         _ -> panicOp

    MO_And          _ -> panicOp
    MO_Or           _ -> panicOp
    MO_Xor          _ -> panicOp
    MO_Shl          _ -> panicOp
    MO_U_Shr        _ -> panicOp
    MO_S_Shr        _ -> panicOp

    MO_V_Insert   _ _ -> panicOp
    MO_V_Extract  _ _ -> panicOp

    MO_V_Add      _ _ -> panicOp
    MO_V_Sub      _ _ -> panicOp
    MO_V_Mul      _ _ -> panicOp

    MO_VS_Quot    _ _ -> panicOp
    MO_VS_Rem     _ _ -> panicOp

    MO_VU_Quot    _ _ -> panicOp
    MO_VU_Rem     _ _ -> panicOp

    MO_VF_Insert  _ _ -> panicOp
    MO_VF_Extract _ _ -> panicOp

    MO_VF_Add     _ _ -> panicOp
    MO_VF_Sub     _ _ -> panicOp
    MO_VF_Mul     _ _ -> panicOp
    MO_VF_Quot    _ _ -> panicOp

    where
        negate ty v2 negOp = do
            (vx, stmts, top) <- exprToVar x
            (v1, s1) <- doExpr ty $ LlvmOp negOp v2 vx
            return (v1, stmts `snocOL` s1, top)

        negateVec ty v2 negOp = do
            (vx, stmts1, top) <- exprToVar x
            ([vx'], stmts2) <- castVars [(vx, ty)]
            (v1, s1) <- doExpr ty $ LlvmOp negOp v2 vx'
            return (v1, stmts1 `appOL` stmts2 `snocOL` s1, top)

        fiConv ty convOp = do
            (vx, stmts, top) <- exprToVar x
            (v1, s1) <- doExpr ty $ Cast convOp vx ty
            return (v1, stmts `snocOL` s1, top)

        sameConv from ty reduce expand = do
            x'@(vx, stmts, top) <- exprToVar x
            let sameConv' op = do
                    (v1, s1) <- doExpr ty $ Cast op vx ty
                    return (v1, stmts `snocOL` s1, top)
            dflags <- getDynFlags
            let toWidth = llvmWidthInBits dflags ty
            -- LLVM doesn't like trying to convert to same width, so
            -- need to check for that as we do get Cmm code doing it.
            case widthInBits from  of
                 w | w < toWidth -> sameConv' expand
                 w | w > toWidth -> sameConv' reduce
                 _w              -> return x'

        panicOp = panic $ "LLVM.CodeGen.genMachOp: non unary op encountered"
                       ++ "with one argument! (" ++ show op ++ ")"

-- Handle GlobalRegs pointers
genMachOp _ op [x] = case op of

    MO_Not w ->
        let all1 = mkIntLit (widthToLlvmInt w) (-1)
        in negate (widthToLlvmInt w) all1 LM_MO_Xor

    MO_S_Neg w ->
        let all0 = mkIntLit (widthToLlvmInt w) 0
        in negate (widthToLlvmInt w) all0 LM_MO_Sub

    MO_F_Neg w ->
        let all0 = LMLitVar $ LMFloatLit (-0) (widthToLlvmFloat w)
        in negate (widthToLlvmFloat w) all0 LM_MO_FSub

    MO_SF_Conv _ w -> fiConv (widthToLlvmFloat w) LM_Sitofp
    MO_FS_Conv _ w -> fiConv (widthToLlvmInt w) LM_Fptosi

    MO_SS_Conv from to
        -> sameConv from (widthToLlvmInt to) LM_Trunc LM_Sext

    MO_UU_Conv from to
        -> sameConv from (widthToLlvmInt to) LM_Trunc LM_Zext

    MO_FF_Conv from to
        -> sameConv from (widthToLlvmFloat to) LM_Fptrunc LM_Fpext

    MO_VS_Neg len w ->
        let ty    = widthToLlvmInt w
            vecty = LMVector len ty
            all0  = LMIntLit (-0) ty
            all0s = LMLitVar $ LMVectorLit (replicate len all0)
        in negateVec vecty all0s LM_MO_Sub

    MO_VF_Neg len w ->
        let ty    = widthToLlvmFloat w
            vecty = LMVector len ty
            all0  = LMFloatLit (-0) ty
            all0s = LMLitVar $ LMVectorLit (replicate len all0)
        in negateVec vecty all0s LM_MO_FSub

    MO_AlignmentCheck _ _ -> panic "-falignment-sanitisation is not supported by -fllvm"

    -- Handle unsupported cases explicitly so we get a warning
    -- of missing case when new MachOps added
    MO_Add _          -> panicOp
    MO_Mul _          -> panicOp
    MO_Sub _          -> panicOp
    MO_S_MulMayOflo _ -> panicOp
    MO_S_Quot _       -> panicOp
    MO_S_Rem _        -> panicOp
    MO_U_MulMayOflo _ -> panicOp
    MO_U_Quot _       -> panicOp
    MO_U_Rem _        -> panicOp

    MO_Eq  _          -> panicOp
    MO_Ne  _          -> panicOp
    MO_S_Ge _         -> panicOp
    MO_S_Gt _         -> panicOp
    MO_S_Le _         -> panicOp
    MO_S_Lt _         -> panicOp
    MO_U_Ge _         -> panicOp
    MO_U_Gt _         -> panicOp
    MO_U_Le _         -> panicOp
    MO_U_Lt _         -> panicOp

    MO_F_Add        _ -> panicOp
    MO_F_Sub        _ -> panicOp
    MO_F_Mul        _ -> panicOp
    MO_F_Quot       _ -> panicOp
    MO_F_Eq         _ -> panicOp
    MO_F_Ne         _ -> panicOp
    MO_F_Ge         _ -> panicOp
    MO_F_Gt         _ -> panicOp
    MO_F_Le         _ -> panicOp
    MO_F_Lt         _ -> panicOp

    MO_And          _ -> panicOp
    MO_Or           _ -> panicOp
    MO_Xor          _ -> panicOp
    MO_Shl          _ -> panicOp
    MO_U_Shr        _ -> panicOp
    MO_S_Shr        _ -> panicOp

    MO_V_Insert   _ _ -> panicOp
    MO_V_Extract  _ _ -> panicOp

    MO_V_Add      _ _ -> panicOp
    MO_V_Sub      _ _ -> panicOp
    MO_V_Mul      _ _ -> panicOp

    MO_VS_Quot    _ _ -> panicOp
    MO_VS_Rem     _ _ -> panicOp

    MO_VU_Quot    _ _ -> panicOp
    MO_VU_Rem     _ _ -> panicOp

    MO_VF_Insert  _ _ -> panicOp
    MO_VF_Extract _ _ -> panicOp

    MO_VF_Add     _ _ -> panicOp
    MO_VF_Sub     _ _ -> panicOp
    MO_VF_Mul     _ _ -> panicOp
    MO_VF_Quot    _ _ -> panicOp

    where
        negate ty v2 negOp = do
            (vx, stmts, top) <- exprToVar x
            (v1, s1) <- doExpr ty $ LlvmOp negOp v2 vx
            return (v1, stmts `snocOL` s1, top)

        negateVec ty v2 negOp = do
            (vx, stmts1, top) <- exprToVar x
            ([vx'], stmts2) <- castVars [(vx, ty)]
            (v1, s1) <- doExpr ty $ LlvmOp negOp v2 vx'
            return (v1, stmts1 `appOL` stmts2 `snocOL` s1, top)

        fiConv ty convOp = do
            (vx, stmts, top) <- exprToVar x
            (v1, s1) <- doExpr ty $ Cast convOp vx ty
            return (v1, stmts `snocOL` s1, top)

        sameConv from ty reduce expand = do
            x'@(vx, stmts, top) <- exprToVar x
            let sameConv' op = do
                    (v1, s1) <- doExpr ty $ Cast op vx ty
                    return (v1, stmts `snocOL` s1, top)
            dflags <- getDynFlags
            let toWidth = llvmWidthInBits dflags ty
            -- LLVM doesn't like trying to convert to same width, so
            -- need to check for that as we do get Cmm code doing it.
            case widthInBits from  of
                 w | w < toWidth -> sameConv' expand
                 w | w > toWidth -> sameConv' reduce
                 _w              -> return x'

        panicOp = panic $ "LLVM.CodeGen.genMachOp: non unary op encountered"
                       ++ "with one argument! (" ++ show op ++ ")"

-- Handle GlobalRegs pointers