https://github.com/erichkeane created https://github.com/llvm/llvm-project/pull/217710
This should be the rest of the lowering for fixed point, so it removes the `MissingFeatures` listing. It is a faithful re-implementation of classic-lowering as best I can tell, except the create<COMPARE> ops are combined. >From 22e2de1539a142b5d06859b786ae9140de4d9e1d Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Wed, 19 Aug 2026 07:14:29 -0700 Subject: [PATCH] [CIR] Implement fixed-point arithmetic lowering This should be the rest of the lowering for fixed point, so it removes the `MissingFeatures` listing. It is a faithful re-implementation of classic-lowering as best I can tell, except the create<COMPARE> ops are combined. --- clang/include/clang/CIR/MissingFeatures.h | 1 - clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 391 ++++++++-- clang/test/CIR/CodeGen/fixed-point-arith.cpp | 759 +++++++++++++++++++ 3 files changed, 1100 insertions(+), 51 deletions(-) create mode 100644 clang/test/CIR/CodeGen/fixed-point-arith.cpp diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 041820faa27cc..ea14bf20713b2 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -318,7 +318,6 @@ struct MissingFeatures { static bool scalableVectors() { return false; } static bool unsizedTypes() { return false; } static bool vectorType() { return false; } - static bool fixedPointType() { return false; } // Future CIR operations static bool callOp() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 73978b4e5cffe..887d392f7883f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -615,6 +615,8 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { mlir::Value VisitUnaryPreInc(const UnaryOperator *e) { return VisitUnaryPrePostIncDec(e); } + + mlir::Value emitFixedPointIncDec(const UnaryOperator *e, mlir::Value value, QualType type); mlir::Value emitScalarPrePostIncDec(const UnaryOperator *e, LValue lv) { if (cgf.getLangOpts().OpenMP) cgf.cgm.errorNYI(e->getSourceRange(), "inc/dec OpenMP"); @@ -727,8 +729,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { return {}; } } else if (type->isFixedPointType()) { - cgf.cgm.errorNYI(e->getSourceRange(), "Unary inc/dec other fixed point"); - return {}; + value = emitFixedPointIncDec(e, value, type); } else { assert(type->castAs<ObjCObjectPointerType>()); cgf.cgm.errorNYI(e->getSourceRange(), "Unary inc/dec ObjectiveC pointer"); @@ -1103,6 +1104,8 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { mlir::Value emitXor(const BinOpInfo &ops); mlir::Value emitOr(const BinOpInfo &ops); + mlir::Value emitFixedPointBinOp(const BinOpInfo &ops); + LValue emitCompoundAssignLValue( const CompoundAssignOperator *e, mlir::Value (ScalarExprEmitter::*f)(const BinOpInfo &), @@ -1157,6 +1160,25 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { HANDLEBINOP(Or) #undef HANDLEBINOP + cir::CmpOpKind clangCmpToCIRCmp(clang::BinaryOperatorKind clangCmp) { + switch (clangCmp) { + case BO_LT: + return cir::CmpOpKind::lt; + case BO_GT: + return cir::CmpOpKind::gt; + case BO_LE: + return cir::CmpOpKind::le; + case BO_GE: + return cir::CmpOpKind::ge; + case BO_EQ: + return cir::CmpOpKind::eq; + case BO_NE: + return cir::CmpOpKind::ne; + default: + llvm_unreachable("unsupported comparison kind for cir.cmp"); + } + } + mlir::Value emitCmp(const BinaryOperator *e) { ignoreResultAssign = false; const mlir::Location loc = cgf.getLoc(e->getExprLoc()); @@ -1164,26 +1186,6 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { QualType lhsTy = e->getLHS()->getType(); QualType rhsTy = e->getRHS()->getType(); - auto clangCmpToCIRCmp = - [](clang::BinaryOperatorKind clangCmp) -> cir::CmpOpKind { - switch (clangCmp) { - case BO_LT: - return cir::CmpOpKind::lt; - case BO_GT: - return cir::CmpOpKind::gt; - case BO_LE: - return cir::CmpOpKind::le; - case BO_GE: - return cir::CmpOpKind::ge; - case BO_EQ: - return cir::CmpOpKind::eq; - case BO_NE: - return cir::CmpOpKind::ne; - default: - llvm_unreachable("unsupported comparison kind for cir.cmp"); - } - }; - cir::CmpOpKind kind = clangCmpToCIRCmp(e->getOpcode()); if (lhsTy->getAs<MemberPointerType>()) { assert(!cir::MissingFeatures::dataMemberType()); @@ -1209,9 +1211,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { boInfo.lhs, boInfo.rhs); } } else if (boInfo.isFixedPointOp()) { - assert(!cir::MissingFeatures::fixedPointType()); - cgf.cgm.errorNYI(loc, "fixed point comparisons"); - result = builder.getBool(false, loc); + result = emitFixedPointBinOp(boInfo); } else { // integers and pointers if (cgf.cgm.getCodeGenOpts().StrictVTablePointers && @@ -2168,6 +2168,168 @@ struct FixedPointBuilder { return convert(src, srcSema, dstSema, /*dstIsInteger=*/false); } + mlir::Value createAdd(mlir::Value lhs, + const llvm::FixedPointSemantics &lhsSema, + mlir::Value rhs, + const llvm::FixedPointSemantics &rhsSema) { + auto commonSema = getCommonBinopSemantic(lhsSema, rhsSema); + bool useSigned = commonSema.isSigned() || commonSema.hasUnsignedPadding(); + + mlir::Value wideLhs = createFixedToFixed(lhs, lhsSema, commonSema); + mlir::Value wideRhs = createFixedToFixed(rhs, rhsSema, commonSema); + + mlir::Value result; + if (commonSema.isSaturated()) { + result = builder.emitIntrinsicCallOp( + loc, useSigned ? "sadd.sat" : "uadd.sat", wideLhs.getType(), + mlir::ValueRange{wideLhs, wideRhs}); + } else { + result = builder.createAdd(loc, wideLhs, wideRhs); + } + + return createFixedToFixed(result, commonSema, + lhsSema.getCommonSemantics(rhsSema)); + } + + mlir::Value createSub(mlir::Value lhs, + const llvm::FixedPointSemantics &lhsSema, + mlir::Value rhs, + const llvm::FixedPointSemantics &rhsSema) { + auto commonSema = getCommonBinopSemantic(lhsSema, rhsSema); + bool useSigned = commonSema.isSigned() || commonSema.hasUnsignedPadding(); + + mlir::Value wideLhs = createFixedToFixed(lhs, lhsSema, commonSema); + mlir::Value wideRhs = createFixedToFixed(rhs, rhsSema, commonSema); + + mlir::Value result; + if (commonSema.isSaturated()) { + result = builder.emitIntrinsicCallOp( + loc, useSigned ? "ssub.sat" : "usub.sat", wideLhs.getType(), + mlir::ValueRange{wideLhs, wideRhs}); + } else { + result = builder.createSub(loc, wideLhs, wideRhs); + } + + // Subtraction can end up below 0 for padded unsigned operations, so emit + // an extra clamp in that case. + if (commonSema.isSaturated() && commonSema.hasUnsignedPadding()) { + mlir::Value zero = builder.getNullValue(result.getType(), loc); + mlir::Value ltZero = + builder.createCompare(loc, cir::CmpOpKind::lt, result, zero); + result = builder.createSelect(loc, ltZero, zero, result); + } + + return createFixedToFixed(result, commonSema, + lhsSema.getCommonSemantics(rhsSema)); + } + + mlir::Value createMul(mlir::Value lhs, + const llvm::FixedPointSemantics &lhsSema, + mlir::Value rhs, + const llvm::FixedPointSemantics &rhsSema) { + auto commonSema = getCommonBinopSemantic(lhsSema, rhsSema); + bool useSigned = commonSema.isSigned() || commonSema.hasUnsignedPadding(); + + mlir::Value wideLhs = createFixedToFixed(lhs, lhsSema, commonSema); + mlir::Value wideRhs = createFixedToFixed(rhs, rhsSema, commonSema); + + llvm::SmallString<13> intrinId; + cir::ConstantOp scale; + + if (useSigned) { + intrinId = "smul.fix"; + scale = builder.getSInt32(commonSema.getScale(), loc); + } else { + intrinId = "umul.fix"; + scale = builder.getUInt32(commonSema.getScale(), loc); + } + + if (commonSema.isSaturated()) + intrinId += ".sat"; + + mlir::Value result = + builder.emitIntrinsicCallOp(loc, intrinId, wideLhs.getType(), + mlir::ValueRange{wideLhs, wideRhs, scale}); + + return createFixedToFixed(result, commonSema, + lhsSema.getCommonSemantics(rhsSema)); + } + + mlir::Value createDiv(mlir::Value lhs, + const llvm::FixedPointSemantics &lhsSema, + mlir::Value rhs, + const llvm::FixedPointSemantics &rhsSema) { + auto commonSema = getCommonBinopSemantic(lhsSema, rhsSema); + bool useSigned = commonSema.isSigned() || commonSema.hasUnsignedPadding(); + + mlir::Value wideLhs = createFixedToFixed(lhs, lhsSema, commonSema); + mlir::Value wideRhs = createFixedToFixed(rhs, rhsSema, commonSema); + + llvm::SmallString<13> intrinId; + cir::ConstantOp scale; + + if (useSigned) { + intrinId = "sdiv.fix"; + scale = builder.getSInt32(commonSema.getScale(), loc); + } else { + intrinId = "udiv.fix"; + scale = builder.getUInt32(commonSema.getScale(), loc); + } + + if (commonSema.isSaturated()) + intrinId += ".sat"; + + mlir::Value result = + builder.emitIntrinsicCallOp(loc, intrinId, wideLhs.getType(), + mlir::ValueRange{wideLhs, wideRhs, scale}); + + return createFixedToFixed(result, commonSema, + lhsSema.getCommonSemantics(rhsSema)); + } + + mlir::Value createCmp(mlir::Value lhs, + const llvm::FixedPointSemantics &lhsSema, + mlir::Value rhs, + const llvm::FixedPointSemantics &rhsSema, + cir::CmpOpKind kind) { + auto commonSema = getCommonBinopSemantic(lhsSema, rhsSema); + + mlir::Value wideLhs = createFixedToFixed(lhs, lhsSema, commonSema); + mlir::Value wideRhs = createFixedToFixed(rhs, rhsSema, commonSema); + + return builder.createCompare(loc, kind, wideLhs, wideRhs); + } + + mlir::Value createShl(mlir::Value lhs, + const llvm::FixedPointSemantics &lhsSema, + mlir::Value rhs) { + mlir::Value result; + if (lhsSema.isSaturated()) { + // We have to cast the RHS to the matching int type, but we have to do so + // through unsigned so we can ensure we get zext. + auto rhsIntTy = mlir::cast<cir::IntType>(rhs.getType()); + auto rhsUnsignedTy = cir::IntType::get(builder.getContext(), rhsIntTy.getWidth(), /*isSigned=*/false); + + mlir::Value rhsUnsigned = + builder.createCast(cir::CastKind::integral, rhs, rhsUnsignedTy); + mlir::Value rhsResized = builder.createCast(cir::CastKind::integral, + rhsUnsigned, lhs.getType()); + + bool useSigned = lhsSema.isSigned() || lhsSema.hasUnsignedPadding(); + result = builder.emitIntrinsicCallOp( + loc, useSigned ? "sshl.sat" : "ushl.sat", lhs.getType(), + mlir::ValueRange{lhs, rhsResized}); + } else { + result = builder.createShiftLeft(loc, lhs, rhs); + } + + return result; + } + + mlir::Value createShr(mlir::Value lhs, mlir::Value rhs) { + return builder.createShiftRight(loc, lhs, rhs); + } + private: mlir::Value convert(mlir::Value src, const llvm::FixedPointSemantics &srcSema, const llvm::FixedPointSemantics &dstSema, @@ -2263,12 +2425,57 @@ struct FixedPointBuilder { assert(accommodating && "no float type for semantics?"); return accommodating; } + /// Get the common semantic for two semantics, with the added imposition that + /// saturated padded types retain the padding bit. + llvm::FixedPointSemantics + getCommonBinopSemantic(const llvm::FixedPointSemantics &lhsSema, + const llvm::FixedPointSemantics &rhsSema) { + auto c = lhsSema.getCommonSemantics(rhsSema); + bool bothPadded = + lhsSema.hasUnsignedPadding() && rhsSema.hasUnsignedPadding(); + return llvm::FixedPointSemantics( + c.getWidth() + static_cast<unsigned>(bothPadded && c.isSaturated()), + c.getScale(), c.isSigned(), c.isSaturated(), bothPadded); + } CIRGenBuilderTy &builder; mlir::Location loc; }; } // namespace +mlir::Value ScalarExprEmitter::emitFixedPointIncDec(const UnaryOperator *e, + mlir::Value value, + QualType type) { + // Fixed-point types are tricky. In some cases, it isn't possible to + // represent a 1 or a -1 in the type at all. Piggyback off of + // EmitFixedPointBinOp to avoid having to reimplement saturation. + BinOpInfo info; + info.loc = e->getSourceRange(); + info.fpFeatures = e->getFPFeaturesInEffect(cgf.getLangOpts()); + info.e = e; + info.compType = info.fullType = e->getType(); + info.opcode = e->isIncrementOp() ? BO_Add : BO_Sub; + info.lhs = value; + info.rhs = builder.getConstInt(cgf.getLoc(info.loc), value.getType(), 1); + // If the type is signed, it's better to represent this as +(-1) or -(-1), + // since -1 is guaranteed to be representable. + // FIXME(cir): This is a carry-over from Classic codegen, we should probably + // figure out if ++ -> -(-1) and -- -> +(-1) is REALLY what we want? For now + // we are just doing what classic does here. + if (type->isSignedFixedPointType()) { + info.opcode = e->isIncrementOp() ? BO_Sub : BO_Add; + info.rhs = builder.createNeg(cgf.getLoc(info.loc), info.rhs); + } + // Now, convert from our invented integer literal to the type of the unary + // op. This will upscale and saturate if necessary. This value can become + // undef in some cases. + FixedPointBuilder fpbuilder(builder, cgf.getLoc(info.loc)); + auto dstSema = cgf.getContext().getFixedPointSemantics(info.fullType); + info.rhs = + fpbuilder.createIntegerToFixed(info.rhs, /*srcIsSigned=*/true, dstSema); + return emitFixedPointBinOp(info); +} + mlir::Value ScalarExprEmitter::emitFixedPointConversion(mlir::Value src, QualType srcTy, QualType dstTy, @@ -2340,11 +2547,8 @@ mlir::Value ScalarExprEmitter::emitMul(const BinOpInfo &ops) { return builder.createFMul(loc, ops.lhs, ops.rhs); } - if (ops.isFixedPointOp()) { - assert(!cir::MissingFeatures::fixedPointType()); - cgf.cgm.errorNYI("fixed point"); - return nullptr; - } + if (ops.isFixedPointOp()) + return emitFixedPointBinOp(ops); return cir::MulOp::create(builder, cgf.getLoc(ops.loc), cgf.convertType(ops.fullType), ops.lhs, ops.rhs); @@ -2355,6 +2559,10 @@ mlir::Value ScalarExprEmitter::emitDiv(const BinOpInfo &ops) { CIRGenFunction::CIRGenFPOptionsRAII FPOptsRAII(cgf, ops.fpFeatures); return builder.createFDiv(loc, ops.lhs, ops.rhs); } + + if (ops.isFixedPointOp()) + return emitFixedPointBinOp(ops); + return cir::DivOp::create(builder, loc, cgf.convertType(ops.fullType), ops.lhs, ops.rhs); } @@ -2368,6 +2576,100 @@ mlir::Value ScalarExprEmitter::emitRem(const BinOpInfo &ops) { ops.lhs, ops.rhs); } +mlir::Value ScalarExprEmitter::emitFixedPointBinOp(const BinOpInfo &ops) { + // This is either a binary operation where at least one of the operands is + // a fixed-point type, or a unary operation where the operand is a fixed-point + // type. The result type of a binary operation is determined by + // Sema::handleFixedPointConversions(). + QualType resultTy = ops.compType; + QualType lhsTy, rhsTy; + if (const auto *binOp = dyn_cast<BinaryOperator>(ops.e)) { + rhsTy = binOp->getRHS()->getType(); + if (const auto *cao = dyn_cast<CompoundAssignOperator>(binOp)) { + // For compound assignment, the effective type of the LHS at this point + // is the computation LHS type, not the actual LHS type, and the final + // result type is not the type of the expression but rather the + // computation result type. + lhsTy = cao->getComputationLHSType(); + resultTy = cao->getComputationResultType(); + } else + lhsTy = binOp->getLHS()->getType(); + } else if (const auto *unOp = dyn_cast<UnaryOperator>(ops.e)) { + lhsTy = unOp->getSubExpr()->getType(); + rhsTy = unOp->getSubExpr()->getType(); + } + ASTContext &ctx = cgf.getContext(); + mlir::Value lhs = ops.lhs; + mlir::Value rhs = ops.rhs; + + auto lhsFixedSema = ctx.getFixedPointSemantics(lhsTy); + auto rhsFixedSema = ctx.getFixedPointSemantics(rhsTy); + auto resultFixedSema = ctx.getFixedPointSemantics(resultTy); + auto commonFixedSema = lhsFixedSema.getCommonSemantics(rhsFixedSema); + + // Perform the actual operation. + mlir::Value result; + FixedPointBuilder fpbuilder(builder, cgf.getLoc(ops.loc)); + switch (ops.opcode) { + case BO_AddAssign: + case BO_Add: + result = fpbuilder.createAdd(lhs, lhsFixedSema, rhs, rhsFixedSema); + break; + case BO_SubAssign: + case BO_Sub: + result = fpbuilder.createSub(lhs, lhsFixedSema, rhs, rhsFixedSema); + break; + case BO_MulAssign: + case BO_Mul: + result = fpbuilder.createMul(lhs, lhsFixedSema, rhs, rhsFixedSema); + break; + case BO_DivAssign: + case BO_Div: + result = fpbuilder.createDiv(lhs, lhsFixedSema, rhs, rhsFixedSema); + break; + case BO_ShlAssign: + case BO_Shl: + result = fpbuilder.createShl(lhs, lhsFixedSema, rhs); + break; + case BO_ShrAssign: + case BO_Shr: + result = fpbuilder.createShr(lhs, rhs); + break; + case BO_LT: + case BO_GT: + case BO_LE: + case BO_GE: + case BO_EQ: + case BO_NE: + return fpbuilder.createCmp(lhs, lhsFixedSema, rhs, rhsFixedSema, + clangCmpToCIRCmp(ops.opcode)); + case BO_Cmp: + case BO_LAnd: + case BO_LOr: + llvm_unreachable("Found unimplemented fixed point binary operation"); + case BO_PtrMemD: + case BO_PtrMemI: + case BO_Rem: + case BO_Xor: + case BO_And: + case BO_Or: + case BO_Assign: + case BO_RemAssign: + case BO_AndAssign: + case BO_XorAssign: + case BO_OrAssign: + case BO_Comma: + llvm_unreachable( + "Found unsupported binary operation for fixed point types."); + } + + bool isShift = BinaryOperator::isShiftOp(ops.opcode) || + BinaryOperator::isShiftAssignOp(ops.opcode); + // Convert to the result type. + return fpbuilder.createFixedToFixed( + result, isShift ? lhsFixedSema : commonFixedSema, resultFixedSema); +} + mlir::Value ScalarExprEmitter::emitAdd(const BinOpInfo &ops) { if (mlir::isa<cir::PointerType>(ops.lhs.getType()) || mlir::isa<cir::PointerType>(ops.rhs.getType())) @@ -2411,11 +2713,8 @@ mlir::Value ScalarExprEmitter::emitAdd(const BinOpInfo &ops) { return builder.createFAdd(loc, ops.lhs, ops.rhs); } - if (ops.isFixedPointOp()) { - assert(!cir::MissingFeatures::fixedPointType()); - cgf.cgm.errorNYI("fixed point"); - return {}; - } + if (ops.isFixedPointOp()) + return emitFixedPointBinOp(ops); return builder.createAdd(loc, ops.lhs, ops.rhs); } @@ -2463,11 +2762,8 @@ mlir::Value ScalarExprEmitter::emitSub(const BinOpInfo &ops) { return builder.createFSub(loc, ops.lhs, ops.rhs); } - if (ops.isFixedPointOp()) { - assert(!cir::MissingFeatures::fixedPointType()); - cgf.cgm.errorNYI("fixed point"); - return {}; - } + if (ops.isFixedPointOp()) + return emitFixedPointBinOp(ops); return builder.createSub(loc, ops.lhs, ops.rhs); } @@ -2492,11 +2788,9 @@ mlir::Value ScalarExprEmitter::emitSub(const BinOpInfo &ops) { mlir::Value ScalarExprEmitter::emitShl(const BinOpInfo &ops) { // TODO: This misses out on the sanitizer check below. - if (ops.isFixedPointOp()) { - assert(!cir::MissingFeatures::fixedPointType()); - cgf.cgm.errorNYI("fixed point"); - return {}; - } + if (ops.isFixedPointOp()) + return emitFixedPointBinOp(ops); + // CIR accepts shift between different types, meaning nothing special // to be done here. OTOH, LLVM requires the LHS and RHS to be the same type: @@ -2524,11 +2818,8 @@ mlir::Value ScalarExprEmitter::emitShl(const BinOpInfo &ops) { mlir::Value ScalarExprEmitter::emitShr(const BinOpInfo &ops) { // TODO: This misses out on the sanitizer check below. - if (ops.isFixedPointOp()) { - assert(!cir::MissingFeatures::fixedPointType()); - cgf.cgm.errorNYI("fixed point"); - return {}; - } + if (ops.isFixedPointOp()) + return emitFixedPointBinOp(ops); // CIR accepts shift between different types, meaning nothing special // to be done here. OTOH, LLVM requires the LHS and RHS to be the same type: diff --git a/clang/test/CIR/CodeGen/fixed-point-arith.cpp b/clang/test/CIR/CodeGen/fixed-point-arith.cpp new file mode 100644 index 0000000000000..ee28fe3bae8c6 --- /dev/null +++ b/clang/test/CIR/CodeGen/fixed-point-arith.cpp @@ -0,0 +1,759 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -ffixed-point -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -ffixed-point -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -ffixed-point -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM,OGCG + +extern "C" { +// CIR-LABEL: cir.func {{.*}}@add( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[F:.*]] = cir.alloca "f" align(2) init : !cir.ptr<!s16i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!s32i> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[LOAD_F:.*]] = cir.load align(2) %[[F]] : !cir.ptr<!s16i>, !s16i +// CIR-NEXT: %[[WIDEN_F:.*]] = cir.cast integral %[[LOAD_F]] : !s16i -> !s32i +// CIR-NEXT: %[[RESULT:.*]] = cir.add %[[LOAD_A]], %[[WIDEN_F]] : !s32i +// CIR-NEXT: cir.store %[[RESULT]], %[[RET]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define {{.*}}i32 @add( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM-NEXT: %[[F:.*]] = alloca i16, align 2 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[LOAD_F:.*]] = load i16, ptr %[[F]], align 2 +// LLVM-NEXT: %[[EXT_F:.*]] = sext i16 %[[LOAD_F]] to i32 +// LLVM-NEXT: %[[ADD:.*]] = add i32 %[[LOAD_A]], %[[EXT_F]] +_Accum add(_Accum a, _Fract f) { + return a + f; +} + +// CIR-LABEL: cir.func {{.*}}@add2( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[F:.*]] = cir.alloca "f" align(2) init : !cir.ptr<!s16i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!s32i> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[LOAD_F:.*]] = cir.load align(2) %[[F]] : !cir.ptr<!s16i>, !s16i +// CIR-NEXT: %[[WIDEN_A:.*]] = cir.cast integral %[[LOAD_A]] : !s32i -> !cir.int<s, 47> +// CIR-NEXT: %[[SHIFT_VAL:.*]] = cir.const #cir.int<15> : !cir.int<s, 47> +// CIR-NEXT: %[[SHIFT_A:.*]] = cir.shift(left, %[[WIDEN_A]] : !cir.int<s, 47>, %[[SHIFT_VAL]] : !cir.int<s, 47>) -> !cir.int<s, 47> +// CIR-NEXT: %[[WIDEN_F:.*]] = cir.cast integral %[[LOAD_F]] : !s16i -> !cir.int<s, 47> +// CIR-NEXT: %[[ADD_A_F:.*]] = cir.add %[[SHIFT_A]], %[[WIDEN_F]] : !cir.int<s, 47> +// CIR-NEXT: %[[TRUNC_RES:.*]] = cir.cast integral %[[ADD_A_F]] : !cir.int<s, 47> -> !s16i +// CIR-NEXT: %[[WIDEN_RES:.*]] = cir.cast integral %[[TRUNC_RES]] : !s16i -> !s32i +// CIR-NEXT: cir.store %[[WIDEN_RES]], %[[RET]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define {{.*}}i32 @add2( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM-NEXT: %[[F:.*]] = alloca i16, align 2 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[LOAD_F:.*]] = load i16, ptr %[[F]], align 2 +// LLVM-NEXT: %[[EXT_A:.*]] = sext i32 %[[LOAD_A]] to i47 +// LLVM-NEXT: %[[SHIFT_A:.*]] = shl i47 %[[EXT_A]], 15 +// LLVM-NEXT: %[[EXT_F:.*]] = sext i16 %[[LOAD_F]] to i47 +// LLVM-NEXT: %[[ADD:.*]] = add i47 %[[SHIFT_A]], %[[EXT_F]] +// LLVM-NEXT: %[[TRUNC:.*]] = trunc i47 %[[ADD]] to i16 +// LLVM-NEXT: %[[EXT:.*]] = sext i16 %[[TRUNC]] to i32 +_Accum add2(int a, _Fract f) { + return a + f; +} + +// CIR-LABEL: cir.func {{.*}}@sub( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[F:.*]] = cir.alloca "f" align(2) init : !cir.ptr<!s16i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!s32i> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[LOAD_F:.*]] = cir.load align(2) %[[F]] : !cir.ptr<!s16i>, !s16i +// CIR-NEXT: %[[WIDEN_F:.*]] = cir.cast integral %[[LOAD_F]] : !s16i -> !s32i +// CIR-NEXT: %[[DIFF:.*]] = cir.sub %[[LOAD_A]], %[[WIDEN_F]] : !s32i +// CIR-NEXT: cir.store %[[DIFF]], %[[RET]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define {{.*}}i32 @sub( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM-NEXT: %[[F:.*]] = alloca i16, align 2 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[LOAD_F:.*]] = load i16, ptr %[[F]], align 2 +// LLVM-NEXT: %[[EXT_F:.*]] = sext i16 %[[LOAD_F]] to i32 +// LLVM-NEXT: %[[SUB:.*]] = sub i32 %[[LOAD_A]], %[[EXT_F]] +_Accum sub(_Accum a, _Fract f) { + return a - f; +} + +// CIR-LABEL: cir.func {{.*}}@sub2( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[F:.*]] = cir.alloca "f" align(2) init : !cir.ptr<!s16i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!s32i> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[LOAD_F:.*]] = cir.load align(2) %[[F]] : !cir.ptr<!s16i>, !s16i +// CIR-NEXT: %[[WIDEN_F:.*]] = cir.cast integral %[[LOAD_F]] : !s16i -> !s32i +// CIR-NEXT: %[[WIDEN_A47:.*]] = cir.cast integral %[[LOAD_A]] : !s32i -> !cir.int<s, 47> +// CIR-NEXT: %[[WIDEN_F47:.*]] = cir.cast integral %[[WIDEN_F]] : !s32i -> !cir.int<s, 47> +// CIR-NEXT: %[[SHIFT_NUM:.*]] = cir.const #cir.int<15> : !cir.int<s, 47> +// CIR-NEXT: %[[SHIFT_F:.*]] = cir.shift(left, %[[WIDEN_F47]] : !cir.int<s, 47>, %[[SHIFT_NUM]] : !cir.int<s, 47>) -> !cir.int<s, 47> +// CIR-NEXT: %[[DIFF:.*]] = cir.sub %[[WIDEN_A47]], %[[SHIFT_F]] : !cir.int<s, 47> +// CIR-NEXT: %[[TRUNC_DIFF:.*]] = cir.cast integral %[[DIFF]] : !cir.int<s, 47> -> !s32i +// CIR-NEXT: cir.store %[[TRUNC_DIFF]], %[[RET]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define {{.*}}i32 @sub2( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM-NEXT: %[[F:.*]] = alloca i16, align 2 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[LOAD_F:.*]] = load i16, ptr %[[F]], align 2 +// LLVM-NEXT: %[[EXT_F32:.*]] = sext i16 %[[LOAD_F]] to i32 +// LLVM-NEXT: %[[EXT_A:.*]] = sext i32 %[[LOAD_A]] to i47 +// LLVM-NEXT: %[[EXT_F:.*]] = sext i32 %[[EXT_F32]] to i47 +// LLVM-NEXT: %[[SHIFT:.*]] = shl i47 %[[EXT_F]], 15 +// LLVM-NEXT: %[[SUB:.*]] = sub i47 %[[EXT_A]], %[[SHIFT]] +// LLVM-NEXT: %[[TRUNC:.*]] = trunc i47 %[[SUB]] to i32 +_Accum sub2(_Accum a, short f) { + return a - f; +} + +// CIR-LABEL: cir.func {{.*}}@mul( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[F:.*]] = cir.alloca "f" align(2) init : !cir.ptr<!s16i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!s32i> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[LOAD_F:.*]] = cir.load align(2) %[[F]] : !cir.ptr<!s16i>, !s16i +// CIR-NEXT: %[[WIDEN_F:.*]] = cir.cast integral %[[LOAD_F]] : !s16i -> !s32i +// CIR-NEXT: %[[SCALE:.*]] = cir.const #cir.int<15> : !s32i +// CIR-NEXT: %[[RES:.*]] = cir.call_llvm_intrinsic "smul.fix" %[[LOAD_A]], %[[WIDEN_F]], %[[SCALE]] : (!s32i, !s32i, !s32i) -> !s32i +// CIR-NEXT: cir.store %[[RES]], %[[RET]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define {{.*}}i32 @mul( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM-NEXT: %[[F:.*]] = alloca i16, align 2 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[LOAD_F:.*]] = load i16, ptr %[[F]], align 2 +// LLVM-NEXT: %[[EXT_F:.*]] = sext i16 %[[LOAD_F]] to i32 +// LLVM-NEXT: %[[MUL:.*]] = call i32 @llvm.smul.fix.i32(i32 %[[LOAD_A]], i32 %[[EXT_F]], i32 15) +_Accum mul(_Accum a, _Fract f) { + return a * f; +} + +// CIR-LABEL: cir.func {{.*}}@mul2( +// CIR-NEXT: %[[S:.*]] = cir.alloca "s" align(2) init : !cir.ptr<!u16i> +// CIR-NEXT: %[[F:.*]] = cir.alloca "f" align(2) init : !cir.ptr<!s16i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!s32i> +// CIR: %[[LOAD_S:.*]] = cir.load align(2) %[[S]] : !cir.ptr<!u16i>, !u16i +// CIR-NEXT: %[[WIDEN_S:.*]] = cir.cast integral %[[LOAD_S]] : !u16i -> !s32i +// CIR-NEXT: %[[LOAD_F:.*]] = cir.load align(2) %[[F]] : !cir.ptr<!s16i>, !s16i +// CIR-NEXT: %[[WIDEN_S47:.*]] = cir.cast integral %[[WIDEN_S]] : !s32i -> !cir.int<s, 47> +// CIR-NEXT: %[[SHIFT_NUM:.*]] = cir.const #cir.int<15> : !cir.int<s, 47> +// CIR-NEXT: %[[SHIFT:.*]] = cir.shift(left, %[[WIDEN_S47]] : !cir.int<s, 47>, %[[SHIFT_NUM]] : !cir.int<s, 47>) -> !cir.int<s, 47> +// CIR-NEXT: %[[WIDEN_F47:.*]] = cir.cast integral %[[LOAD_F]] : !s16i -> !cir.int<s, 47> +// CIR-NEXT: %[[SCALE:.*]] = cir.const #cir.int<15> : !s32i +// CIR-NEXT: %[[RES:.*]] = cir.call_llvm_intrinsic "smul.fix" %[[SHIFT]], %[[WIDEN_F47]], %[[SCALE]] : (!cir.int<s, 47>, !cir.int<s, 47>, !s32i) -> !cir.int<s, 47> +// CIR-NEXT: %[[TRUNC_RES:.*]] = cir.cast integral %[[RES]] : !cir.int<s, 47> -> !s16i +// CIR-NEXT: %[[WIDEN_RES:.*]] = cir.cast integral %[[TRUNC_RES]] : !s16i -> !s32i +// CIR-NEXT: cir.store %[[WIDEN_RES]], %[[RET]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define {{.*}}i32 @mul2(i16 +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[S:.*]] = alloca i16, align 2 +// LLVM-NEXT: %[[F:.*]] = alloca i16, align 2 +// LLVM: %[[LOAD_S:.*]] = load i16, ptr %[[S]], align 2 +// LLVM-NEXT: %[[EXT_S:.*]] = zext i16 %[[LOAD_S]] to i32 +// LLVM-NEXT: %[[LOAD_F:.*]] = load i16, ptr %[[F]], align 2 +// LLVM-NEXT: %[[EXT_S2:.*]] = sext i32 %[[EXT_S]] to i47 +// LLVM-NEXT: %[[SHIFT_S:.*]] = shl i47 %[[EXT_S2]], 15 +// LLVM-NEXT: %[[EXT_F:.*]] = sext i16 %[[LOAD_F]] to i47 +// LLVM-NEXT: %[[MUL:.*]] = call i47 @llvm.smul.fix.i47(i47 %[[SHIFT_S]], i47 %[[EXT_F]], i32 15) +// LLVM-NEXT: %[[TRUNC_RES:.*]] = trunc i47 %[[MUL]] to i16 +// LLVM-NEXT: %[[EXT_RES:.*]] = sext i16 %[[TRUNC_RES]] to i32 +_Accum mul2(unsigned short s, _Fract f) { + return s * f; +} + +// CIR-LABEL: cir.func {{.*}}@div( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[F:.*]] = cir.alloca "f" align(2) init : !cir.ptr<!s16i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!s32i> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[LOAD_F:.*]] = cir.load align(2) %[[F]] : !cir.ptr<!s16i>, !s16i +// CIR-NEXT: %[[WIDEN_F:.*]] = cir.cast integral %[[LOAD_F]] : !s16i -> !s32i +// CIR-NEXT: %[[SCALE:.*]] = cir.const #cir.int<15> : !s32i +// CIR-NEXT: %[[DIV:.*]] = cir.call_llvm_intrinsic "sdiv.fix" %[[LOAD_A]], %[[WIDEN_F]], %[[SCALE]] : (!s32i, !s32i, !s32i) -> !s32i +// CIR-NEXT: cir.store %[[DIV]], %[[RET]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define {{.*}}i32 @div( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM-NEXT: %[[F:.*]] = alloca i16, align 2 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[LOAD_F:.*]] = load i16, ptr %[[F]], align 2 +// LLVM-NEXT: %[[EXT_F:.*]] = sext i16 %[[LOAD_F]] to i32 +// LLVM-NEXT: %[[DIV:.*]] = call i32 @llvm.sdiv.fix.i32(i32 %[[LOAD_A]], i32 %[[EXT_F]], i32 15) +_Accum div(_Accum a, _Fract f) { + return a / f; +} + +// CIR-LABEL: cir.func {{.*}}@div2( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[F:.*]] = cir.alloca "f" align(4) init : !cir.ptr<!cir.float> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!s32i> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[FLOAT_A:.*]] = cir.cast int_to_float %[[LOAD_A]] : !s32i -> !cir.float +// CIR-NEXT: %[[SCALE:.*]] = cir.const #cir.fp<3.05175781E-5> : !cir.float +// CIR-NEXT: %[[SCALE_A:.*]] = cir.fmul %[[FLOAT_A]], %[[SCALE]] : !cir.float +// CIR-NEXT: %[[LOAD_F:.*]] = cir.load align(4) %[[F]] : !cir.ptr<!cir.float>, !cir.float +// CIR-NEXT: %[[DIV:.*]] = cir.fdiv %[[SCALE_A]], %[[LOAD_F]] : !cir.float +// CIR-NEXT: %[[SCALE:.*]] = cir.const #cir.fp<3.276800e+04> : !cir.float +// CIR-NEXT: %[[RES:.*]] = cir.fmul %[[DIV]], %[[SCALE]] : !cir.float +// CIR-NEXT: %[[RES_TO_INT:.*]] = cir.cast float_to_int %[[RES]] : !cir.float -> !s32i +// CIR-NEXT: cir.store %[[RES_TO_INT]], %[[RET]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define {{.*}}i32 @div2( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM-NEXT: %[[F:.*]] = alloca float, align 4 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[A_TO_FLOAT:.*]] = sitofp i32 %[[LOAD_A]] to float +// LLVM-NEXT: %[[SCALE_A:.*]] = fmul float %[[A_TO_FLOAT]], f0x38000000 +// LLVM-NEXT: %[[LOAD_F:.*]] = load float, ptr %[[F]], align 4 +// LLVM-NEXT: %[[DIV:.*]] = fdiv float %[[SCALE_A]], %[[LOAD_F]] +// LLVM-NEXT: %[[SCALE_RES:.*]] = fmul float %[[DIV]], 3.276800e+04 +// LLVM-NEXT: %[[RES_TO_INT:.*]] = fptosi float %[[SCALE_RES]] to i32 +_Accum div2(_Accum a, float f) { + return a / f; +} + +// CIR-LABEL: cir.func {{.*}}@umul( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!u32i> +// CIR-NEXT: %[[B:.*]] = cir.alloca "b" align(4) init : !cir.ptr<!u32i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!u32i> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!u32i>, !u32i +// CIR-NEXT: %[[LOAD_B:.*]] = cir.load align(4) %[[B]] : !cir.ptr<!u32i>, !u32i +// CIR-NEXT: %[[SCALE:.*]] = cir.const #cir.int<16> : !u32i +// CIR-NEXT: %[[MUL:.*]] = cir.call_llvm_intrinsic "umul.fix" %[[LOAD_A]], %[[LOAD_B]], %[[SCALE]] : (!u32i, !u32i, !u32i) -> !u32i +// CIR-NEXT: cir.store %[[MUL]], %[[RET]] : !u32i, !cir.ptr<!u32i> + +// LLVM-LABEL: define {{.*}}i32 @umul( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM-NEXT: %[[B:.*]] = alloca i32, align 4 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[LOAD_B:.*]] = load i32, ptr %[[B]], align 4 +// LLVM-NEXT: %[[MUL:.*]] = call i32 @llvm.umul.fix.i32(i32 %[[LOAD_A]], i32 %[[LOAD_B]], i32 16) +unsigned _Accum umul(unsigned _Accum a, unsigned _Accum b) { + return a * b; +} + +// CIR-LABEL: cir.func {{.*}}@cmp( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[F:.*]] = cir.alloca "f" align(2) init : !cir.ptr<!s16i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(1) : !cir.ptr<!cir.bool> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[LOAD_F:.*]] = cir.load align(2) %[[F]] : !cir.ptr<!s16i>, !s16i +// CIR-NEXT: %[[WIDEN_F:.*]] = cir.cast integral %[[LOAD_F]] : !s16i -> !s32i +// CIR-NEXT: %[[LT:.*]] = cir.cmp lt %[[LOAD_A]], %[[WIDEN_F]] : !s32i +// CIR-NEXT: cir.store %[[LT]], %[[RET]] : !cir.bool, !cir.ptr<!cir.bool> + +// LLVM-LABEL: define {{.*}}i1 @cmp( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM-NEXT: %[[F:.*]] = alloca i16, align 2 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[LOAD_F:.*]] = load i16, ptr %[[F]], align 2 +// LLVM-NEXT: %[[EXT_F:.*]] = sext i16 %[[LOAD_F]] to i32 +// LLVM-NEXT: %[[CMP:.*]] = icmp slt i32 %[[LOAD_A]], %[[EXT_F]] +bool cmp(_Accum a, _Fract f) { + return a < f; +} + +// CIR-LABEL: cir.func {{.*}}@cmp2( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[F:.*]] = cir.alloca "f" align(2) init : !cir.ptr<!s16i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(1) : !cir.ptr<!cir.bool> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[LOAD_F:.*]] = cir.load align(2) %[[F]] : !cir.ptr<!s16i>, !s16i +// CIR-NEXT: %[[WIDEN_F:.*]] = cir.cast integral %[[LOAD_F]] : !s16i -> !s32i +// CIR-NEXT: %[[GT:.*]] = cir.cmp gt %[[LOAD_A]], %[[WIDEN_F]] : !s32i +// CIR-NEXT: cir.store %[[GT]], %[[RET]] : !cir.bool, !cir.ptr<!cir.bool> + +// LLVM-LABEL: define {{.*}}i1 @cmp2( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM-NEXT: %[[F:.*]] = alloca i16, align 2 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[LOAD_F:.*]] = load i16, ptr %[[F]], align 2 +// LLVM-NEXT: %[[EXT_F:.*]] = sext i16 %[[LOAD_F]] to i32 +// LLVM-NEXT: %[[CMP:.*]] = icmp sgt i32 %[[LOAD_A]], %[[EXT_F]] +bool cmp2(_Accum a, _Fract f) { + return a > f; +} + +// CIR-LABEL: cir.func {{.*}}@cmp3( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[F:.*]] = cir.alloca "f" align(2) init : !cir.ptr<!s16i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(1) : !cir.ptr<!cir.bool> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[LOAD_F:.*]] = cir.load align(2) %[[F]] : !cir.ptr<!s16i>, !s16i +// CIR-NEXT: %[[WIDEN_F:.*]] = cir.cast integral %[[LOAD_F]] : !s16i -> !s32i +// CIR-NEXT: %[[EQ:.*]] = cir.cmp eq %[[LOAD_A]], %[[WIDEN_F]] : !s32i +// CIR-NEXT: cir.store %[[EQ]], %[[RET]] : !cir.bool, !cir.ptr<!cir.bool> + +// LLVM-LABEL: define {{.*}}i1 @cmp3( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM-NEXT: %[[F:.*]] = alloca i16, align 2 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[LOAD_F:.*]] = load i16, ptr %[[F]], align 2 +// LLVM-NEXT: %[[EXT_F:.*]] = sext i16 %[[LOAD_F]] to i32 +// LLVM-NEXT: %[[CMP:.*]] = icmp eq i32 %[[LOAD_A]], %[[EXT_F]] +bool cmp3(_Accum a, _Fract f) { + return a == f; +} + +// CIR-LABEL: cir.func {{.*}}@shl( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!s32i> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[SHIFT_NUM:.*]] = cir.const #cir.int<2> : !s32i +// CIR-NEXT: %[[SHIFT:.*]] = cir.shift(left, %[[LOAD_A]] : !s32i, %[[SHIFT_NUM]] : !s32i) -> !s32i +// CIR-NEXT: cir.store %[[SHIFT]], %[[RET]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define {{.*}}i32 @shl( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[SHIFT:.*]] = shl i32 %[[LOAD_A]], 2 +_Accum shl(_Accum a) { + return a << 2; +} + +// CIR-LABEL: cir.func {{.*}}@shl2( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[C:.*]] = cir.alloca "c" align(1) init : !cir.ptr<!u8i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!s32i> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[LOAD_C:.*]] = cir.load align(1) %[[C]] : !cir.ptr<!u8i>, !u8i +// CIR-NEXT: %[[WIDEN_C:.*]] = cir.cast integral %[[LOAD_C]] : !u8i -> !s32i +// CIR-NEXT: %[[SHIFT:.*]] = cir.shift(left, %[[LOAD_A]] : !s32i, %[[WIDEN_C]] : !s32i) -> !s32i +// CIR-NEXT: cir.store %[[SHIFT]], %[[RET]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define {{.*}}i32 @shl2( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM-NEXT: %[[C:.*]] = alloca i8, align 1 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[LOAD_C:.*]] = load i8, ptr %[[C]], align 1 +// LLVM-NEXT: %[[C_EXT:.*]] = zext i8 %[[LOAD_C]] to i32 +// LLVM-NEXT: %[[SHIFT:.*]] = shl i32 %[[LOAD_A]], %[[C_EXT]] +_Accum shl2(_Accum a, unsigned char c) { + return a << c; +} + +// CIR-LABEL: cir.func {{.*}}@shl3( +// CIR-NEXT: %[[F:.*]] = cir.alloca "f" align(2) init : !cir.ptr<!s16i> +// CIR-NEXT: %[[C:.*]] = cir.alloca "c" align(1) init : !cir.ptr<!u8i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(2) : !cir.ptr<!s16i> +// CIR: %[[LOAD_F:.*]] = cir.load align(2) %[[F]] : !cir.ptr<!s16i>, !s16i +// CIR-NEXT: %[[LOAD_C:.*]] = cir.load align(1) %[[C]] : !cir.ptr<!u8i>, !u8i +// CIR-NEXT: %[[WIDEN_C:.*]] = cir.cast integral %[[LOAD_C]] : !u8i -> !s32i +// CIR-NEXT: %[[SHIFT:.*]] = cir.shift(left, %[[LOAD_F]] : !s16i, %[[WIDEN_C]] : !s32i) -> !s16i +// CIR-NEXT: cir.store %[[SHIFT]], %[[RET]] : !s16i, !cir.ptr<!s16i> + +// LLVM-LABEL: define {{.*}}i16 @shl3( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[F:.*]] = alloca i16, align 2 +// LLVM-NEXT: %[[C:.*]] = alloca i8, align 1 +// LLVM: %[[LOAD_F:.*]] = load i16, ptr %[[F]], align 2 +// LLVM-NEXT: %[[LOAD_C:.*]] = load i8, ptr %[[C]], align 1 +// LLVM-NEXT: %[[EXT_C:.*]] = zext i8 %[[LOAD_C]] to i32 +// LLVM-NEXT: %[[TRUNC_C:.*]] = trunc i32 %[[EXT_C]] to i16 +// LLVM-NEXT: %[[SHIFT:.*]] = shl i16 %[[LOAD_F]], %[[TRUNC_C]] +_Fract shl3(_Fract f, unsigned char c) { + return f << c; +} + +// CIR-LABEL: cir.func {{.*}}@shr( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!s32i> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[SHIFT_NUM:.*]] = cir.const #cir.int<2> : !s32i +// CIR-NEXT: %[[SHIFT:.*]] = cir.shift(right, %[[LOAD_A]] : !s32i, %[[SHIFT_NUM]] : !s32i) -> !s32i +// CIR-NEXT: cir.store %[[SHIFT]], %[[RET]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define {{.*}}i32 @shr( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[SHIFT:.*]] = ashr i32 %[[LOAD_A]], 2 +_Accum shr(_Accum a) { + return a >> 2; +} + +// CIR-LABEL: cir.func {{.*}}@shr2( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[U:.*]] = cir.alloca "u" align(4) init : !cir.ptr<!u32i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!s32i> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[LOAD_U:.*]] = cir.load align(4) %[[U]] : !cir.ptr<!u32i>, !u32i +// CIR-NEXT: %[[SHIFT:.*]] = cir.shift(right, %[[LOAD_A]] : !s32i, %[[LOAD_U]] : !u32i) -> !s32i +// CIR-NEXT: cir.store %[[SHIFT]], %[[RET]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define {{.*}}i32 @shr2( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM-NEXT: %[[U:.*]] = alloca i32, align 4 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[LOAD_U:.*]] = load i32, ptr %[[U]], align 4 +// LLVM-NEXT: %[[SHIFT:.*]] = ashr i32 %[[LOAD_A]], %[[LOAD_U]] +_Accum shr2(_Accum a, unsigned u) { + return a >> u; +} + +// CIR-LABEL: cir.func {{.*}}@shr3( +// CIR-NEXT: %[[F:.*]] = cir.alloca "f" align(2) init : !cir.ptr<!s16i> +// CIR-NEXT: %[[U:.*]] = cir.alloca "u" align(4) init : !cir.ptr<!u32i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(2) : !cir.ptr<!s16i> +// CIR: %[[LOAD_F:.*]] = cir.load align(2) %[[F]] : !cir.ptr<!s16i>, !s16i +// CIR-NEXT: %[[LOAD_U:.*]] = cir.load align(4) %[[U]] : !cir.ptr<!u32i>, !u32i +// CIR-NEXT: %[[SHIFT:.*]] = cir.shift(right, %[[LOAD_F]] : !s16i, %[[LOAD_U]] : !u32i) -> !s16i +// CIR-NEXT: cir.store %[[SHIFT]], %[[RET]] : !s16i, !cir.ptr<!s16i> + +// LLVM-LABEL: define {{.*}}i16 @shr3( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[F:.*]] = alloca i16, align 2 +// LLVM-NEXT: %[[U:.*]] = alloca i32, align 4 +// LLVM: %[[LOAD_F:.*]] = load i16, ptr %[[F]], align 2 +// LLVM-NEXT: %[[LOAD_U:.*]] = load i32, ptr %[[U]], align 4 +// LLVM-NEXT: %[[TRUNC_U:.*]] = trunc i32 %[[LOAD_U]] to i16 +// LLVM-NEXT: %[[SHIFT:.*]] = ashr i16 %[[LOAD_F]], %[[TRUNC_U]] +_Fract shr3(_Fract f, unsigned u) { + return f >> u; +} + +// CIR-LABEL: cir.func {{.*}}@inc( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!s32i> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[NEG_1:.*]] = cir.const #cir.int<-1> : !s32i +// CIR-NEXT: %[[SHIFT_NUM:.*]] = cir.const #cir.int<15> : !s32i +// CIR-NEXT: %[[SHIFT:.*]] = cir.shift(left, %[[NEG_1]] : !s32i, %[[SHIFT_NUM]] : !s32i) -> !s32i +// CIR-NEXT: %[[SUB:.*]] = cir.sub %[[LOAD_A]], %[[SHIFT]] : !s32i +// CIR-NEXT: cir.store align(4) %[[SUB:.*]], %[[A]] : !s32i, !cir.ptr<!s32i> +// CIR-NEXT: cir.store %[[SUB]], %[[RET]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define {{.*}}i32 @inc( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[INC:.*]] = sub i32 %[[LOAD_A]], -32768 +_Accum inc(_Accum a) { + return ++a; +} + +// CIR-LABEL: cir.func {{.*}}@inc2( +// CIR-NEXT: %[[F:.*]] = cir.alloca "f" align(2) init : !cir.ptr<!s16i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(2) : !cir.ptr<!s16i> +// CIR: %[[LOAD_F:.*]] = cir.load align(2) %[[F]] : !cir.ptr<!s16i>, !s16i +// CIR-NEXT: %[[NEG_1:.*]] = cir.const #cir.int<-1> : !s16i +// CIR-NEXT: %[[SHIFT_NUM:.*]] = cir.const #cir.int<15> : !s16i +// CIR-NEXT: %[[SHIFT:.*]] = cir.shift(left, %[[NEG_1:.*]] : !s16i, %[[SHIFT_NUM]] : !s16i) -> !s16i +// CIR-NEXT: %[[SUB:.*]] = cir.sub %[[LOAD_F]], %[[SHIFT]] : !s16i +// CIR-NEXT: cir.store align(2) %[[SUB]], %[[F]] : !s16i, !cir.ptr<!s16i> +// CIR-NEXT: cir.store %[[LOAD_F]], %[[RET]] : !s16i, !cir.ptr<!s16i> + +// LLVM-LABEL: define {{.*}}i16 @inc2( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i16, align 2 +// LLVM: %[[LOAD_A:.*]] = load i16, ptr %[[A]], align 2 +// LLVM-NEXT: %[[INC:.*]] = sub i16 %[[LOAD_A]], -32768 +_Fract inc2(_Fract f) { + return f++; +} + +// CIR-LABEL: cir.func {{.*}}@dec( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!s32i> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[NEG_1:.*]] = cir.const #cir.int<-1> : !s32i +// CIR-NEXT: %[[SHIFT_NUM:.*]] = cir.const #cir.int<15> : !s32i +// CIR-NEXT: %[[SHIFT:.*]] = cir.shift(left, %[[NEG_1]] : !s32i, %[[SHIFT_NUM]] : !s32i) -> !s32i +// CIR-NEXT: %[[ADD:.*]] = cir.add %[[LOAD_A]], %[[SHIFT]] : !s32i +// CIR-NEXT: cir.store align(4) %[[ADD]], %[[A]] : !s32i, !cir.ptr<!s32i> +// CIR-NEXT: cir.store %[[ADD]], %[[RET]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define {{.*}}i32 @dec( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[DEC:.*]] = add i32 %[[LOAD_A]], -32768 +_Accum dec(_Accum a) { + return --a; +} + +// CIR-LABEL: cir.func {{.*}}@dec2( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(2) init : !cir.ptr<!s16i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(2) : !cir.ptr<!s16i> +// CIR: %[[LOAD_A:.*]] = cir.load align(2) %[[A]] : !cir.ptr<!s16i>, !s16i +// CIR-NEXT: %[[NEG_1:.*]] = cir.const #cir.int<-1> : !s16i +// CIR-NEXT: %[[SHIFT_NUM:.*]] = cir.const #cir.int<15> : !s16i +// CIR-NEXT: %[[SHIFT:.*]] = cir.shift(left, %[[NEG_1]] : !s16i, %[[SHIFT_NUM]] : !s16i) -> !s16i +// CIR-NEXT: %[[SUB:.*]] = cir.add %[[LOAD_A]], %[[SHIFT]] : !s16i +// CIR-NEXT: cir.store align(2) %[[SUB]], %[[A]] : !s16i, !cir.ptr<!s16i> +// CIR-NEXT: cir.store %[[LOAD_A:.*]], %[[RET]] : !s16i, !cir.ptr<!s16i> + +// LLVM-LABEL: define {{.*}}i16 @dec2( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i16, align 2 +// LLVM: %[[LOAD_A:.*]] = load i16, ptr %[[A]], align 2 +// LLVM-NEXT: %[[DEC:.*]] = add i16 %[[LOAD_A]], -32768 +_Fract dec2(_Fract a) { + return a--; +} + +// CIR-LABEL: cir.func {{.*}}@satAdd( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[B:.*]] = cir.alloca "b" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!s32i> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[LOAD_B:.*]] = cir.load align(4) %[[B]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[ADD:.*]] = cir.call_llvm_intrinsic "sadd.sat" %[[LOAD_A]], %[[LOAD_B]] : (!s32i, !s32i) -> !s32i +// CIR-NEXT: cir.store %[[ADD]], %[[RET]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define {{.*}}i32 @satAdd( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM-NEXT: %[[B:.*]] = alloca i32, align 4 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[LOAD_B:.*]] = load i32, ptr %[[B]], align 4 +// LLVM-NEXT: %[[SUB:.*]] = call i32 @llvm.sadd.sat.i32(i32 %[[LOAD_A]], i32 %[[LOAD_B]]) +_Sat _Accum satAdd(_Sat _Accum a, _Accum b) { + return a + b; +} + +// CIR-LABEL: cir.func {{.*}}@satSub( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[B:.*]] = cir.alloca "b" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!s32i> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[LOAD_B:.*]] = cir.load align(4) %[[B]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[SUB:.*]] = cir.call_llvm_intrinsic "ssub.sat" %[[LOAD_A]], %[[LOAD_B]] : (!s32i, !s32i) -> !s32i +// CIR-NEXT: cir.store %[[SUB]], %[[RET]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define {{.*}}i32 @satSub( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM-NEXT: %[[B:.*]] = alloca i32, align 4 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[LOAD_B:.*]] = load i32, ptr %[[B]], align 4 +// LLVM-NEXT: %[[SUB:.*]] = call i32 @llvm.ssub.sat.i32(i32 %[[LOAD_A]], i32 %[[LOAD_B]]) +_Sat _Accum satSub(_Sat _Accum a, _Sat _Accum b) { + return a - b; +} + +// CIR-LABEL: cir.func {{.*}}@satMul( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[B:.*]] = cir.alloca "b" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!s32i> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[LOAD_B:.*]] = cir.load align(4) %[[B]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[SCALE:.*]] = cir.const #cir.int<15> : !s32i +// CIR-NEXT: %[[MUL:.*]] = cir.call_llvm_intrinsic "smul.fix.sat" %[[LOAD_A]], %[[LOAD_B]], %[[SCALE]] : (!s32i, !s32i, !s32i) -> !s32i +// CIR-NEXT: cir.store %[[MUL]], %[[RET]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define {{.*}}i32 @satMul( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM-NEXT: %[[B:.*]] = alloca i32, align 4 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[LOAD_B:.*]] = load i32, ptr %[[B]], align 4 +// LLVM-NEXT: %[[SHIFT:.*]] = call i32 @llvm.smul.fix.sat.i32(i32 %[[LOAD_A]], i32 %[[LOAD_B]], i32 15) +_Sat _Accum satMul(_Sat _Accum a, _Sat _Accum b) { + return a * b; +} + +// CIR-LABEL: cir.func {{.*}}@satDiv( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[B:.*]] = cir.alloca "b" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!s32i> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[LOAD_B:.*]] = cir.load align(4) %[[B]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[SCALE:.*]] = cir.const #cir.int<15> : !s32i +// CIR-NEXT: %[[DIV:.*]] = cir.call_llvm_intrinsic "sdiv.fix.sat" %[[LOAD_A]], %[[LOAD_B]], %[[SCALE]] : (!s32i, !s32i, !s32i) -> !s32i +// CIR-NEXT: cir.store %[[DIV]], %[[RET]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define {{.*}}i32 @satDiv( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM-NEXT: %[[B:.*]] = alloca i32, align 4 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[LOAD_B:.*]] = load i32, ptr %[[B]], align 4 +// LLVM-NEXT: %[[SHIFT:.*]] = call i32 @llvm.sdiv.fix.sat.i32(i32 %[[LOAD_A]], i32 %[[LOAD_B]], i32 15) +_Sat _Accum satDiv(_Sat _Accum a, _Sat _Accum b) { + return a / b; +} + +// CIR-LABEL: cir.func {{.*}}@satShl( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!s32i> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[SHIFT_NUM:.*]] = cir.const #cir.int<2> : !s32i +// CIR-NEXT: %[[SHIFT:.*]] = cir.call_llvm_intrinsic "sshl.sat" %[[LOAD_A]], %[[SHIFT_NUM]] : (!s32i, !s32i) -> !s32i +// CIR-NEXT: cir.store %[[SHIFT]], %[[RET]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define {{.*}}i32 @satShl( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[SHIFT:.*]] = call i32 @llvm.sshl.sat.i32(i32 %[[LOAD_A]], i32 2) +_Sat _Accum satShl(_Sat _Accum a) { + return a << 2; +} + +// CIR-LABEL: cir.func {{.*}}@satShl2( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(2) init : !cir.ptr<!s16i> +// CIR-NEXT: %[[C:.*]] = cir.alloca "c" align(1) init : !cir.ptr<!s8i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(2) : !cir.ptr<!s16i> +// CIR: %[[LOAD_A:.*]] = cir.load align(2) %[[A]] : !cir.ptr<!s16i>, !s16i +// CIR-NEXT: %[[LOAD_C:.*]] = cir.load align(1) %[[C]] : !cir.ptr<!s8i>, !s8i +// CIR-NEXT: %[[WIDEN_C:.*]] = cir.cast integral %[[LOAD_C]] : !s8i -> !s32i +// CIR-NEXT: %[[UNSIGNED_C:.*]] = cir.cast integral %[[WIDEN_C]] : !s32i -> !u32i +// CIR-NEXT: %[[TRUNC_C:.*]] = cir.cast integral %[[UNSIGNED_C]] : !u32i -> !s16i +// CIR-NEXT: %[[SHIFT:.*]] = cir.call_llvm_intrinsic "sshl.sat" %[[LOAD_A]], %[[TRUNC_C]] : (!s16i, !s16i) -> !s16i +// CIR-NEXT: cir.store %[[SHIFT]], %[[RET]] : !s16i, !cir.ptr<!s16i> + +// LLVM-LABEL: define {{.*}}i16 @satShl2( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[F:.*]] = alloca i16, align 2 +// LLVM-NEXT: %[[C:.*]] = alloca i8, align 1 +// LLVM: %[[LOAD_F:.*]] = load i16, ptr %[[F]], align 2 +// LLVM-NEXT: %[[LOAD_C:.*]] = load i8, ptr %[[C]], align 1 +// LLVM-NEXT: %[[EXT_C:.*]] = sext i8 %[[LOAD_C]] to i32 +// LLVM-NEXT: %[[TRUNC_C:.*]] = trunc i32 %[[EXT_C]] to i16 +// LLVM-NEXT: %[[SHIFT:.*]] = call i16 @llvm.sshl.sat.i16(i16 %[[LOAD_F]], i16 %[[TRUNC_C]]) +_Sat _Fract satShl2(_Sat _Fract a, char c) { + return a << c; +} + +// CIR-LABEL: cir.func {{.*}}@satShr( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!s32i> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[SHIFT_NUM:.*]] = cir.const #cir.int<2> : !s32i +// CIR-NEXT: %[[SHIFT:.*]] = cir.shift(right, %[[LOAD_A]] : !s32i, %[[SHIFT_NUM]] : !s32i) -> !s32i +// CIR-NEXT: cir.store %[[SHIFT]], %[[RET]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define {{.*}}i32 @satShr( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[SHIFT:.*]] = ashr i32 %[[LOAD_A]], 2 +_Sat _Accum satShr(_Sat _Accum a) { + return a >> 2; +} + +// CIR-LABEL: cir.func {{.*}}@satShr2( +// CIR-NEXT: %[[F:.*]] = cir.alloca "f" align(2) init : !cir.ptr<!s16i> +// CIR-NEXT: %[[I:.*]] = cir.alloca "i" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(2) : !cir.ptr<!s16i> +// CIR: %[[LOAD_F:.*]] = cir.load align(2) %[[F]] : !cir.ptr<!s16i>, !s16i +// CIR-NEXT: %[[LOAD_I:.*]] = cir.load align(4) %[[I]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[SHIFT:.*]] = cir.shift(right, %[[LOAD_F]] : !s16i, %[[LOAD_I]] : !s32i) -> !s16i +// CIR-NEXT: cir.store %[[SHIFT]], %[[RET]] : !s16i, !cir.ptr<!s16i> + +// LLVM-LABEL: define {{.*}}i16 @satShr2( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[F:.*]] = alloca i16, align 2 +// LLVM-NEXT: %[[I:.*]] = alloca i32, align 4 +// LLVM: %[[LOAD_F:.*]] = load i16, ptr %[[F]], align 2 +// LLVM-NEXT: %[[LOAD_I:.*]] = load i32, ptr %[[I]], align 4 +// LLVM-NEXT: %[[TRUNC:.*]] = trunc i32 %[[LOAD_I]] to i16 +// LLVM-NEXT: %[[SHIFT:.*]] = ashr i16 %[[LOAD_F]], %[[TRUNC]] + +_Sat _Fract satShr2(_Sat _Fract f, int i) { + return f >> i; +} + +// CIR-LABEL: cir.func {{.*}}@satInc( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(4) : !cir.ptr<!s32i> +// CIR: %[[LOAD_A:.*]] = cir.load align(4) %[[A]] : !cir.ptr<!s32i>, !s32i +// CIR-NEXT: %[[NEG_1:.*]] = cir.const #cir.int<-1> : !cir.int<s, 47> +// CIR-NEXT: %[[SHIFT_NUM:.*]] = cir.const #cir.int<15> : !cir.int<s, 47> +// CIR-NEXT: %[[SHIFT:.*]] = cir.shift(left, %[[NEG_1]] : !cir.int<s, 47>, %[[SHIFT_NUM]] : !cir.int<s, 47>) -> !cir.int<s, 47> +// CIR-NEXT: %[[MAX:.*]] = cir.const #cir.int<2147483647> : !cir.int<s, 47> +// CIR-NEXT: %[[CMP:.*]] = cir.cmp gt %[[SHIFT]], %[[MAX]] : !cir.int<s, 47> +// CIR-NEXT: %[[SELECT:.*]] = cir.select if %[[CMP]] then %[[MAX]] else %[[SHIFT]] : (!cir.bool, !cir.int<s, 47>, !cir.int<s, 47>) -> !cir.int<s, 47> +// CIR-NEXT: %[[MIN:.*]] = cir.const #cir.int<-2147483648> : !cir.int<s, 47> +// CIR-NEXT: %[[CMP:.*]] = cir.cmp lt %[[SELECT]], %[[MIN]] : !cir.int<s, 47> +// CIR-NEXT: %[[SELECT2:.*]] = cir.select if %[[CMP]] then %[[MIN]] else %[[SELECT]] : (!cir.bool, !cir.int<s, 47>, !cir.int<s, 47>) -> !cir.int<s, 47> +// CIR-NEXT: %[[TRUNC:.*]] = cir.cast integral %[[SELECT2]] : !cir.int<s, 47> -> !s32i +// CIR-NEXT: %[[SUB:.*]] = cir.call_llvm_intrinsic "ssub.sat" %[[LOAD_A]], %[[TRUNC]] : (!s32i, !s32i) -> !s32i +// CIR-NEXT: cir.store align(4) %[[SUB]], %[[A]] : !s32i, !cir.ptr<!s32i> +// CIR-NEXT: cir.store %[[SUB]], %[[RET]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define {{.*}}i32 @satInc( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i32, align 4 +// LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[A]], align 4 +// LLVM-NEXT: %[[ADD:.*]] = call i32 @llvm.ssub.sat.i32(i32 %[[LOAD_A]], i32 -32768) +// LLVM-NEXT: store i32 %[[ADD]], ptr %[[A]], align 4 +_Sat _Accum satInc(_Sat _Accum a) { + return ++a; +} + +// CIR-LABEL: cir.func {{.*}}@satDec( +// CIR-NEXT: %[[F:.*]] = cir.alloca "f" align(2) init : !cir.ptr<!s16i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(2) : !cir.ptr<!s16i> +// CIR: %[[LOAD_F:.*]] = cir.load align(2) %[[F]] : !cir.ptr<!s16i>, !s16i +// CIR-NEXT: %[[NEG_1:.*]] = cir.const #cir.int<-1> : !cir.int<s, 31> +// CIR-NEXT: %[[SHIFT_NUM:.*]] = cir.const #cir.int<15> : !cir.int<s, 31> +// CIR-NEXT: %[[SHIFT:.*]] = cir.shift(left, %[[NEG_1]] : !cir.int<s, 31>, %[[SHIFT_NUM]] : !cir.int<s, 31>) -> !cir.int<s, 31> +// CIR-NEXT: %[[MAX:.*]] = cir.const #cir.int<32767> : !cir.int<s, 31> +// CIR-NEXT: %[[CMP:.*]] = cir.cmp gt %[[SHIFT]], %[[MAX]] : !cir.int<s, 31> +// CIR-NEXT: %[[SELECT:.*]] = cir.select if %[[CMP]] then %[[MAX]] else %[[SHIFT]] : (!cir.bool, !cir.int<s, 31>, !cir.int<s, 31>) -> !cir.int<s, 31> +// CIR-NEXT: %[[MIN:.*]] = cir.const #cir.int<-32768> : !cir.int<s, 31> +// CIR-NEXT: %[[CMP:.*]] = cir.cmp lt %[[SELECT]], %[[MIN]] : !cir.int<s, 31> +// CIR-NEXT: %[[SELECT2:.*]] = cir.select if %[[CMP]] then %[[MIN]] else %[[SELECT]] : (!cir.bool, !cir.int<s, 31>, !cir.int<s, 31>) -> !cir.int<s, 31> +// CIR-NEXT: %[[TRUNC:.*]] = cir.cast integral %[[SELECT2]] : !cir.int<s, 31> -> !s16i +// CIR-NEXT: %[[ADD:.*]] = cir.call_llvm_intrinsic "sadd.sat" %[[LOAD_F]], %[[TRUNC]] : (!s16i, !s16i) -> !s16i +// CIR-NEXT: cir.store align(2) %[[ADD]], %[[F]] : !s16i, !cir.ptr<!s16i> +// CIR-NEXT: cir.store %[[LOAD_F]], %[[RET]] : !s16i, !cir.ptr<!s16i> + +// LLVM-LABEL: define {{.*}}i16 @satDec( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[F:.*]] = alloca i16, align 2 +// LLVM: %[[LOAD_F:.*]] = load i16, ptr %[[F]], align 2 +// LLVM-NEXT: %[[ADD:.*]] = call i16 @llvm.sadd.sat.i16(i16 %[[LOAD_F]], i16 -32768) +// LLVM-NEXT: store i16 %[[ADD]], ptr %[[F]], align 2 + +_Sat _Fract satDec(_Sat _Fract f) { + return f--; +} + +// CIR-LABEL: cir.func {{.*}}@satIncUFract( +// CIR-NEXT: %[[A:.*]] = cir.alloca "a" align(2) init : !cir.ptr<!u16i> +// CIR-NEXT: %[[RET:.*]] = cir.alloca "__retval" align(2) : !cir.ptr<!u16i> +// CIR: %[[LOAD_A:.*]] = cir.load align(2) %[[A]] : !cir.ptr<!u16i>, !u16i +// CIR-NEXT: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i +// CIR-NEXT: %[[SHIFT_NUM:.*]] = cir.const #cir.int<16> : !s32i +// CIR-NEXT: %[[SHIFT:.*]] = cir.shift(left, %[[ONE]] : !s32i, %[[SHIFT_NUM]] : !s32i) -> !s32i +// CIR-NEXT: %[[MAX:.*]] = cir.const #cir.int<65535> : !s32i +// CIR-NEXT: %[[CMP:.*]] = cir.cmp gt %[[SHIFT]], %[[MAX]] : !s32i +// CIR-NEXT: %[[SELECT:.*]] = cir.select if %[[CMP]] then %[[MAX]] else %[[SHIFT]] : (!cir.bool, !s32i, !s32i) -> !s32i +// CIR-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i +// CIR-NEXT: %[[CMP:.*]] = cir.cmp lt %[[SELECT]], %[[ZERO]] : !s32i +// CIR-NEXT: %[[SELECT2:.*]] = cir.select if %[[CMP]] then %[[ZERO]] else %[[SELECT]] : (!cir.bool, !s32i, !s32i) -> !s32i +// CIR-NEXT: %[[TRUNC:.*]] = cir.cast integral %[[SELECT2]] : !s32i -> !u16i +// CIR-NEXT: %[[ADD:.*]] = cir.call_llvm_intrinsic "uadd.sat" %[[LOAD_A]], %[[TRUNC]] : (!u16i, !u16i) -> !u16i +// CIR-NEXT: cir.store align(2) %[[ADD]], %[[A]] : !u16i, !cir.ptr<!u16i> +// CIR-NEXT: cir.store %[[ADD]], %[[RET]] : !u16i, !cir.ptr<!u16i> + +// LLVM-LABEL: define {{.*}}i16 @satIncUFract( +// OGCG-NEXT: entry: +// LLVM-NEXT: %[[A:.*]] = alloca i16, align 2 +// LLVM: %[[LOAD_A:.*]] = load i16, ptr %[[A]], align 2 +// LLVM-NEXT: %[[ADD:.*]] = call i16 @llvm.uadd.sat.i16(i16 %[[LOAD_A]], i16 -1) +// LLVM-NEXT: store i16 %[[ADD]], ptr %[[A]], align 2 +_Sat unsigned _Fract satIncUFract(_Sat unsigned _Fract a) { + return ++a; +} +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
