-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[CodeGen] Use 128bits for LaneBitmask. #111157
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,72 +29,120 @@ | |
#ifndef LLVM_MC_LANEBITMASK_H | ||
#define LLVM_MC_LANEBITMASK_H | ||
|
||
#include "llvm/ADT/APInt.h" | ||
#include "llvm/ADT/SmallString.h" | ||
#include "llvm/Support/Compiler.h" | ||
#include "llvm/Support/Format.h" | ||
#include "llvm/Support/MathExtras.h" | ||
#include "llvm/Support/Printable.h" | ||
#include "llvm/Support/raw_ostream.h" | ||
#include <utility> | ||
|
||
namespace llvm { | ||
|
||
struct LaneBitmask { | ||
// When changing the underlying type, change the format string as well. | ||
using Type = uint64_t; | ||
enum : unsigned { BitWidth = 8*sizeof(Type) }; | ||
constexpr static const char *const FormatStr = "%016llX"; | ||
struct LaneBitmask { | ||
static constexpr unsigned int BitWidth = 128; | ||
|
||
constexpr LaneBitmask() = default; | ||
explicit constexpr LaneBitmask(Type V) : Mask(V) {} | ||
|
||
constexpr bool operator== (LaneBitmask M) const { return Mask == M.Mask; } | ||
constexpr bool operator!= (LaneBitmask M) const { return Mask != M.Mask; } | ||
constexpr bool operator< (LaneBitmask M) const { return Mask < M.Mask; } | ||
constexpr bool none() const { return Mask == 0; } | ||
constexpr bool any() const { return Mask != 0; } | ||
constexpr bool all() const { return ~Mask == 0; } | ||
|
||
constexpr LaneBitmask operator~() const { | ||
return LaneBitmask(~Mask); | ||
} | ||
constexpr LaneBitmask operator|(LaneBitmask M) const { | ||
return LaneBitmask(Mask | M.Mask); | ||
} | ||
constexpr LaneBitmask operator&(LaneBitmask M) const { | ||
return LaneBitmask(Mask & M.Mask); | ||
} | ||
LaneBitmask &operator|=(LaneBitmask M) { | ||
Mask |= M.Mask; | ||
return *this; | ||
} | ||
LaneBitmask &operator&=(LaneBitmask M) { | ||
Mask &= M.Mask; | ||
return *this; | ||
explicit LaneBitmask(APInt V) { | ||
switch (V.getBitWidth()) { | ||
case BitWidth: | ||
Mask[0] = V.getRawData()[0]; | ||
Mask[1] = V.getRawData()[1]; | ||
break; | ||
default: | ||
llvm_unreachable("Unsupported bitwidth"); | ||
} | ||
} | ||
constexpr explicit LaneBitmask(uint64_t Lo = 0, uint64_t Hi = 0) : Mask{Lo, Hi} {} | ||
|
||
constexpr Type getAsInteger() const { return Mask; } | ||
constexpr bool operator==(LaneBitmask M) const { | ||
return Mask[0] == M.Mask[0] && Mask[1] == M.Mask[1]; | ||
} | ||
constexpr bool operator!=(LaneBitmask M) const { | ||
return Mask[0] != M.Mask[0] || Mask[1] != M.Mask[1]; | ||
} | ||
constexpr bool operator<(LaneBitmask M) const { | ||
return Mask[1] < M.Mask[1] || (Mask[1] == M.Mask[1] && Mask[0] < M.Mask[0]); | ||
} | ||
constexpr bool none() const { return Mask[0] == 0 && Mask[1] == 0; } | ||
constexpr bool any() const { return Mask[0] != 0 || Mask[1] != 0; } | ||
constexpr bool all() const { return ~Mask[0] == 0 && ~Mask[1] == 0; } | ||
|
||
unsigned getNumLanes() const { return llvm::popcount(Mask); } | ||
unsigned getHighestLane() const { | ||
return Log2_64(Mask); | ||
} | ||
constexpr LaneBitmask operator~() const { return LaneBitmask(~Mask[0], ~Mask[1]); } | ||
constexpr LaneBitmask operator|(LaneBitmask M) const { | ||
return LaneBitmask(Mask[0] | M.Mask[0], Mask[1] | M.Mask[1]); | ||
} | ||
constexpr LaneBitmask operator&(LaneBitmask M) const { | ||
return LaneBitmask(Mask[0] & M.Mask[0], Mask[1] & M.Mask[1]); | ||
} | ||
LaneBitmask &operator|=(LaneBitmask M) { | ||
Mask[0] |= M.Mask[0]; | ||
Mask[1] |= M.Mask[1]; | ||
return *this; | ||
} | ||
LaneBitmask &operator&=(LaneBitmask M) { | ||
Mask[0] &= M.Mask[0]; | ||
Mask[1] &= M.Mask[1]; | ||
return *this; | ||
} | ||
|
||
static constexpr LaneBitmask getNone() { return LaneBitmask(0); } | ||
static constexpr LaneBitmask getAll() { return ~LaneBitmask(0); } | ||
static constexpr LaneBitmask getLane(unsigned Lane) { | ||
return LaneBitmask(Type(1) << Lane); | ||
} | ||
APInt getAsAPInt() const { return APInt(BitWidth, {Mask[0], Mask[1]}); } | ||
constexpr std::pair<uint64_t, uint64_t> getAsPair() const { return {Mask[0], Mask[1]}; } | ||
|
||
private: | ||
Type Mask = 0; | ||
}; | ||
unsigned getNumLanes() const { | ||
return Mask[1] ? llvm::popcount(Mask[1]) + llvm::popcount(Mask[0]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would hope you could rely on modern host machines having a fast popcount instruction so you don't need to special-case |
||
: llvm::popcount(Mask[0]); | ||
} | ||
unsigned getHighestLane() const { | ||
return Mask[1] ? Log2_64(Mask[1]) + 64 : Log2_64(Mask[0]); | ||
} | ||
|
||
/// Create Printable object to print LaneBitmasks on a \ref raw_ostream. | ||
inline Printable PrintLaneMask(LaneBitmask LaneMask) { | ||
return Printable([LaneMask](raw_ostream &OS) { | ||
OS << format(LaneBitmask::FormatStr, LaneMask.getAsInteger()); | ||
}); | ||
static constexpr LaneBitmask getNone() { return LaneBitmask(0, 0); } | ||
static constexpr LaneBitmask getAll() { return ~LaneBitmask(0, 0); } | ||
static constexpr LaneBitmask getLane(unsigned Lane) { | ||
return Lane >= 64 ? LaneBitmask(0, 1ULL << (Lane % 64)) | ||
: LaneBitmask(1ULL << Lane, 0); | ||
} | ||
|
||
private: | ||
uint64_t Mask[2]; | ||
}; | ||
|
||
/// Create Printable object to print LaneBitmasks on a \ref raw_ostream. | ||
/// If \p FormatAsCLiterals is true, it will print the bitmask as | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need the option? Please can we just print it as a single hex literal with lots of digits? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is mostly to avoid having to update many tests that rely on a pretty printed format. Changing it would require changing various tests (which I'm happy to do if that's preferred). |
||
/// a hexadecimal C literal with zero padding, or a list of such C literals if | ||
/// the value cannot be represented in 64 bits. | ||
/// For example (FormatAsCliterals == true) | ||
/// bitmask '1' => "0x0000000000000001" | ||
/// bitmask '1 << 64' => "0x0000000000000000,0x0000000000000001" | ||
/// (FormatAsCLiterals == false) | ||
/// bitmask '1' => "00000000000000000000000000000001" | ||
/// bitmask '1 << 64' => "00000000000000010000000000000000" | ||
inline Printable PrintLaneMask(LaneBitmask LaneMask, | ||
bool FormatAsCLiterals = false) { | ||
return Printable([LaneMask, FormatAsCLiterals](raw_ostream &OS) { | ||
SmallString<64> Buffer; | ||
APInt V = LaneMask.getAsAPInt(); | ||
while (true) { | ||
unsigned Bitwidth = FormatAsCLiterals ? 64 : LaneBitmask::BitWidth; | ||
APInt VToPrint = V.trunc(Bitwidth); | ||
|
||
Buffer.clear(); | ||
VToPrint.toString(Buffer, 16, /*Signed=*/false, | ||
/*formatAsCLiteral=*/false); | ||
unsigned NumZeroesToPad = | ||
(VToPrint.countLeadingZeros() / 4) - VToPrint.isZero(); | ||
OS << (FormatAsCLiterals ? "0x" : "") << std::string(NumZeroesToPad, '0') | ||
<< Buffer.str(); | ||
V = V.lshr(Bitwidth); | ||
if (V.getActiveBits()) | ||
OS << ","; | ||
else | ||
break; | ||
} | ||
}); | ||
} | ||
|
||
} // end namespace llvm | ||
|
||
#endif // LLVM_MC_LANEBITMASK_H |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5 | ||
# RUN: llc -o - %s -mtriple=aarch64 -stop-before=greedy | FileCheck %s | ||
--- | ||
name: test_parse_lanebitmask | ||
tracksRegLiveness: true | ||
liveins: | ||
- { reg: '$h0' } | ||
- { reg: '$s1' } | ||
body: | | ||
bb.0: | ||
liveins: $h0:0x0000000000000001, $s1:(0x0000000000000001,0x0000000000000000) | ||
; CHECK-LABEL: name: test_parse_lanebitmask | ||
; CHECK: liveins: $h0:0x0000000000000001, $s1:0x0000000000000001, $h0, $s1 | ||
; CHECK-NEXT: {{ $}} | ||
; CHECK-NEXT: RET_ReallyLR | ||
RET_ReallyLR | ||
... | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
//===------------------ LaneBitmaskTest.cpp -------------------------------===// | ||
// | ||
// 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "gtest/gtest.h" | ||
#include "llvm/MC/LaneBitmask.h" | ||
#include "llvm/Support/raw_ostream.h" | ||
#include <string> | ||
|
||
using namespace llvm; | ||
|
||
TEST(LaneBitmaskTest, Basic) { | ||
EXPECT_EQ(LaneBitmask::getAll(), ~LaneBitmask::getNone()); | ||
EXPECT_EQ(LaneBitmask::getNone(), ~LaneBitmask::getAll()); | ||
EXPECT_EQ(LaneBitmask::getLane(0) | LaneBitmask::getLane(1), LaneBitmask(3)); | ||
EXPECT_EQ(LaneBitmask(3) & LaneBitmask::getLane(1), LaneBitmask::getLane(1)); | ||
|
||
EXPECT_EQ(LaneBitmask(APInt(128, 42)).getAsAPInt(), APInt(128, 42)); | ||
EXPECT_EQ(LaneBitmask(3).getNumLanes(), 2); | ||
EXPECT_EQ(LaneBitmask::getLane(0).getHighestLane(), 0); | ||
EXPECT_EQ(LaneBitmask::getLane(64).getHighestLane(), 64); | ||
EXPECT_EQ(LaneBitmask::getLane(127).getHighestLane(), 127); | ||
|
||
EXPECT_LT(LaneBitmask::getLane(64), LaneBitmask::getLane(65)); | ||
EXPECT_LT(LaneBitmask::getLane(63), LaneBitmask::getLane(64)); | ||
EXPECT_LT(LaneBitmask::getLane(62), LaneBitmask::getLane(63)); | ||
EXPECT_LT(LaneBitmask::getLane(64), LaneBitmask::getLane(64) | LaneBitmask::getLane(0)); | ||
|
||
LaneBitmask X(1); | ||
X |= LaneBitmask(2); | ||
EXPECT_EQ(X, LaneBitmask(3)); | ||
|
||
LaneBitmask Y(3); | ||
Y &= LaneBitmask(1); | ||
EXPECT_EQ(Y, LaneBitmask(1)); | ||
} | ||
|
||
TEST(LaneBitmaskTest, Print) { | ||
std::string buffer; | ||
raw_string_ostream OS(buffer); | ||
|
||
buffer = ""; | ||
OS << PrintLaneMask(LaneBitmask::getAll(), /*FormatAsCLiterals=*/true); | ||
EXPECT_STREQ(OS.str().data(), "0xFFFFFFFFFFFFFFFF,0xFFFFFFFFFFFFFFFF"); | ||
|
||
buffer = ""; | ||
OS << PrintLaneMask(LaneBitmask::getAll(), /*FormatAsCLiterals=*/false); | ||
EXPECT_STREQ(OS.str().data(), "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); | ||
|
||
buffer = ""; | ||
OS << PrintLaneMask(LaneBitmask::getLane(0), /*FormatAsCLiterals=*/true); | ||
EXPECT_STREQ(OS.str().data(), "0x0000000000000001"); | ||
|
||
buffer = ""; | ||
OS << PrintLaneMask(LaneBitmask::getLane(63), /*FormatAsCLiterals=*/true); | ||
EXPECT_STREQ(OS.str().data(), "0x8000000000000000"); | ||
|
||
buffer = ""; | ||
OS << PrintLaneMask(LaneBitmask::getNone(), /*FormatAsCLiterals=*/true); | ||
EXPECT_STREQ(OS.str().data(), "0x0000000000000000"); | ||
|
||
buffer = ""; | ||
OS << PrintLaneMask(LaneBitmask::getLane(64), /*FormatAsCLiterals=*/true); | ||
EXPECT_STREQ(OS.str().data(), "0x0000000000000000,0x0000000000000001"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably should use APInt::extractBitsAsZExtValue() instead exposing APInts internals.