Skip to content

Improve the interfaces for working with integer widths #20178

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 31, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -789,10 +789,12 @@ class IntegerLiteralExpr : public NumberLiteralExpr {
static IntegerLiteralExpr *
createFromUnsigned(ASTContext &C, unsigned value);

/// Returns the value of the literal, appropriately constructed in the
/// target type.
APInt getValue() const;
static APInt getValue(StringRef Text, unsigned BitWidth, bool Negative);

APInt getRawMagnitude() const;
/// Returns the raw value of the literal without any truncation.
APInt getRawValue() const;

static bool classof(const Expr *E) {
return E->getKind() == ExprKind::IntegerLiteral;
Expand Down
34 changes: 28 additions & 6 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -1346,13 +1346,17 @@ END_CAN_TYPE_WRAPPER(BuiltinVectorType, BuiltinType)
class BuiltinIntegerWidth {
/// Tag values for abstract integer sizes.
enum : unsigned {
Least_SpecialValue = ~2U,
/// Inhabitants stolen for use as DenseMap special values.
DenseMapEmpty = ~0U,
DenseMapTombstone = ~1U,

/// An arbitrary-precision integer.
ArbitraryWidth = ~2U,

/// The size of a pointer on the target system.
PointerWidth = ~0U,
PointerWidth = ~3U,

/// Inhabitants stolen for use as DenseMap special values.
DenseMapEmpty = ~1U,
DenseMapTombstone = ~2U,
Least_SpecialValue = ~3U,
};

unsigned RawValue;
Expand All @@ -1372,6 +1376,10 @@ class BuiltinIntegerWidth {
static BuiltinIntegerWidth pointer() {
return BuiltinIntegerWidth(PointerWidth);
}

static BuiltinIntegerWidth arbitrary() {
return BuiltinIntegerWidth(ArbitraryWidth);
}

/// Is this a fixed width?
bool isFixedWidth() const { return RawValue < Least_SpecialValue; }
Expand All @@ -1384,6 +1392,9 @@ class BuiltinIntegerWidth {

/// Is this the abstract target pointer width?
bool isPointerWidth() const { return RawValue == PointerWidth; }

/// Is this the abstract arbitrary-width value?
bool isArbitraryWidth() const { return RawValue == ArbitraryWidth; }

/// Get the least supported value for the width.
///
Expand All @@ -1393,6 +1404,8 @@ class BuiltinIntegerWidth {
return getFixedWidth();
if (isPointerWidth())
return 32;
if (isArbitraryWidth())
return 1;
llvm_unreachable("impossible width value");
}

Expand All @@ -1404,8 +1417,16 @@ class BuiltinIntegerWidth {
return getFixedWidth();
if (isPointerWidth())
return 64;
if (isArbitraryWidth())
return ~0U;
llvm_unreachable("impossible width value");
}

/// Parse a value of this bit-width.
///
/// If the radix is 0, it is autosensed.
APInt parse(StringRef text, unsigned radix, bool negate,
bool *hadError = nullptr) const;

friend bool operator==(BuiltinIntegerWidth a, BuiltinIntegerWidth b) {
return a.RawValue == b.RawValue;
Expand Down Expand Up @@ -1440,7 +1461,8 @@ class BuiltinIntegerType : public BuiltinType {
return get(BuiltinIntegerWidth::pointer(), C);
}

/// Return the bit width of the integer.
/// Return the bit width of the integer. Always returns a non-arbitrary
/// width.
BuiltinIntegerWidth getWidth() const {
return Width;
}
Expand Down
54 changes: 54 additions & 0 deletions include/swift/Basic/APIntMap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//===--- APIntMap.h - A map with APInts as the keys -------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// LLVM does not allow arbitrary APInts to be the keys of a DenseMap because
// APInts are only comparable if they have the same bit-width. This map
// implementation assumes that its keys will always be constrained to their
// minimum width, so it's not a general-purpose structure, but it does work.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_BASIC_APINTMAP_H
#define SWIFT_BASIC_APINTMAP_H

#include "llvm/ADT/APInt.h"
#include "llvm/ADT/DenseMap.h"
#include "swift/Basic/LLVM.h"

namespace swift {

struct WidthPreservingAPIntDenseMapInfo {
// For the special values, we use -1 with a bit-width that isn't minimal
// for the value, then use a parser that always produces values with
// minimal bit-widths so that we don't get a conflict.
static inline APInt getEmptyKey() {
return APInt::getAllOnesValue(/*bitwidth*/2);
}
static inline APInt getTombstoneKey() {
return APInt::getAllOnesValue(/*bitwidth*/3);
}

static unsigned getHashValue(const APInt &Key) {
return static_cast<unsigned>(hash_value(Key));
}

static bool isEqual(const APInt &LHS, const APInt &RHS) {
return LHS.getBitWidth() == RHS.getBitWidth() && LHS == RHS;
}
};

template <class Value>
using APIntMap = llvm::DenseMap<APInt, Value, WidthPreservingAPIntDenseMapInfo>;

}

#endif
1 change: 1 addition & 0 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3032,6 +3032,7 @@ Type ErrorType::get(Type originalType) {

BuiltinIntegerType *BuiltinIntegerType::get(BuiltinIntegerWidth BitWidth,
const ASTContext &C) {
assert(!BitWidth.isArbitraryWidth());
BuiltinIntegerType *&Result = C.getImpl().IntegerTypes[BitWidth];
if (Result == nullptr)
Result = new (C, AllocationArena::Permanent) BuiltinIntegerType(BitWidth,C);
Expand Down
103 changes: 69 additions & 34 deletions lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -849,46 +849,81 @@ IntegerLiteralExpr * IntegerLiteralExpr::createFromUnsigned(ASTContext &C, unsig
return new (C) IntegerLiteralExpr(Text, SourceLoc(), /*implicit*/ true);
}

/// A wrapper around LLVM::getAsInteger that can be used on Swift interger
/// literals. It avoids misinterpreting decimal numbers prefixed with 0 as
/// octal numbers.
static bool getAsInteger(StringRef Text, llvm::APInt &Value) {
// swift encodes octal differently from C
bool IsCOctal = Text.size() > 1 && Text[0] == '0' && isdigit(Text[1]);
return Text.getAsInteger(IsCOctal ? 10 : 0, Value);
APInt IntegerLiteralExpr::getRawValue() const {
return BuiltinIntegerWidth::arbitrary().parse(getDigitsText(), /*radix*/0,
isNegative());
}

static APInt getIntegerLiteralValue(bool IsNegative, StringRef Text,
unsigned BitWidth) {
llvm::APInt Value(BitWidth, 0);
bool Error = getAsInteger(Text, Value);
assert(!Error && "Invalid IntegerLiteral formed"); (void)Error;
if (IsNegative)
Value = -Value;
if (Value.getBitWidth() != BitWidth)
Value = Value.sextOrTrunc(BitWidth);
return Value;
APInt IntegerLiteralExpr::getValue() const {
assert(!getType().isNull() && "Semantic analysis has not completed");
assert(!getType()->hasError() && "Should have a valid type");
auto width = getType()->castTo<BuiltinIntegerType>()->getWidth();
return width.parse(getDigitsText(), /*radix*/ 0, isNegative());
}

APInt IntegerLiteralExpr::getValue(StringRef Text, unsigned BitWidth, bool Negative) {
return getIntegerLiteralValue(Negative, Text, BitWidth);
}
APInt BuiltinIntegerWidth::parse(StringRef text, unsigned radix, bool negate,
bool *hadError) const {
if (hadError) *hadError = false;

/// Returns the raw magnitude of the literal text without any truncation.
APInt IntegerLiteralExpr::getRawMagnitude() const {
llvm::APInt Value(64, 0);
bool Error = getAsInteger(getDigitsText(), Value);
assert(!Error && "Invalid IntegerLiteral formed");
(void)Error;
return Value;
}
// Parse an unsigned value from the string.
APInt value;

APInt IntegerLiteralExpr::getValue() const {
assert(!getType().isNull() && "Semantic analysis has not completed");
assert(!getType()->hasError() && "Should have a valid type");
return getIntegerLiteralValue(
isNegative(), getDigitsText(),
getType()->castTo<BuiltinIntegerType>()->getGreatestWidth());
// Swift doesn't treat a leading zero as signifying octal, but
// StringRef::getAsInteger does. Force decimal parsing in this case.
if (radix == 0 && text.size() >= 2 && text[0] == '0' && isdigit(text[1]))
radix = 10;

bool error = text.getAsInteger(radix, value);
if (error) {
if (hadError) *hadError = true;
return value;
}

// If we're producing an arbitrary-precision value, we don't need to do
// much additional processing.
if (isArbitraryWidth()) {
// The parser above always produces a non-negative value, so if the sign
// bit is set we need to give it some breathing room.
if (value.isNegative())
value = value.zext(value.getBitWidth() + 1);
assert(!value.isNegative());

// Now we can safely negate.
if (negate) {
value = -value;
assert(value.isNegative() || value.isNullValue());
}

// Truncate down to the minimum number of bits required to express
// this value exactly.
auto requiredBits = value.getMinSignedBits();
if (value.getBitWidth() > requiredBits)
value = value.trunc(requiredBits);

// If we have a fixed-width type (including abstract ones), we need to do
// fixed-width transformations, which can overflow.
} else {
unsigned width = getGreatestWidth();

// The overflow diagnostics in this case can't be fully correct because
// we don't know whether we're supposed to be producing a signed number
// or an unsigned one.

if (hadError && value.getActiveBits() > width)
*hadError = true;
value = value.zextOrTrunc(width);

if (negate) {
value = -value;

if (hadError && !value.isNegative())
*hadError = true;
}

assert(value.getBitWidth() == width);
}

return value;
}

static APFloat getFloatLiteralValue(bool IsNegative, StringRef Text,
Expand Down
6 changes: 3 additions & 3 deletions lib/IRGen/GenEnum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1077,9 +1077,9 @@ namespace {
auto intExpr = cast<IntegerLiteralExpr>(target->getRawValueExpr());
auto intType = getDiscriminatorType();

APInt intValue = IntegerLiteralExpr::getValue(intExpr->getDigitsText(),
intType->getBitWidth(),
intExpr->isNegative());
APInt intValue =
BuiltinIntegerWidth::fixed(intType->getBitWidth())
.parse(intExpr->getDigitsText(), /*radix*/ 0, intExpr->isNegative());

return intValue.getZExtValue();
}
Expand Down
12 changes: 1 addition & 11 deletions lib/Sema/CSGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1220,17 +1220,7 @@ namespace {
if (IntegerLiteralExpr *intLit = dyn_cast<IntegerLiteralExpr>(expr)) {
unsigned maxWidth =
maxIntType->castTo<BuiltinIntegerType>()->getGreatestWidth();
APInt magnitude = intLit->getRawMagnitude();
unsigned magWidth = magnitude.getActiveBits();
bool isNegative = intLit->isNegative();

// Compute the literal bit width in the signed two's complement form.
// This is generally one more than the magnitude width, but is the
// same when the literal is of the form -2^i (for some Nat `i`).
unsigned signedLitWidth =
(isNegative && (magnitude.countTrailingZeros() == magWidth - 1))
? magWidth
: (magWidth + 1);
unsigned signedLitWidth = intLit->getRawValue().getMinSignedBits();

if (signedLitWidth > maxWidth) { // overflow?
CS.TC.diagnose(expr->getLoc(),
Expand Down
26 changes: 3 additions & 23 deletions lib/Sema/TypeCheckSwitchStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#include "swift/AST/Pattern.h"
#include "swift/Basic/STLExtras.h"

#include <llvm/ADT/APInt.h>
#include "swift/Basic/APIntMap.h"
#include <llvm/ADT/APFloat.h>

#include <numeric>
Expand All @@ -31,22 +31,6 @@ using namespace swift;
#define DEBUG_TYPE "TypeCheckSwitchStmt"

namespace {
struct DenseMapAPIntKeyInfo {
static inline APInt getEmptyKey() { return APInt(); }

static inline APInt getTombstoneKey() {
return APInt::getAllOnesValue(/*bitwidth*/1);
}

static unsigned getHashValue(const APInt &Key) {
return static_cast<unsigned>(hash_value(Key));
}

static bool isEqual(const APInt &LHS, const APInt &RHS) {
return LHS.getBitWidth() == RHS.getBitWidth() && LHS == RHS;
}
};

struct DenseMapAPFloatKeyInfo {
static inline APFloat getEmptyKey() { return APFloat(APFloat::Bogus(), 1); }
static inline APFloat getTombstoneKey() { return APFloat(APFloat::Bogus(), 2); }
Expand Down Expand Up @@ -858,7 +842,7 @@ namespace {
TypeChecker &TC;
const SwitchStmt *Switch;
const DeclContext *DC;
llvm::DenseMap<APInt, Expr *, ::DenseMapAPIntKeyInfo> IntLiteralCache;
APIntMap<Expr *> IntLiteralCache;
llvm::DenseMap<APFloat, Expr *, ::DenseMapAPFloatKeyInfo> FloatLiteralCache;
llvm::DenseMap<StringRef, Expr *> StringLiteralCache;

Expand Down Expand Up @@ -886,12 +870,8 @@ namespace {
return !cacheVal.second;
}
case ExprKind::IntegerLiteral: {
// FIXME: The magic number 128 is bad and we should actually figure out
// the bitwidth. But it's too early in Sema to get it.
auto *ILE = cast<IntegerLiteralExpr>(EL);
auto cacheVal =
IntLiteralCache.insert(
{ILE->getValue(ILE->getDigitsText(), 128, ILE->isNegative()), ILE});
auto cacheVal = IntLiteralCache.insert({ILE->getRawValue(), ILE});
PrevPattern = (cacheVal.first != IntLiteralCache.end())
? cacheVal.first->getSecond()
: nullptr;
Expand Down