Address Operations

Addresses

template<typename EXTENT>
class address_slice

The correctness of address operations is critical to the correctness of ChampSim.

This class is a strong type for addresses to prevent bugs that would result from unchecked operations on raw integers. The primary benefit is this: Most address operation bugs are compiler errors. Those that are not are usually runtime exceptions. If you need to manipulate the bits of an address, you can, but you must explicitly enter an unsafe mode.

This class is a generalization of a subset of address bits. Implicit conversions between address slices of different extents are compile-time errors, providing a measure of safety. New slices must be explicitly constructed.

// LOG2_PAGE_SIZE = 12
// LOG2_BLOCK_SIZE = 6
address full_addr{0xffff'ffff};
block_number block{full_addr}; // 0xffff'ffc0
page_number page{full_addr}; // 0xffff'f000

All address slices can take their extent as a constructor parameter.

address_slice st_full_addr{static_extent<64_b,0_b>{},0xffff'ffff};
address_slice st_block{static_extent<64_b, champsim::data::bits{LOG2_BLOCK_SIZE}>{}, st_full_addr}; // 0xffff'ffc0
address_slice st_page{static_extent<64_b, champsim::data::bits{LOG2_PAGE_SIZE}>{}, st_full_addr}; // 0xffff'f000

address_slice dyn_full_addr{dynamic_extent{64_b,0_b},0xffff'ffff};
address_slice dyn_block{dynamic_extent{64_b, champsim::data::bits{LOG2_BLOCK_SIZE}}, dyn_full_addr}; // 0xffff'ffc0
address_slice dyn_page{dynamic_extent{64_b, champsim::data::bits{LOG2_PAGE_SIZE}}, dyn_full_addr}; // 0xffff'f000

For static extents, it can be useful to explicitly specify the template parameter.

address_slice<static_extent<64_b,0_b>> st_full_addr{0xffff'ffff};
address_slice<static_extent<champsim::data::bits{LOG2_BLOCK_SIZE}, 0_b>> st_block{st_full_addr}; // 0x3f
address_slice<static_extent<champsim::data::bits{LOG2_PAGE_SIZE}, 0_b>> st_page{st_full_addr}; // 0xfff

The address slices have a constructor that accepts a uint64_t. No bit shifting is performed in these constructors. The argument is assumed to be in the domain of the slice.

champsim::block_number block{0xffff}; // 0x003f'ffc0

Relative slicing is possible with member functions:

address_slice<static_extent<24_b,12_b>>::slice(static_extent<8_b,4_b>{}) -> address_slice<static_extent<20_b,16_b>>
address_slice<static_extent<24_b,12_b>>::slice<8_b,4_b>() -> address_slice<static_extent<20_b,16_b>>
address_slice<static_extent<24_b,12_b>>::slice_upper<4_b>() -> address_slice<static_extent<24_b,16_b>>
address_slice<static_extent<24_b,12_b>>::slice_lower<8_b>() -> address_slice<static_extent<20_b,12_b>>

The offset between two addresses can be found with

auto champsim::offset(champsim::address base, champsim::address other) -> champsim::address::difference_type
auto champsim::uoffset(champsim::address base, champsim::address other) -> std::make_unsigned_t<champsim::address::difference_type>

The function is a template, so any address slice is accepted, but the two arguments must be of the same type. For the first function, the return type is signed and the conversion is safe against overflows, where it will throw an exception. For the second function, the return type is unsigned and the ‘other’ argument must succeed ‘base’, or the function will throw an exception.

Address slices also support addition and subtraction with signed integers. The arguments to this arithmetic are in the domain of the type.

champsim::block_number block{0xffff}; // 0x003f'ffc0
block += 1; // 0x0040'0000

Two or more slices can be spliced together. Later arguments takes priority over the first, and the result type has an extent that is a superset of all slices.

champsim::splice(champsim::page_number{0xaaa}, champsim::page_offset{0xbbb}) == champsim::address{0xaaabbb};

Sometimes, it is necessary to directly manipulate the bits of an address in a way that these strong types do not allow. Additionally, sometimes slices must be used as array indices. In those cases, the address_slice<>::to<T>() function performs a checked cast to the type.

champsim::address addr{0xffff'ffff};
class Foo {};
std::array<Foo, 256> foos = {};
auto the_foo = foos.at(addr.slice_lower<8_b>().to<std::size_t>());
Template Parameters:

EXTENT – One of champsim::static_extent<>, champsim::dynamic_extent, or one of the page- or block-sized extents.

Public Types

using extent_type = EXTENT

The extent passed as a template parameter.

using underlying_type = uint64_t

The underlying representation of the address.

using difference_type = std::make_signed_t<underlying_type>

The type of an offset between two addresses.

Public Functions

inline constexpr address_slice() noexcept(is_static)

Default-initialize the slice. This constructor is invalid for dynamically-sized extents.

inline explicit constexpr address_slice(underlying_type val) noexcept(is_static)

Initialize the slice with the given raw value. This constructor is invalid for dynamically-sized extents.

template<typename OTHER_EXT>
inline explicit constexpr address_slice(const address_slice<OTHER_EXT> &val) noexcept(is_static)

Initialize the slice with the given slice, shifting and masking if necessary. If this extent is dynamic, it will have the same bounds as the given slice. If the conversion is a widening, the widened bits will be 0.

template<typename OTHER_EXT>
inline constexpr address_slice(extent_type ext, const address_slice<OTHER_EXT> &val) noexcept(is_static)

Initialize the slice with the given slice, shifting and masking if necessary. The extent type can be deduced from the first argument. If the conversion is a widening, the widened bits will be 0.

inline constexpr address_slice(extent_type ext, underlying_type val) noexcept(is_static)

Initialize the slice with the given value. The extent type can be deduced from the first argument. If the conversion is a widening, the widened bits will be 0.

template<typename T>
inline constexpr T to() const

Unwrap the value of this slice as a raw integer value.

inline constexpr bool operator==(self_type other) const noexcept(is_static)

Compare with another address slice for equality.

Throws:

std::invalid_argument – If the extents do not match

inline constexpr bool operator<(self_type other) const noexcept(is_static)

Compare with another address slice for ordering.

Throws:

std::invalid_argument – If the extents do not match

inline constexpr bool operator!=(self_type other) const noexcept(is_static)

Compare with another address slice for inequality.

Throws:

std::invalid_argument – If the extents do not match

inline constexpr bool operator<=(self_type other) const noexcept(is_static)

Compare with another address slice for ordering.

Throws:

std::invalid_argument – If the extents do not match

inline constexpr bool operator>(self_type other) const noexcept(is_static)

Compare with another address slice for ordering.

Throws:

std::invalid_argument – If the extents do not match

inline constexpr bool operator>=(self_type other) const noexcept(is_static)

Compare with another address slice for ordering.

Throws:

std::invalid_argument – If the extents do not match

inline constexpr self_type &operator+=(difference_type delta)

Increment the slice in place by the given amount. The delta is interpreted in the domain of the slice. That is, the delta need not be scaled by 1 << slice.lower_extent().

inline constexpr self_type &operator+=(champsim::data::bytes delta)

Increment the slice in place by the given amount. The delta is interpreted in the domain of the slice. That is, the delta need not be scaled by 1 << slice.lower_extent().

inline constexpr self_type operator+(difference_type delta) const

Increment the slice by the given amount. The delta is interpreted in the domain of the slice. That is, the delta need not be scaled by 1 << slice.lower_extent().

inline constexpr self_type operator+(champsim::data::bytes delta) const

Increment the slice by the given amount. The delta is interpreted in the domain of the slice. That is, the delta need not be scaled by 1 << slice.lower_extent().

inline constexpr self_type &operator-=(difference_type delta)

Decrement the slice in place by the given amount. The delta is interpreted in the domain of the slice. That is, the delta need not be scaled by 1 << slice.lower_extent().

inline constexpr self_type &operator-=(champsim::data::bytes delta)

Decrement the slice in place by the given amount. The delta is interpreted in the domain of the slice. That is, the delta need not be scaled by 1 << slice.lower_extent().

inline constexpr self_type operator-(difference_type delta) const

Decrement the slice by the given amount. The delta is interpreted in the domain of the slice. That is, the delta need not be scaled by 1 << slice.lower_extent().

inline constexpr self_type operator-(champsim::data::bytes delta) const

Decrement the slice by the given amount. The delta is interpreted in the domain of the slice. That is, the delta need not be scaled by 1 << slice.lower_extent().

inline constexpr self_type &operator++()

Increment the slice by one.

inline constexpr self_type operator++(int)

Increment the slice by one.

inline constexpr self_type &operator--()

Decrement the slice by one.

inline constexpr self_type operator--(int)

Decrement the slice by one.

template<typename SUB_EXTENT>
inline auto slice(SUB_EXTENT subextent) const noexcept(is_static && detail::extent_is_static<SUB_EXTENT>)

Perform a slice on this address. The given extent should be relative to this slice’s extent. If either this extent or the subextent are runtime-sized, the result will have a runtime-sized extent. Otherwise, the extent will be statically-sized.

template<champsim::data::bits new_upper, champsim::data::bits new_lower>
inline auto slice() const noexcept

Perform a slice on this address. The given extent should be relative to this slice’s extent. This is a synonym for slice(static_extent<new_upper, new_lower>{}).

template<champsim::data::bits new_lower>
inline auto slice_upper() const noexcept

Slice the upper bits, ending with the given bit relative to the lower extent of this. If this slice is statically-sized, the result will be statically-sized.

template<champsim::data::bits new_upper>
inline auto slice_lower() const noexcept

Slice the lower bits, ending with the given bit relative to the lower extent of this. If this slice is statically-sized, the result will be statically-sized.

inline auto slice_upper(champsim::data::bits new_lower) const

Slice the upper bits, ending with the given bit relative to the lower extent of this. The result of this will always be runtime-sized.

inline auto slice_lower(champsim::data::bits new_upper) const

Slice the lower bits, ending with the given bit relative to the lower extent of this. The result of this will always be runtime-sized.

template<champsim::data::bits split_loc>
inline auto split() const

Split the slice into an upper and lower slice.

inline auto split(champsim::data::bits split_loc) const

Split the slice into an upper and lower slice.

inline constexpr auto upper_extent() const noexcept

Get the upper portion of the extent.

inline constexpr auto lower_extent() const noexcept

Get the lower portion of the extent.

Public Static Attributes

static constexpr champsim::data::bits bits = {std::numeric_limits<underlying_type>::digits}

The maximum width of any address slice. This is not the width of this slice, which can be found by slice.upper_extent() - slice.lower_extent().

template<typename Extent>
constexpr auto champsim::offset(address_slice<Extent> base, address_slice<Extent> other) -> typename address_slice<Extent>::difference_type

Find the offset between two slices with the same types.

Throws:

overflow_error – if the difference cannot be represented in the difference type

template<typename Extent>
constexpr auto champsim::uoffset(address_slice<Extent> base, address_slice<Extent> other) -> std::make_unsigned_t<typename address_slice<Extent>::difference_type>

Find the offset between two slices with the same types, where the first element must be less than or equal to than the second. The return type of this function is unsigned.

Throws:

overflow_error – if the difference cannot be represented in the difference type

template<typename ...Extents>
constexpr auto champsim::splice(address_slice<Extents>... slices)

Join address slices together. Later slices will overwrite bits from earlier slices. The extent of the returned slice is the superset of all slices. If all of the slices are statically-sized, the result will be statically-sized as well.

Convenience typedefs

Champsim provides five specializations of an address slice in inc/champsim.h

using champsim::address = address_slice<static_extent<champsim::data::bits{std::numeric_limits<uint64_t>::digits}, champsim::data::bits{}>>

Convenience definitions for commmon address slices

using champsim::page_number = address_slice<page_number_extent>
using champsim::page_offset = address_slice<page_offset_extent>
using champsim::block_number = address_slice<block_number_extent>
using champsim::block_offset = address_slice<block_offset_extent>

Extents

template<champsim::data::bits UP, champsim::data::bits LOW>
struct static_extent

An extent with compile-time size

Public Static Attributes

static constexpr champsim::data::bits upper = {UP}

The upper limit of the extent. Generally, this is not inclusive.

static constexpr champsim::data::bits lower = {LOW}

The lower limit of the extent. This is generally inclusive.

struct dynamic_extent

An extent with runtime size

Subclassed by champsim::block_number_extent, champsim::block_offset_extent, champsim::page_number_extent, champsim::page_offset_extent

Public Functions

inline constexpr dynamic_extent(champsim::data::bits up, champsim::data::bits low)

Initialize the extent with the given upper and lower extents. The upper must be greater than or equal to the lower.

inline constexpr dynamic_extent(champsim::data::bits low, std::size_t size)

Initialize the extent with a lower extent and size.

Public Members

champsim::data::bits upper

The upper limit of the extent. Generally, this is not inclusive.

champsim::data::bits lower

The lower limit of the extent. This is generally inclusive.

struct page_number_extent : public champsim::dynamic_extent

An extent that is always the size of a page number

Public Functions

page_number_extent()

This extent can only be default-initialized. It will have upper == champsim::address::bits and lower == LOG2_PAGE_SIZE.

struct page_offset_extent : public champsim::dynamic_extent

An extent that is always the size of a page offset

Public Functions

page_offset_extent()

This extent can only be default-initialized. It will have upper == LOG2_PAGE_SIZE and lower == 0.

struct block_number_extent : public champsim::dynamic_extent

An extent that is always the size of a block number

Public Functions

block_number_extent()

This extent can only be default-initialized. It will have upper == champsim::address::bits and lower == LOG2_BLOCK_SIZE.

struct block_offset_extent : public champsim::dynamic_extent

An extent that is always the size of a block offset

Public Functions

block_offset_extent()

This extent can only be default-initialized. It will have upper == LOG2_BLOCK_SIZE and lower == 0.