================
@@ -2714,12 +2714,440 @@ static void ClearPadding(CodeGenFunction &CGF, Address 
Src,
   }
 }
 
+/// Lower builtins that operate on ARM64EC's software x87 long double.  The
+/// ones that are required to be exact (fabs, copysign, signbit, isnan, isinf,
+/// isfinite, isnormal, fpclassify) are implemented with integer operations on
+/// the image itself; everything else is evaluated in `double` exactly like the
+/// C library does for its own long double entry points.  Return false when the
+/// builtin does not involve such a value.
+bool CodeGenFunction::EmitSoftX87LongDoubleBuiltin(unsigned BuiltinID,
+                                                   const CallExpr *E,
+                                                   RValue &Result) {
+  // The 'nan' and 'inf' families produce a `long double`, so they do not
+  // consume one but do need the integer image constructed below: they are the
+  // only builtins that yield a constant.  Match the bare name, since some of
+  // them are also reachable with a `__builtin_` prefix.
+  std::string Name = getContext().BuiltinInfo.getName(BuiltinID);
+  StringRef BaseName = Name;
+  BaseName.consume_front("__builtin_");
+  bool ProducesLongDouble = false;
+  for (const char *Str : {"nanl", "nansl", "infl", "huge_vall"})
+    ProducesLongDouble |= BaseName == Str;
+
+  // Everything below rewrites a call into its binary64 equivalent, which only
+  // makes sense when a `long double` is *consumed*.
+  bool ConsumesLongDouble = false;
+  for (const Expr *Arg : E->arguments()) {
+    QualType Ty = Arg->getType();
+    if (const auto *CTy = Ty->getAs<ComplexType>())
+      Ty = CTy->getElementType();
+    ConsumesLongDouble |= Ty->isSpecificBuiltinType(BuiltinType::LongDouble);
+  }
+
+  if (!ProducesLongDouble && !ConsumesLongDouble)
+    return false;
+
+  // `fabsl`: Set the sign bit to zero.
+  if (BaseName == "fabsl") {
+    if (!E->getType()->isSpecificBuiltinType(BuiltinType::LongDouble))
+      return false;
+    llvm::Value *V = EmitScalarExpr(E->getArg(0));
+    llvm::Constant *MagnitudeMask = llvm::ConstantInt::get(
+        V->getType(), ~llvm::APInt::getOneBitSet(128, 79));
+    Result = RValue::get(Builder.CreateAnd(V, MagnitudeMask));
+    return true;
+  }
+
+  // `copysignl`: Take the magnitude from the first operand and the sign bit
+  // from the second one.
+  if (BaseName == "copysignl") {
+    if (!E->getType()->isSpecificBuiltinType(BuiltinType::LongDouble))
+      return false;
+    llvm::Value *X = EmitScalarExpr(E->getArg(0));
+    llvm::Value *Y = EmitScalarExpr(E->getArg(1));
+    llvm::Constant *MagnitudeMask = llvm::ConstantInt::get(
+        X->getType(), ~llvm::APInt::getOneBitSet(128, 79));
+    llvm::Value *Magnitude = Builder.CreateAnd(X, MagnitudeMask);
+    llvm::Value *Sign = Builder.CreateAnd(Y, Builder.CreateNot(MagnitudeMask));
+    Result = RValue::get(Builder.CreateOr(Magnitude, Sign));
+    return true;
+  }
+
+  // `signbit`: Take the sign bit and normalize it to boolean.
+  if (Name == "__builtin_signbit" || BaseName == "signbitl") {
+    if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+            BuiltinType::LongDouble))
+      return false;
+    llvm::Value *V = EmitScalarExpr(E->getArg(0));
+    llvm::Value *Sign =
+        Builder.CreateTrunc(Builder.CreateLShr(V, 79), Builder.getInt1Ty());
+    Result = RValue::get(Builder.CreateZExt(Sign, ConvertType(E->getType())));
+    return true;
+  }
+
+  // `__builtin_isnan`: Check if the value is a NaN.  An unnormal or pseudo
+  // denormal image is undefined behavior, since it will never be produced in a
+  // constant expression.
+  if (Name == "__builtin_isnan") {
+    if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+            BuiltinType::LongDouble))
+      return false;
+    llvm::Value *V = EmitScalarExpr(E->getArg(0));
+    llvm::Constant *MagnitudeMask = llvm::ConstantInt::get(
+        V->getType(), ~llvm::APInt::getOneBitSet(128, 79));
+    llvm::Value *Magnitude = Builder.CreateAnd(V, MagnitudeMask);
+    llvm::APInt InfinityImage(128, 0x8000000000000000ull);
+    InfinityImage |= llvm::APInt(128, 0x7fffull) << 64;
+    llvm::Constant *Infinity =
+        llvm::ConstantInt::get(V->getType(), InfinityImage);
+    llvm::Value *IsNan = Builder.CreateICmpUGT(Magnitude, Infinity);
+    Result = RValue::get(Builder.CreateZExt(IsNan, ConvertType(E->getType())));
+    return true;
+  }
+
+  // `__builtin_isinf`: Check if the value is a (positive or negative) 
infinity.
+  if (Name == "__builtin_isinf") {
+    if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+            BuiltinType::LongDouble))
+      return false;
+    llvm::Value *V = EmitScalarExpr(E->getArg(0));
+    llvm::Constant *MagnitudeMask = llvm::ConstantInt::get(
+        V->getType(), ~llvm::APInt::getOneBitSet(128, 79));
+    llvm::Value *Magnitude = Builder.CreateAnd(V, MagnitudeMask);
+    llvm::APInt InfinityImage(128, 0x8000000000000000ull);
+    InfinityImage |= llvm::APInt(128, 0x7fffull) << 64;
+    llvm::Constant *Infinity =
+        llvm::ConstantInt::get(V->getType(), InfinityImage);
+    llvm::Value *IsInf = Builder.CreateICmpEQ(Magnitude, Infinity);
+    Result = RValue::get(Builder.CreateZExt(IsInf, ConvertType(E->getType())));
+    return true;
+  }
+
+  // `__builtin_isinf_sign`: Return 1 or -1 depending on the sign of an
+  // infinity, and zero for everything else.
+  if (Name == "__builtin_isinf_sign") {
+    if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+            BuiltinType::LongDouble))
+      return false;
+    llvm::Value *V = EmitScalarExpr(E->getArg(0));
+    llvm::Constant *MagnitudeMask = llvm::ConstantInt::get(
+        V->getType(), ~llvm::APInt::getOneBitSet(128, 79));
+    llvm::Value *Magnitude = Builder.CreateAnd(V, MagnitudeMask);
+    llvm::APInt InfinityImage(128, 0x8000000000000000ull);
+    InfinityImage |= llvm::APInt(128, 0x7fffull) << 64;
+    llvm::Constant *Infinity =
+        llvm::ConstantInt::get(V->getType(), InfinityImage);
+    llvm::Type *IntTy = ConvertType(E->getType());
+    llvm::Value *Sign =
+        Builder.CreateTrunc(Builder.CreateLShr(V, 79), Builder.getInt1Ty());
+    llvm::Value *Signed = Builder.CreateSelect(
+        Sign, llvm::ConstantInt::get(IntTy, -1, /*isSigned=*/true),
+        llvm::ConstantInt::get(IntTy, 1));
+    llvm::Value *IsInf = Builder.CreateICmpEQ(Magnitude, Infinity);
+    Result = RValue::get(
+        Builder.CreateSelect(IsInf, Signed, llvm::ConstantInt::get(IntTy, 0)));
+    return true;
+  }
+
+  // `__builtin_isfinite`: Check if the value is neither an infinity nor a NaN.
+  if (Name == "__builtin_isfinite") {
+    if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+            BuiltinType::LongDouble))
+      return false;
+    llvm::Value *V = EmitScalarExpr(E->getArg(0));
+    llvm::Constant *MagnitudeMask = llvm::ConstantInt::get(
+        V->getType(), ~llvm::APInt::getOneBitSet(128, 79));
+    llvm::Value *Magnitude = Builder.CreateAnd(V, MagnitudeMask);
+    llvm::APInt InfinityImage(128, 0x8000000000000000ull);
+    InfinityImage |= llvm::APInt(128, 0x7fffull) << 64;
+    llvm::Constant *Infinity =
+        llvm::ConstantInt::get(V->getType(), InfinityImage);
+    llvm::Value *IsFinite = Builder.CreateICmpULT(Magnitude, Infinity);
+    Result =
+        RValue::get(Builder.CreateZExt(IsFinite, ConvertType(E->getType())));
+    return true;
+  }
+
+  // `__builtin_isnormal`: Check if the value is finite and nonzero.  A
+  // canonical image has the explicit integer bit set when the exponent is
+  // nonzero, so the exponent alone decides; an unnormal is undefined 
behaviour.
+  if (Name == "__builtin_isnormal") {
+    if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+            BuiltinType::LongDouble))
+      return false;
+    llvm::Value *V = EmitScalarExpr(E->getArg(0));
+    llvm::Value *Exp = Builder.CreateAnd(
+        Builder.CreateTrunc(Builder.CreateLShr(V, 64), Builder.getInt16Ty()),
+        Builder.getInt16(0x7FFF));
+    llvm::Value *IsNormal =
+        Builder.CreateAnd(Builder.CreateICmpNE(Exp, Builder.getInt16(0)),
+                          Builder.CreateICmpNE(Exp, Builder.getInt16(0x7FFF)));
+    Result =
+        RValue::get(Builder.CreateZExt(IsNormal, ConvertType(E->getType())));
+    return true;
+  }
+
+  // `__builtin_fpclassify`: Pick the first five arguments that correspond to a
+  // NaN, an infinity, a normal, a subnormal and a zero respectively.
+  if (Name == "__builtin_fpclassify") {
+    if (!E->getArg(5)->getType()->isSpecificBuiltinType(
+            BuiltinType::LongDouble))
+      return false;
+    llvm::Value *V = EmitScalarExpr(E->getArg(5));
+    llvm::Value *Exp = Builder.CreateAnd(
+        Builder.CreateTrunc(Builder.CreateLShr(V, 64), Builder.getInt16Ty()),
+        Builder.getInt16(0x7FFF));
+    llvm::Value *Man = Builder.CreateTrunc(V, Builder.getInt64Ty());
+    llvm::Value *ExpZero = Builder.CreateICmpEQ(Exp, Builder.getInt16(0));
+    llvm::Value *ExpAllOnes =
+        Builder.CreateICmpEQ(Exp, Builder.getInt16(0x7FFF));
+    llvm::Value *ManZero = Builder.CreateICmpEQ(Man, Builder.getInt64(0));
+    // Infinity is the all-ones exponent with nothing but the integer bit set;
+    // any other value with that exponent is a NaN.  An unnormal, whose integer
+    // bit is clear, is undefined behaviour.
+    llvm::Value *IsInf = Builder.CreateAnd(
+        ExpAllOnes,
+        Builder.CreateICmpEQ(Man, Builder.getInt64(0x8000000000000000ull)));
+    llvm::Value *IsNaN =
+        Builder.CreateAnd(ExpAllOnes, Builder.CreateNot(IsInf));
+    llvm::Value *IsNormal = Builder.CreateAnd(Builder.CreateNot(ExpZero),
+                                              Builder.CreateNot(ExpAllOnes));
+    llvm::Value *IsSubnormal =
+        Builder.CreateAnd(ExpZero, Builder.CreateNot(ManZero));
+    llvm::Value *R = EmitScalarExpr(E->getArg(4)); // zero
+    R = Builder.CreateSelect(IsSubnormal, EmitScalarExpr(E->getArg(3)), R);
+    R = Builder.CreateSelect(IsNormal, EmitScalarExpr(E->getArg(2)), R);
+    R = Builder.CreateSelect(IsInf, EmitScalarExpr(E->getArg(1)), R);
+    R = Builder.CreateSelect(IsNaN, EmitScalarExpr(E->getArg(0)), R);
+    Result = RValue::get(R);
+    return true;
+  }
+
+  // `infl` and `huge_vall`: Infinity has the significand 0x8000000000000000
+  // with an all-ones exponent, and the six padding bytes are zeroes.  They
+  // take no argument at all, so there is nothing to check.
+  if (BaseName == "infl" || BaseName == "huge_vall") {
+    llvm::APInt Image(128, 0x8000000000000000ull);
+    Image |= llvm::APInt(128, 0x7fffull) << 64;
+    llvm::Type *ImageTy = llvm::IntegerType::get(getLLVMContext(), 128);
+    Result = RValue::get(llvm::ConstantInt::get(ImageTy, Image));
+    return true;
+  }
+
+  // `nanl` and `nansl`: The quiet NaN has the significand 0xc000000000000000
+  // and the signalling one has 0xa000000000000000, both with an all-ones
+  // exponent.  The payload of the tag argument is not propagated, but the
+  // argument is still evaluated, since it may have side effects.
+  if (BaseName == "nanl" || BaseName == "nansl") {
+    if (!E->getArg(0)->getType()->isPointerType())
+      return false;
+    EmitScalarExpr(E->getArg(0));
+    llvm::APInt Image(128, BaseName == "nansl" ? 0xa000000000000000ull
+                                               : 0xc000000000000000ull);
+    Image |= llvm::APInt(128, 0x7fffull) << 64;
+    llvm::Type *ImageTy = llvm::IntegerType::get(getLLVMContext(), 128);
+    Result = RValue::get(llvm::ConstantInt::get(ImageTy, Image));
+    return true;
+  }
+
+  // `__builtin_isunordered`: True if either operand is a NaN.
+  // `__builtin_isgreater`, `__builtin_isgreaterequal`, `__builtin_isless` and
+  // `__builtin_islessequal`: Perform ordered comparisons of two operands.
+  // `__builtin_islessgreater`: True if the operands are ordered and unequal;
+  // that is, if either of `x < y` and `x > y` holds.
+  struct BuiltinComparison {
+    const char *Name;
+    CmpInst::Predicate Predicate;
+  };
+  static constexpr BuiltinComparison BuiltinComparisons[] = {
+      {"__builtin_isunordered", CmpInst::FCMP_UNO},
+      {"__builtin_isgreater", CmpInst::FCMP_OGT},
+      {"__builtin_isgreaterequal", CmpInst::FCMP_OGE},
+      {"__builtin_isless", CmpInst::FCMP_OLT},
+      {"__builtin_islessequal", CmpInst::FCMP_OLE},
+      {"__builtin_islessgreater", CmpInst::FCMP_ONE},
+  };
+  for (const auto &BC : BuiltinComparisons)
+    if (Name == BC.Name) {
+      if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+              BuiltinType::LongDouble) ||
+          !E->getArg(1)->getType()->isSpecificBuiltinType(
+              BuiltinType::LongDouble))
+        return false;
+      llvm::Value *A = EmitScalarExpr(E->getArg(0));
+      llvm::Value *B = EmitScalarExpr(E->getArg(1));
+      llvm::Value *Cmp = EmitSoftX87LongDoubleComparison(A, B, BC.Predicate);
+      Result = RValue::get(Builder.CreateZExt(Cmp, ConvertType(E->getType())));
+      return true;
+    }
+
+  // `__builtin_isfpclass`: Test the image against a mask of class bits.  The
+  // second argument is an integer constant expression built out of the
+  // `__FPCLASS_*` values, whose bits number the classes in this order:
+  // signalling NaN, quiet NaN, -inf, negative normal, negative subnormal,
+  // -0, +0, positive subnormal, positive normal and +inf.  So the value only
+  // has to be mapped to the ordinal of the class it belongs to, and the bit
+  // of the mask with that index decides.
+  if (Name == "__builtin_isfpclass") {
+    if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+            BuiltinType::LongDouble))
+      return false;
+    Expr::EvalResult MaskResult;
+    if (!E->getArg(1)->EvaluateAsInt(MaskResult, getContext()))
+      return false;
+    const uint64_t Mask = MaskResult.Val.getInt().getLimitedValue();
+    llvm::Value *V = EmitScalarExpr(E->getArg(0));
+    llvm::Value *Exp = Builder.CreateAnd(
+        Builder.CreateTrunc(Builder.CreateLShr(V, 64), Builder.getInt16Ty()),
+        Builder.getInt16(0x7FFF));
+    llvm::Value *Man = Builder.CreateTrunc(V, Builder.getInt64Ty());
+    llvm::Value *Negative =
+        Builder.CreateTrunc(Builder.CreateLShr(V, 79), Builder.getInt1Ty());
+    llvm::Value *ExpZero = Builder.CreateICmpEQ(Exp, Builder.getInt16(0));
+    llvm::Value *ExpAllOnes =
+        Builder.CreateICmpEQ(Exp, Builder.getInt16(0x7FFF));
+    llvm::Value *ManZero = Builder.CreateICmpEQ(Man, Builder.getInt64(0));
+    // Infinity is the all-ones exponent with nothing but the integer bit
+    // set, and a NaN is any other image with that exponent, quiet when bit
+    // 62 of the significand is set.  An unnormal, whose integer bit is
+    // clear, is undefined behaviour.
+    llvm::Value *Infinity = Builder.CreateAnd(
+        ExpAllOnes,
+        Builder.CreateICmpEQ(Man, Builder.getInt64(0x8000000000000000ull)));
+    llvm::Value *Quiet = Builder.CreateICmpNE(
+        Builder.CreateAnd(Man, Builder.getInt64(0x4000000000000000ull)),
+        Builder.getInt64(0));
+    // The magnitudes are numbered 2 to 5 from the largest downwards, and the
+    // positive classes continue the sequence in the reverse order, which makes
+    // them `11 - Magnitude`.  A NaN takes 0 or 1 instead of a magnitude.
+    llvm::Value *Magnitude = Builder.getInt64(2);
+    Magnitude = Builder.CreateSelect(Builder.CreateNot(ExpAllOnes),
+                                     Builder.getInt64(3), Magnitude);
+    Magnitude = Builder.CreateSelect(ExpZero, Builder.getInt64(4), Magnitude);
+    Magnitude = Builder.CreateSelect(Builder.CreateAnd(ExpZero, ManZero),
+                                     Builder.getInt64(5), Magnitude);
+    Magnitude = Builder.CreateSelect(
+        Negative, Magnitude,
+        Builder.CreateSub(Builder.getInt64(11), Magnitude));
+    llvm::Value *Ordinal =
+        Builder.CreateSelect(Quiet, Builder.getInt64(1), Builder.getInt64(0));
+    Ordinal = Builder.CreateSelect(
+        Builder.CreateAnd(ExpAllOnes, Builder.CreateNot(Infinity)), Ordinal,
+        Magnitude);
+    llvm::Value *Bit =
+        Builder.CreateAnd(Builder.CreateLShr(Builder.getInt64(Mask), Ordinal),
+                          Builder.getInt64(1));
+    Result = RValue::get(
+        Builder.CreateZExt(Builder.CreateICmpNE(Bit, Builder.getInt64(0)),
+                           ConvertType(E->getType())));
+    return true;
+  }
+
+  // `__builtin_complex`: Assemble a complex value out of a real and an
+  // imaginary part, which become the low and high halves of the image pair.
+  // Its operands are "custom type checked", so they are not promoted: both
+  // have the same real floating type already, and they have to be the `long
+  // double` one for this lowering to apply.
+  if (Name == "__builtin_complex") {
+    if (!E->getArg(0)->getType()->isSpecificBuiltinType(
+            BuiltinType::LongDouble) ||
+        !E->getArg(1)->getType()->isSpecificBuiltinType(
+            BuiltinType::LongDouble))
+      return false;
+    llvm::Value *Real = EmitScalarExpr(E->getArg(0));
+    llvm::Value *Imag = EmitScalarExpr(E->getArg(1));
+    Result = RValue::getComplex(Real, Imag);
+    return true;
+  }
+
+  // `conjl`: Conjugation only flips the sign bit of the imaginary part, which
+  // is the high half of the image pair, and leaves the real part alone.
+  if (BaseName == "conjl") {
+    const auto *CTy = E->getArg(0)->getType()->getAs<ComplexType>();
+    if (!CTy ||
+        !CTy->getElementType()->isSpecificBuiltinType(BuiltinType::LongDouble))
+      return false;
+    ComplexPairTy Z = EmitComplexExpr(E->getArg(0));
+    llvm::Constant *SignMask = llvm::ConstantInt::get(
+        Z.second->getType(), llvm::APInt::getOneBitSet(128, 79));
+    llvm::Value *Imag = Builder.CreateXor(Z.second, SignMask);
+    Result = RValue::getComplex(Z.first, Imag);
+    return true;
+  }
+
+  // `creall` and `cimagl`: They only pick the low or the high half of the
+  // image pair, which is the real or the imaginary part.
+  if (BaseName == "creall" || BaseName == "cimagl") {
+    const auto *CTy = E->getArg(0)->getType()->getAs<ComplexType>();
+    if (!CTy ||
+        !CTy->getElementType()->isSpecificBuiltinType(BuiltinType::LongDouble))
+      return false;
+    ComplexPairTy Z = EmitComplexExpr(E->getArg(0));
+    Result = RValue::get(BaseName == "creall" ? Z.first : Z.second);
+    return true;
+  }
+
+  // Everything else is a function of the C library, which on this target works
+  // on the 80-bit image itself: a `long double` *is* the image, so its own
+  // entry point takes and returns it untouched, and the builtin is nothing
+  // but a call to that entry point.  Nothing is evaluated in `double` and
+  // rounded back, so that not a single bit is lost.  The variadic `printf`
+  // and `scanf` families go the same way: their ellipsis arguments are
+  // forwarded untouched, since the x64 calling convention that ARM64EC
+  // reproduces passes a 16-byte `long double` by reference.
+
+  // `powil` is the only one left that has no entry point of that spelling in
+  // the C library: there is no `powi` function, and `llvm.powi` only accepts
+  // real floating-point values.  mingw-w64 CRT provides `__powil` for it.
+  if (Name == "__builtin_powil") {
+    llvm::Value *X = EmitScalarExpr(E->getArg(0));
+    llvm::Value *N = EmitScalarExpr(E->getArg(1));
+    llvm::FunctionType *FT = llvm::FunctionType::get(
+        ConvertType(E->getType()), {X->getType(), N->getType()}, false);
+    Result = RValue::get(
+        Builder.CreateCall(CGM.CreateRuntimeFunction(FT, "__powil"), {X, N}));
+    return true;
+  }
+
+  // Both spellings arrive here: `__builtin_sinl` and the `sinl` that the
+  // header declares are separate builtin identifiers, and they resolve to the
+  // same entry point.
+  const FunctionDecl *FD = E->getDirectCallee();
+  if (FD && getContext().BuiltinInfo.isLibFunction(BuiltinID)) {
+    Result =
+        emitLibraryCall(*this, FD, E, CGM.getBuiltinLibFunction(FD, 
BuiltinID));
+    return true;
+  }
+
+  if (FD && getContext().BuiltinInfo.isPredefinedLibFunction(BuiltinID)) {
+    Result = emitLibraryCall(*this, FD, E, CGM.getRawFunctionPointer(FD));
+    return true;
+  }
+
+  return false;
----------------
lhmouse wrote:

Add in 295fe6ac475e3b80ddcbbf2281f8984fcd052f67

https://github.com/llvm/llvm-project/pull/237057
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to