genCall (PrimTarget (MO_U_Mul2 w)) [dstH, dstL] [lhs, rhs] = runStmtsDecls $ do
    let width = widthToLlvmInt w
        bitWidth = widthInBits w
        width2x = LMInt (bitWidth * 2)
    -- First zero-extend the operands ('mul' instruction requires the operands
    -- and the result to be of the same type). Note that we don't use 'castVars'
    -- because it tries to do LM_Sext.
    lhsVar <- exprToVarW lhs
    rhsVar <- exprToVarW rhs
    lhsExt <- doExprW width2x $ Cast LM_Zext lhsVar width2x
    rhsExt <- doExprW width2x $ Cast LM_Zext rhsVar width2x
    -- Do the actual multiplication (note that the result is also 2x width).
    retV <- doExprW width2x $ LlvmOp LM_MO_Mul lhsExt rhsExt
    -- Extract the lower bits of the result into retL.
    retL <- doExprW width $ Cast LM_Trunc retV width
    -- Now we right-shift the higher bits by width.
    let widthLlvmLit = LMLitVar $ LMIntLit (fromIntegral bitWidth) width
    retShifted <- doExprW width2x $ LlvmOp LM_MO_LShr retV widthLlvmLit
    -- And extract them into retH.
    retH <- doExprW width $ Cast LM_Trunc retShifted width
    dstRegL <- getCmmRegW (CmmLocal dstL)
    dstRegH <- getCmmRegW (CmmLocal dstH)
    statement $ Store retL dstRegL
    statement $ Store retH dstRegH

-- MO_U_QuotRem2 is another case we handle by widening the registers to double
-- the width and use normal LLVM instructions (similarly to the MO_U_Mul2). The
-- main difference here is that we need to combine two words into one register
-- and then use both 'udiv' and 'urem' instructions to compute the result.
genCall (PrimTarget (MO_U_Mul2 w)) [dstH, dstL] [lhs, rhs] = runStmtsDecls $ do
    let width = widthToLlvmInt w
        bitWidth = widthInBits w
        width2x = LMInt (bitWidth * 2)
    -- First zero-extend the operands ('mul' instruction requires the operands
    -- and the result to be of the same type). Note that we don't use 'castVars'
    -- because it tries to do LM_Sext.
    lhsVar <- exprToVarW lhs
    rhsVar <- exprToVarW rhs
    lhsExt <- doExprW width2x $ Cast LM_Zext lhsVar width2x
    rhsExt <- doExprW width2x $ Cast LM_Zext rhsVar width2x
    -- Do the actual multiplication (note that the result is also 2x width).
    retV <- doExprW width2x $ LlvmOp LM_MO_Mul lhsExt rhsExt
    -- Extract the lower bits of the result into retL.
    retL <- doExprW width $ Cast LM_Trunc retV width
    -- Now we right-shift the higher bits by width.
    let widthLlvmLit = LMLitVar $ LMIntLit (fromIntegral bitWidth) width
    retShifted <- doExprW width2x $ LlvmOp LM_MO_LShr retV widthLlvmLit
    -- And extract them into retH.
    retH <- doExprW width $ Cast LM_Trunc retShifted width
    dstRegL <- getCmmRegW (CmmLocal dstL)
    dstRegH <- getCmmRegW (CmmLocal dstH)
    statement $ Store retL dstRegL
    statement $ Store retH dstRegH

-- MO_U_QuotRem2 is another case we handle by widening the registers to double
-- the width and use normal LLVM instructions (similarly to the MO_U_Mul2). The
-- main difference here is that we need to combine two words into one register
-- and then use both 'udiv' and 'urem' instructions to compute the result.
genCall (PrimTarget (MO_U_Mul2 w)) [dstH, dstL] [lhs, rhs] = runStmtsDecls $ do
    let width = widthToLlvmInt w
        bitWidth = widthInBits w
        width2x = LMInt (bitWidth * 2)
    -- First zero-extend the operands ('mul' instruction requires the operands
    -- and the result to be of the same type). Note that we don't use 'castVars'
    -- because it tries to do LM_Sext.
    lhsVar <- exprToVarW lhs
    rhsVar <- exprToVarW rhs
    lhsExt <- doExprW width2x $ Cast LM_Zext lhsVar width2x
    rhsExt <- doExprW width2x $ Cast LM_Zext rhsVar width2x
    -- Do the actual multiplication (note that the result is also 2x width).
    retV <- doExprW width2x $ LlvmOp LM_MO_Mul lhsExt rhsExt
    -- Extract the lower bits of the result into retL.
    retL <- doExprW width $ Cast LM_Trunc retV width
    -- Now we right-shift the higher bits by width.
    let widthLlvmLit = LMLitVar $ LMIntLit (fromIntegral bitWidth) width
    retShifted <- doExprW width2x $ LlvmOp LM_MO_LShr retV widthLlvmLit
    -- And extract them into retH.
    retH <- doExprW width $ Cast LM_Trunc retShifted width
    dstRegL <- getCmmRegW (CmmLocal dstL)
    dstRegH <- getCmmRegW (CmmLocal dstH)
    statement $ Store retL dstRegL
    statement $ Store retH dstRegH

-- MO_U_QuotRem2 is another case we handle by widening the registers to double
-- the width and use normal LLVM instructions (similarly to the MO_U_Mul2). The
-- main difference here is that we need to combine two words into one register
-- and then use both 'udiv' and 'urem' instructions to compute the result.