mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-06 07:34:45 +01:00
Implement Core.Negate for float literals (#7616)
Addresses part of issue raised in #7159 by implementing float.negate builtin for FloatLiteralValues The following code now compiles: ``` let a: f64 = -1.0; ``` To achieve this I switch the mantissa from being unsigned to signed. Lexed literals within source file will still always be unsigned, however it is now possible to create negative FloatLiteralValue constants. Main non-local changes this causes is: - All llvm::APInt parsing / printing calls flipped isSigned param - Zero extension replaced with sign extension - getActiveBits() replaced with getSignificantBits() for min bit width calculation
This commit is contained in:
+41
-27
@@ -431,6 +431,19 @@ static auto MakeIntResult(Context& context, SemIR::TypeId type_id,
|
||||
return MakeIntResult(context, type_id, result);
|
||||
}
|
||||
|
||||
// Converts a Real into a ConstantId.
|
||||
static auto MakeFloatLiteralResult(Context& context, Real real)
|
||||
-> SemIR::ConstantId {
|
||||
auto real_id = context.reals().Add(real);
|
||||
return MakeConstantResult(
|
||||
context,
|
||||
SemIR::FloatLiteralValue{
|
||||
.type_id =
|
||||
GetSingletonType(context, SemIR::FloatLiteralType::TypeInstId),
|
||||
.real_id = real_id},
|
||||
Phase::Concrete);
|
||||
}
|
||||
|
||||
// Converts an APFloat value into a ConstantId.
|
||||
static auto MakeFloatResult(Context& context, SemIR::TypeId type_id,
|
||||
llvm::APFloat value) -> SemIR::ConstantId {
|
||||
@@ -1438,7 +1451,7 @@ static auto RealToAPFloat(Context& context, RealId real_id,
|
||||
// Convert the real value to a string.
|
||||
llvm::SmallString<64> str;
|
||||
real_value.mantissa.toString(str, real_value.is_decimal ? 10 : 16,
|
||||
/*signed=*/false, /*formatAsCLiteral=*/true);
|
||||
/*signed=*/true, /*formatAsCLiteral=*/true);
|
||||
str += real_value.is_decimal ? "e" : "p";
|
||||
real_value.exponent.toStringSigned(str);
|
||||
|
||||
@@ -1550,20 +1563,10 @@ static auto PerformIntToFloatConvert(Context& context, SemIR::LocId loc_id,
|
||||
if (!dest_float_type) {
|
||||
// Target is Core.FloatLiteral, which is always exact.
|
||||
llvm::APInt mantissa = op_val;
|
||||
if (src_is_signed && op_val.isNegative()) {
|
||||
// FloatLiteral can only represent positive real values. Negative
|
||||
// literals are parsed as Negate(FloatLiteralValue).
|
||||
context.TODO(loc_id, "negative float literal conversion");
|
||||
return SemIR::ErrorInst::ConstantId;
|
||||
}
|
||||
auto real_id = context.reals().Add(
|
||||
Real{.mantissa = mantissa,
|
||||
.exponent = llvm::APInt(32, 0, /*isSigned=*/true),
|
||||
.is_decimal = true});
|
||||
return MakeConstantResult(
|
||||
context,
|
||||
SemIR::FloatLiteralValue{.type_id = dest_type_id, .real_id = real_id},
|
||||
Phase::Concrete);
|
||||
return MakeFloatLiteralResult(
|
||||
context, Real{.mantissa = mantissa,
|
||||
.exponent = llvm::APInt(32, 0, /*isSigned=*/true),
|
||||
.is_decimal = true});
|
||||
}
|
||||
|
||||
llvm::APFloat ap_float(dest_float_type->float_kind.Semantics());
|
||||
@@ -1702,19 +1705,19 @@ static auto ConvertRealLiteralToInt(Context& context, SemIR::LocId loc_id,
|
||||
// If the exponent is positive, base^exponent cannot be larger than the result
|
||||
// size. If it's negative, base^exponent can't be *much* larger than the
|
||||
// mantissa or we'd have computed a lower bound of 0 bits and bailed out.
|
||||
CARBON_CHECK(
|
||||
exponent_upper_bound <=
|
||||
std::max<unsigned>(mantissa.getActiveBits() * 2, bounds.upper_bound));
|
||||
CARBON_CHECK(exponent_upper_bound <=
|
||||
std::max<unsigned>(mantissa.getSignificantBits() * 2,
|
||||
bounds.upper_bound));
|
||||
|
||||
// Compute a bit-width in which we can safely compute the result. We need
|
||||
// enough space to store the mantissa, base^exponent, the result and a sign
|
||||
// bit, and the number 10 (4 bits).
|
||||
unsigned calc_width =
|
||||
std::max({mantissa.getActiveBits(), exponent_upper_bound,
|
||||
std::max({mantissa.getSignificantBits(), exponent_upper_bound,
|
||||
static_cast<unsigned>(bounds.upper_bound + 1), 4U});
|
||||
|
||||
// Compute the integer result.
|
||||
llvm::APInt integer_val = mantissa.zextOrTrunc(calc_width);
|
||||
llvm::APInt integer_val = mantissa.sextOrTrunc(calc_width);
|
||||
if (!real_val.is_decimal) {
|
||||
// Binary exponent (mantissa * 2^exponent).
|
||||
if (!exponent.isNegative()) {
|
||||
@@ -2178,16 +2181,27 @@ static auto PerformBuiltinUnaryFloatOp(Context& context,
|
||||
SemIR::BuiltinFunctionKind builtin_kind,
|
||||
SemIR::InstId arg_id)
|
||||
-> SemIR::ConstantId {
|
||||
CARBON_CHECK(builtin_kind == SemIR::BuiltinFunctionKind::FloatNegate,
|
||||
"Unexpected builtin kind");
|
||||
|
||||
if (auto literal =
|
||||
context.insts().TryGetAs<SemIR::FloatLiteralValue>(arg_id)) {
|
||||
auto real_val = context.reals().Get(literal->real_id);
|
||||
|
||||
// Check if negation would overflow.
|
||||
if (real_val.mantissa.isMinSignedValue()) {
|
||||
real_val.mantissa =
|
||||
real_val.mantissa.sext(real_val.mantissa.getBitWidth() + 1);
|
||||
}
|
||||
real_val.mantissa.negate();
|
||||
|
||||
return MakeFloatLiteralResult(context, std::move(real_val));
|
||||
}
|
||||
|
||||
auto op = context.insts().GetAs<SemIR::FloatValue>(arg_id);
|
||||
auto op_val = context.floats().Get(op.float_id);
|
||||
|
||||
switch (builtin_kind) {
|
||||
case SemIR::BuiltinFunctionKind::FloatNegate:
|
||||
op_val.changeSign();
|
||||
break;
|
||||
default:
|
||||
CARBON_FATAL("Unexpected builtin kind");
|
||||
}
|
||||
op_val.changeSign();
|
||||
|
||||
return MakeFloatResult(context, op.type_id, std::move(op_val));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user