aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/llvm/lib/Support/APFixedPoint.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/llvm/lib/Support/APFixedPoint.cpp')
-rw-r--r--contrib/llvm-project/llvm/lib/Support/APFixedPoint.cpp574
1 files changed, 574 insertions, 0 deletions
diff --git a/contrib/llvm-project/llvm/lib/Support/APFixedPoint.cpp b/contrib/llvm-project/llvm/lib/Support/APFixedPoint.cpp
new file mode 100644
index 000000000000..9764dd51f572
--- /dev/null
+++ b/contrib/llvm-project/llvm/lib/Support/APFixedPoint.cpp
@@ -0,0 +1,574 @@
+//===- APFixedPoint.cpp - Fixed point constant handling ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+/// Defines the implementation for the fixed point number interface.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/APFixedPoint.h"
+#include "llvm/ADT/APFloat.h"
+
+namespace llvm {
+
+APFixedPoint APFixedPoint::convert(const FixedPointSemantics &DstSema,
+ bool *Overflow) const {
+ APSInt NewVal = Val;
+ unsigned DstWidth = DstSema.getWidth();
+ unsigned DstScale = DstSema.getScale();
+ bool Upscaling = DstScale > getScale();
+ if (Overflow)
+ *Overflow = false;
+
+ if (Upscaling) {
+ NewVal = NewVal.extend(NewVal.getBitWidth() + DstScale - getScale());
+ NewVal <<= (DstScale - getScale());
+ } else {
+ NewVal >>= (getScale() - DstScale);
+ }
+
+ auto Mask = APInt::getBitsSetFrom(
+ NewVal.getBitWidth(),
+ std::min(DstScale + DstSema.getIntegralBits(), NewVal.getBitWidth()));
+ APInt Masked(NewVal & Mask);
+
+ // Change in the bits above the sign
+ if (!(Masked == Mask || Masked == 0)) {
+ // Found overflow in the bits above the sign
+ if (DstSema.isSaturated())
+ NewVal = NewVal.isNegative() ? Mask : ~Mask;
+ else if (Overflow)
+ *Overflow = true;
+ }
+
+ // If the dst semantics are unsigned, but our value is signed and negative, we
+ // clamp to zero.
+ if (!DstSema.isSigned() && NewVal.isSigned() && NewVal.isNegative()) {
+ // Found negative overflow for unsigned result
+ if (DstSema.isSaturated())
+ NewVal = 0;
+ else if (Overflow)
+ *Overflow = true;
+ }
+
+ NewVal = NewVal.extOrTrunc(DstWidth);
+ NewVal.setIsSigned(DstSema.isSigned());
+ return APFixedPoint(NewVal, DstSema);
+}
+
+int APFixedPoint::compare(const APFixedPoint &Other) const {
+ APSInt ThisVal = getValue();
+ APSInt OtherVal = Other.getValue();
+ bool ThisSigned = Val.isSigned();
+ bool OtherSigned = OtherVal.isSigned();
+ unsigned OtherScale = Other.getScale();
+ unsigned OtherWidth = OtherVal.getBitWidth();
+
+ unsigned CommonWidth = std::max(Val.getBitWidth(), OtherWidth);
+
+ // Prevent overflow in the event the widths are the same but the scales differ
+ CommonWidth += getScale() >= OtherScale ? getScale() - OtherScale
+ : OtherScale - getScale();
+
+ ThisVal = ThisVal.extOrTrunc(CommonWidth);
+ OtherVal = OtherVal.extOrTrunc(CommonWidth);
+
+ unsigned CommonScale = std::max(getScale(), OtherScale);
+ ThisVal = ThisVal.shl(CommonScale - getScale());
+ OtherVal = OtherVal.shl(CommonScale - OtherScale);
+
+ if (ThisSigned && OtherSigned) {
+ if (ThisVal.sgt(OtherVal))
+ return 1;
+ else if (ThisVal.slt(OtherVal))
+ return -1;
+ } else if (!ThisSigned && !OtherSigned) {
+ if (ThisVal.ugt(OtherVal))
+ return 1;
+ else if (ThisVal.ult(OtherVal))
+ return -1;
+ } else if (ThisSigned && !OtherSigned) {
+ if (ThisVal.isSignBitSet())
+ return -1;
+ else if (ThisVal.ugt(OtherVal))
+ return 1;
+ else if (ThisVal.ult(OtherVal))
+ return -1;
+ } else {
+ // !ThisSigned && OtherSigned
+ if (OtherVal.isSignBitSet())
+ return 1;
+ else if (ThisVal.ugt(OtherVal))
+ return 1;
+ else if (ThisVal.ult(OtherVal))
+ return -1;
+ }
+
+ return 0;
+}
+
+APFixedPoint APFixedPoint::getMax(const FixedPointSemantics &Sema) {
+ bool IsUnsigned = !Sema.isSigned();
+ auto Val = APSInt::getMaxValue(Sema.getWidth(), IsUnsigned);
+ if (IsUnsigned && Sema.hasUnsignedPadding())
+ Val = Val.lshr(1);
+ return APFixedPoint(Val, Sema);
+}
+
+APFixedPoint APFixedPoint::getMin(const FixedPointSemantics &Sema) {
+ auto Val = APSInt::getMinValue(Sema.getWidth(), !Sema.isSigned());
+ return APFixedPoint(Val, Sema);
+}
+
+bool FixedPointSemantics::fitsInFloatSemantics(
+ const fltSemantics &FloatSema) const {
+ // A fixed point semantic fits in a floating point semantic if the maximum
+ // and minimum values as integers of the fixed point semantic can fit in the
+ // floating point semantic.
+
+ // If these values do not fit, then a floating point rescaling of the true
+ // maximum/minimum value will not fit either, so the floating point semantic
+ // cannot be used to perform such a rescaling.
+
+ APSInt MaxInt = APFixedPoint::getMax(*this).getValue();
+ APFloat F(FloatSema);
+ APFloat::opStatus Status = F.convertFromAPInt(MaxInt, MaxInt.isSigned(),
+ APFloat::rmNearestTiesToAway);
+ if ((Status & APFloat::opOverflow) || !isSigned())
+ return !(Status & APFloat::opOverflow);
+
+ APSInt MinInt = APFixedPoint::getMin(*this).getValue();
+ Status = F.convertFromAPInt(MinInt, MinInt.isSigned(),
+ APFloat::rmNearestTiesToAway);
+ return !(Status & APFloat::opOverflow);
+}
+
+FixedPointSemantics FixedPointSemantics::getCommonSemantics(
+ const FixedPointSemantics &Other) const {
+ unsigned CommonScale = std::max(getScale(), Other.getScale());
+ unsigned CommonWidth =
+ std::max(getIntegralBits(), Other.getIntegralBits()) + CommonScale;
+
+ bool ResultIsSigned = isSigned() || Other.isSigned();
+ bool ResultIsSaturated = isSaturated() || Other.isSaturated();
+ bool ResultHasUnsignedPadding = false;
+ if (!ResultIsSigned) {
+ // Both are unsigned.
+ ResultHasUnsignedPadding = hasUnsignedPadding() &&
+ Other.hasUnsignedPadding() && !ResultIsSaturated;
+ }
+
+ // If the result is signed, add an extra bit for the sign. Otherwise, if it is
+ // unsigned and has unsigned padding, we only need to add the extra padding
+ // bit back if we are not saturating.
+ if (ResultIsSigned || ResultHasUnsignedPadding)
+ CommonWidth++;
+
+ return FixedPointSemantics(CommonWidth, CommonScale, ResultIsSigned,
+ ResultIsSaturated, ResultHasUnsignedPadding);
+}
+
+APFixedPoint APFixedPoint::add(const APFixedPoint &Other,
+ bool *Overflow) const {
+ auto CommonFXSema = Sema.getCommonSemantics(Other.getSemantics());
+ APFixedPoint ConvertedThis = convert(CommonFXSema);
+ APFixedPoint ConvertedOther = Other.convert(CommonFXSema);
+ APSInt ThisVal = ConvertedThis.getValue();
+ APSInt OtherVal = ConvertedOther.getValue();
+ bool Overflowed = false;
+
+ APSInt Result;
+ if (CommonFXSema.isSaturated()) {
+ Result = CommonFXSema.isSigned() ? ThisVal.sadd_sat(OtherVal)
+ : ThisVal.uadd_sat(OtherVal);
+ } else {
+ Result = ThisVal.isSigned() ? ThisVal.sadd_ov(OtherVal, Overflowed)
+ : ThisVal.uadd_ov(OtherVal, Overflowed);
+ }
+
+ if (Overflow)
+ *Overflow = Overflowed;
+
+ return APFixedPoint(Result, CommonFXSema);
+}
+
+APFixedPoint APFixedPoint::sub(const APFixedPoint &Other,
+ bool *Overflow) const {
+ auto CommonFXSema = Sema.getCommonSemantics(Other.getSemantics());
+ APFixedPoint ConvertedThis = convert(CommonFXSema);
+ APFixedPoint ConvertedOther = Other.convert(CommonFXSema);
+ APSInt ThisVal = ConvertedThis.getValue();
+ APSInt OtherVal = ConvertedOther.getValue();
+ bool Overflowed = false;
+
+ APSInt Result;
+ if (CommonFXSema.isSaturated()) {
+ Result = CommonFXSema.isSigned() ? ThisVal.ssub_sat(OtherVal)
+ : ThisVal.usub_sat(OtherVal);
+ } else {
+ Result = ThisVal.isSigned() ? ThisVal.ssub_ov(OtherVal, Overflowed)
+ : ThisVal.usub_ov(OtherVal, Overflowed);
+ }
+
+ if (Overflow)
+ *Overflow = Overflowed;
+
+ return APFixedPoint(Result, CommonFXSema);
+}
+
+APFixedPoint APFixedPoint::mul(const APFixedPoint &Other,
+ bool *Overflow) const {
+ auto CommonFXSema = Sema.getCommonSemantics(Other.getSemantics());
+ APFixedPoint ConvertedThis = convert(CommonFXSema);
+ APFixedPoint ConvertedOther = Other.convert(CommonFXSema);
+ APSInt ThisVal = ConvertedThis.getValue();
+ APSInt OtherVal = ConvertedOther.getValue();
+ bool Overflowed = false;
+
+ // Widen the LHS and RHS so we can perform a full multiplication.
+ unsigned Wide = CommonFXSema.getWidth() * 2;
+ if (CommonFXSema.isSigned()) {
+ ThisVal = ThisVal.sextOrSelf(Wide);
+ OtherVal = OtherVal.sextOrSelf(Wide);
+ } else {
+ ThisVal = ThisVal.zextOrSelf(Wide);
+ OtherVal = OtherVal.zextOrSelf(Wide);
+ }
+
+ // Perform the full multiplication and downscale to get the same scale.
+ //
+ // Note that the right shifts here perform an implicit downwards rounding.
+ // This rounding could discard bits that would technically place the result
+ // outside the representable range. We interpret the spec as allowing us to
+ // perform the rounding step first, avoiding the overflow case that would
+ // arise.
+ APSInt Result;
+ if (CommonFXSema.isSigned())
+ Result = ThisVal.smul_ov(OtherVal, Overflowed)
+ .ashr(CommonFXSema.getScale());
+ else
+ Result = ThisVal.umul_ov(OtherVal, Overflowed)
+ .lshr(CommonFXSema.getScale());
+ assert(!Overflowed && "Full multiplication cannot overflow!");
+ Result.setIsSigned(CommonFXSema.isSigned());
+
+ // If our result lies outside of the representative range of the common
+ // semantic, we either have overflow or saturation.
+ APSInt Max = APFixedPoint::getMax(CommonFXSema).getValue()
+ .extOrTrunc(Wide);
+ APSInt Min = APFixedPoint::getMin(CommonFXSema).getValue()
+ .extOrTrunc(Wide);
+ if (CommonFXSema.isSaturated()) {
+ if (Result < Min)
+ Result = Min;
+ else if (Result > Max)
+ Result = Max;
+ } else
+ Overflowed = Result < Min || Result > Max;
+
+ if (Overflow)
+ *Overflow = Overflowed;
+
+ return APFixedPoint(Result.sextOrTrunc(CommonFXSema.getWidth()),
+ CommonFXSema);
+}
+
+APFixedPoint APFixedPoint::div(const APFixedPoint &Other,
+ bool *Overflow) const {
+ auto CommonFXSema = Sema.getCommonSemantics(Other.getSemantics());
+ APFixedPoint ConvertedThis = convert(CommonFXSema);
+ APFixedPoint ConvertedOther = Other.convert(CommonFXSema);
+ APSInt ThisVal = ConvertedThis.getValue();
+ APSInt OtherVal = ConvertedOther.getValue();
+ bool Overflowed = false;
+
+ // Widen the LHS and RHS so we can perform a full division.
+ unsigned Wide = CommonFXSema.getWidth() * 2;
+ if (CommonFXSema.isSigned()) {
+ ThisVal = ThisVal.sextOrSelf(Wide);
+ OtherVal = OtherVal.sextOrSelf(Wide);
+ } else {
+ ThisVal = ThisVal.zextOrSelf(Wide);
+ OtherVal = OtherVal.zextOrSelf(Wide);
+ }
+
+ // Upscale to compensate for the loss of precision from division, and
+ // perform the full division.
+ ThisVal = ThisVal.shl(CommonFXSema.getScale());
+ APSInt Result;
+ if (CommonFXSema.isSigned()) {
+ APInt Rem;
+ APInt::sdivrem(ThisVal, OtherVal, Result, Rem);
+ // If the quotient is negative and the remainder is nonzero, round
+ // towards negative infinity by subtracting epsilon from the result.
+ if (ThisVal.isNegative() != OtherVal.isNegative() && !Rem.isNullValue())
+ Result = Result - 1;
+ } else
+ Result = ThisVal.udiv(OtherVal);
+ Result.setIsSigned(CommonFXSema.isSigned());
+
+ // If our result lies outside of the representative range of the common
+ // semantic, we either have overflow or saturation.
+ APSInt Max = APFixedPoint::getMax(CommonFXSema).getValue()
+ .extOrTrunc(Wide);
+ APSInt Min = APFixedPoint::getMin(CommonFXSema).getValue()
+ .extOrTrunc(Wide);
+ if (CommonFXSema.isSaturated()) {
+ if (Result < Min)
+ Result = Min;
+ else if (Result > Max)
+ Result = Max;
+ } else
+ Overflowed = Result < Min || Result > Max;
+
+ if (Overflow)
+ *Overflow = Overflowed;
+
+ return APFixedPoint(Result.sextOrTrunc(CommonFXSema.getWidth()),
+ CommonFXSema);
+}
+
+APFixedPoint APFixedPoint::shl(unsigned Amt, bool *Overflow) const {
+ APSInt ThisVal = Val;
+ bool Overflowed = false;
+
+ // Widen the LHS.
+ unsigned Wide = Sema.getWidth() * 2;
+ if (Sema.isSigned())
+ ThisVal = ThisVal.sextOrSelf(Wide);
+ else
+ ThisVal = ThisVal.zextOrSelf(Wide);
+
+ // Clamp the shift amount at the original width, and perform the shift.
+ Amt = std::min(Amt, ThisVal.getBitWidth());
+ APSInt Result = ThisVal << Amt;
+ Result.setIsSigned(Sema.isSigned());
+
+ // If our result lies outside of the representative range of the
+ // semantic, we either have overflow or saturation.
+ APSInt Max = APFixedPoint::getMax(Sema).getValue().extOrTrunc(Wide);
+ APSInt Min = APFixedPoint::getMin(Sema).getValue().extOrTrunc(Wide);
+ if (Sema.isSaturated()) {
+ if (Result < Min)
+ Result = Min;
+ else if (Result > Max)
+ Result = Max;
+ } else
+ Overflowed = Result < Min || Result > Max;
+
+ if (Overflow)
+ *Overflow = Overflowed;
+
+ return APFixedPoint(Result.sextOrTrunc(Sema.getWidth()), Sema);
+}
+
+void APFixedPoint::toString(SmallVectorImpl<char> &Str) const {
+ APSInt Val = getValue();
+ unsigned Scale = getScale();
+
+ if (Val.isSigned() && Val.isNegative() && Val != -Val) {
+ Val = -Val;
+ Str.push_back('-');
+ }
+
+ APSInt IntPart = Val >> Scale;
+
+ // Add 4 digits to hold the value after multiplying 10 (the radix)
+ unsigned Width = Val.getBitWidth() + 4;
+ APInt FractPart = Val.zextOrTrunc(Scale).zext(Width);
+ APInt FractPartMask = APInt::getAllOnesValue(Scale).zext(Width);
+ APInt RadixInt = APInt(Width, 10);
+
+ IntPart.toString(Str, /*Radix=*/10);
+ Str.push_back('.');
+ do {
+ (FractPart * RadixInt)
+ .lshr(Scale)
+ .toString(Str, /*Radix=*/10, Val.isSigned());
+ FractPart = (FractPart * RadixInt) & FractPartMask;
+ } while (FractPart != 0);
+}
+
+APFixedPoint APFixedPoint::negate(bool *Overflow) const {
+ if (!isSaturated()) {
+ if (Overflow)
+ *Overflow =
+ (!isSigned() && Val != 0) || (isSigned() && Val.isMinSignedValue());
+ return APFixedPoint(-Val, Sema);
+ }
+
+ // We never overflow for saturation
+ if (Overflow)
+ *Overflow = false;
+
+ if (isSigned())
+ return Val.isMinSignedValue() ? getMax(Sema) : APFixedPoint(-Val, Sema);
+ else
+ return APFixedPoint(Sema);
+}
+
+APSInt APFixedPoint::convertToInt(unsigned DstWidth, bool DstSign,
+ bool *Overflow) const {
+ APSInt Result = getIntPart();
+ unsigned SrcWidth = getWidth();
+
+ APSInt DstMin = APSInt::getMinValue(DstWidth, !DstSign);
+ APSInt DstMax = APSInt::getMaxValue(DstWidth, !DstSign);
+
+ if (SrcWidth < DstWidth) {
+ Result = Result.extend(DstWidth);
+ } else if (SrcWidth > DstWidth) {
+ DstMin = DstMin.extend(SrcWidth);
+ DstMax = DstMax.extend(SrcWidth);
+ }
+
+ if (Overflow) {
+ if (Result.isSigned() && !DstSign) {
+ *Overflow = Result.isNegative() || Result.ugt(DstMax);
+ } else if (Result.isUnsigned() && DstSign) {
+ *Overflow = Result.ugt(DstMax);
+ } else {
+ *Overflow = Result < DstMin || Result > DstMax;
+ }
+ }
+
+ Result.setIsSigned(DstSign);
+ return Result.extOrTrunc(DstWidth);
+}
+
+const fltSemantics *APFixedPoint::promoteFloatSemantics(const fltSemantics *S) {
+ if (S == &APFloat::BFloat())
+ return &APFloat::IEEEdouble();
+ else if (S == &APFloat::IEEEhalf())
+ return &APFloat::IEEEsingle();
+ else if (S == &APFloat::IEEEsingle())
+ return &APFloat::IEEEdouble();
+ else if (S == &APFloat::IEEEdouble())
+ return &APFloat::IEEEquad();
+ llvm_unreachable("Could not promote float type!");
+}
+
+APFloat APFixedPoint::convertToFloat(const fltSemantics &FloatSema) const {
+ // For some operations, rounding mode has an effect on the result, while
+ // other operations are lossless and should never result in rounding.
+ // To signify which these operations are, we define two rounding modes here.
+ APFloat::roundingMode RM = APFloat::rmNearestTiesToEven;
+ APFloat::roundingMode LosslessRM = APFloat::rmTowardZero;
+
+ // Make sure that we are operating in a type that works with this fixed-point
+ // semantic.
+ const fltSemantics *OpSema = &FloatSema;
+ while (!Sema.fitsInFloatSemantics(*OpSema))
+ OpSema = promoteFloatSemantics(OpSema);
+
+ // Convert the fixed point value bits as an integer. If the floating point
+ // value does not have the required precision, we will round according to the
+ // given mode.
+ APFloat Flt(*OpSema);
+ APFloat::opStatus S = Flt.convertFromAPInt(Val, Sema.isSigned(), RM);
+
+ // If we cared about checking for precision loss, we could look at this
+ // status.
+ (void)S;
+
+ // Scale down the integer value in the float to match the correct scaling
+ // factor.
+ APFloat ScaleFactor(std::pow(2, -(int)Sema.getScale()));
+ bool Ignored;
+ ScaleFactor.convert(*OpSema, LosslessRM, &Ignored);
+ Flt.multiply(ScaleFactor, LosslessRM);
+
+ if (OpSema != &FloatSema)
+ Flt.convert(FloatSema, RM, &Ignored);
+
+ return Flt;
+}
+
+APFixedPoint APFixedPoint::getFromIntValue(const APSInt &Value,
+ const FixedPointSemantics &DstFXSema,
+ bool *Overflow) {
+ FixedPointSemantics IntFXSema = FixedPointSemantics::GetIntegerSemantics(
+ Value.getBitWidth(), Value.isSigned());
+ return APFixedPoint(Value, IntFXSema).convert(DstFXSema, Overflow);
+}
+
+APFixedPoint
+APFixedPoint::getFromFloatValue(const APFloat &Value,
+ const FixedPointSemantics &DstFXSema,
+ bool *Overflow) {
+ // For some operations, rounding mode has an effect on the result, while
+ // other operations are lossless and should never result in rounding.
+ // To signify which these operations are, we define two rounding modes here,
+ // even though they are the same mode.
+ APFloat::roundingMode RM = APFloat::rmTowardZero;
+ APFloat::roundingMode LosslessRM = APFloat::rmTowardZero;
+
+ const fltSemantics &FloatSema = Value.getSemantics();
+
+ if (Value.isNaN()) {
+ // Handle NaN immediately.
+ if (Overflow)
+ *Overflow = true;
+ return APFixedPoint(DstFXSema);
+ }
+
+ // Make sure that we are operating in a type that works with this fixed-point
+ // semantic.
+ const fltSemantics *OpSema = &FloatSema;
+ while (!DstFXSema.fitsInFloatSemantics(*OpSema))
+ OpSema = promoteFloatSemantics(OpSema);
+
+ APFloat Val = Value;
+
+ bool Ignored;
+ if (&FloatSema != OpSema)
+ Val.convert(*OpSema, LosslessRM, &Ignored);
+
+ // Scale up the float so that the 'fractional' part of the mantissa ends up in
+ // the integer range instead. Rounding mode is irrelevant here.
+ // It is fine if this overflows to infinity even for saturating types,
+ // since we will use floating point comparisons to check for saturation.
+ APFloat ScaleFactor(std::pow(2, DstFXSema.getScale()));
+ ScaleFactor.convert(*OpSema, LosslessRM, &Ignored);
+ Val.multiply(ScaleFactor, LosslessRM);
+
+ // Convert to the integral representation of the value. This rounding mode
+ // is significant.
+ APSInt Res(DstFXSema.getWidth(), !DstFXSema.isSigned());
+ Val.convertToInteger(Res, RM, &Ignored);
+
+ // Round the integral value and scale back. This makes the
+ // overflow calculations below work properly. If we do not round here,
+ // we risk checking for overflow with a value that is outside the
+ // representable range of the fixed-point semantic even though no overflow
+ // would occur had we rounded first.
+ ScaleFactor = APFloat(std::pow(2, -(int)DstFXSema.getScale()));
+ ScaleFactor.convert(*OpSema, LosslessRM, &Ignored);
+ Val.roundToIntegral(RM);
+ Val.multiply(ScaleFactor, LosslessRM);
+
+ // Check for overflow/saturation by checking if the floating point value
+ // is outside the range representable by the fixed-point value.
+ APFloat FloatMax = getMax(DstFXSema).convertToFloat(*OpSema);
+ APFloat FloatMin = getMin(DstFXSema).convertToFloat(*OpSema);
+ bool Overflowed = false;
+ if (DstFXSema.isSaturated()) {
+ if (Val > FloatMax)
+ Res = getMax(DstFXSema).getValue();
+ else if (Val < FloatMin)
+ Res = getMin(DstFXSema).getValue();
+ } else
+ Overflowed = Val > FloatMax || Val < FloatMin;
+
+ if (Overflow)
+ *Overflow = Overflowed;
+
+ return APFixedPoint(Res, DstFXSema);
+}
+
+} // namespace llvm