
Up until now the generation of vector instructions was taking place during the top-down post-order traversal of vectorizeRec(). The issue with this approach is that the vector instructions emitted during the traversal can be reordered by the scheduler, making it challenging to place them without breaking the def-before-uses rule. With this patch we separate the vectorization decisions (done in `vectorizeRec()`) from the code generation phase (`emitVectors()`). The vectorization decisions are stored in the `Actions` vector and are used by `emitVectors()` to drive code generation.
120 lines
3.8 KiB
C++
120 lines
3.8 KiB
C++
//===- InstrMapsTest.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 "llvm/Transforms/Vectorize/SandboxVectorizer/InstrMaps.h"
|
|
#include "llvm/ADT/SmallSet.h"
|
|
#include "llvm/AsmParser/Parser.h"
|
|
#include "llvm/SandboxIR/Function.h"
|
|
#include "llvm/SandboxIR/Instruction.h"
|
|
#include "llvm/Support/SourceMgr.h"
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace llvm;
|
|
|
|
struct InstrMapsTest : public testing::Test {
|
|
LLVMContext C;
|
|
std::unique_ptr<Module> M;
|
|
|
|
void parseIR(LLVMContext &C, const char *IR) {
|
|
SMDiagnostic Err;
|
|
M = parseAssemblyString(IR, Err, C);
|
|
if (!M)
|
|
Err.print("InstrMapsTest", errs());
|
|
}
|
|
};
|
|
|
|
TEST_F(InstrMapsTest, Basic) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(i8 %v0, i8 %v1, i8 %v2, i8 %v3, <2 x i8> %vec) {
|
|
%add0 = add i8 %v0, %v0
|
|
%add1 = add i8 %v1, %v1
|
|
%add2 = add i8 %v2, %v2
|
|
%add3 = add i8 %v3, %v3
|
|
%vadd0 = add <2 x i8> %vec, %vec
|
|
ret void
|
|
}
|
|
)IR");
|
|
llvm::Function *LLVMF = &*M->getFunction("foo");
|
|
sandboxir::Context Ctx(C);
|
|
auto *F = Ctx.createFunction(LLVMF);
|
|
auto *BB = &*F->begin();
|
|
auto It = BB->begin();
|
|
|
|
auto *Add0 = cast<sandboxir::BinaryOperator>(&*It++);
|
|
auto *Add1 = cast<sandboxir::BinaryOperator>(&*It++);
|
|
auto *Add2 = cast<sandboxir::BinaryOperator>(&*It++);
|
|
auto *Add3 = cast<sandboxir::BinaryOperator>(&*It++);
|
|
auto *VAdd0 = cast<sandboxir::BinaryOperator>(&*It++);
|
|
[[maybe_unused]] auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
|
|
|
|
sandboxir::InstrMaps IMaps;
|
|
{
|
|
// Check with empty IMaps.
|
|
sandboxir::Action A(nullptr, {Add0}, {}, 0);
|
|
EXPECT_EQ(IMaps.getVectorForOrig(Add0), nullptr);
|
|
EXPECT_EQ(IMaps.getVectorForOrig(Add1), nullptr);
|
|
EXPECT_FALSE(IMaps.getOrigLane(&A, Add0));
|
|
}
|
|
{
|
|
// Check with 1 match.
|
|
sandboxir::Action A(nullptr, {Add0, Add1}, {}, 0);
|
|
sandboxir::Action OtherA(nullptr, {}, {}, 0);
|
|
IMaps.registerVector({Add0, Add1}, &A);
|
|
EXPECT_EQ(IMaps.getVectorForOrig(Add0), &A);
|
|
EXPECT_EQ(IMaps.getVectorForOrig(Add1), &A);
|
|
EXPECT_FALSE(IMaps.getOrigLane(&A, VAdd0)); // Bad Orig value
|
|
EXPECT_FALSE(IMaps.getOrigLane(&OtherA, Add0)); // Bad Vector value
|
|
EXPECT_EQ(*IMaps.getOrigLane(&A, Add0), 0U);
|
|
EXPECT_EQ(*IMaps.getOrigLane(&A, Add1), 1U);
|
|
}
|
|
{
|
|
// Check when the same vector maps to different original values (which is
|
|
// common for vector constants).
|
|
sandboxir::Action A(nullptr, {Add2, Add3}, {}, 0);
|
|
IMaps.registerVector({Add2, Add3}, &A);
|
|
EXPECT_EQ(*IMaps.getOrigLane(&A, Add2), 0U);
|
|
EXPECT_EQ(*IMaps.getOrigLane(&A, Add3), 1U);
|
|
}
|
|
{
|
|
// Check when we register for a second time.
|
|
sandboxir::Action A(nullptr, {Add2, Add3}, {}, 0);
|
|
#ifndef NDEBUG
|
|
EXPECT_DEATH(IMaps.registerVector({Add1, Add0}, &A), ".*exists.*");
|
|
#endif // NDEBUG
|
|
}
|
|
}
|
|
|
|
TEST_F(InstrMapsTest, VectorLanes) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(<2 x i8> %v0, <2 x i8> %v1, <4 x i8> %v2, <4 x i8> %v3) {
|
|
%vadd0 = add <2 x i8> %v0, %v1
|
|
%vadd1 = add <2 x i8> %v0, %v1
|
|
ret void
|
|
}
|
|
)IR");
|
|
llvm::Function *LLVMF = &*M->getFunction("foo");
|
|
sandboxir::Context Ctx(C);
|
|
auto *F = Ctx.createFunction(LLVMF);
|
|
auto *BB = &*F->begin();
|
|
auto It = BB->begin();
|
|
|
|
auto *VAdd0 = cast<sandboxir::BinaryOperator>(&*It++);
|
|
auto *VAdd1 = cast<sandboxir::BinaryOperator>(&*It++);
|
|
|
|
sandboxir::InstrMaps IMaps;
|
|
|
|
{
|
|
// Check that the vector lanes are calculated correctly.
|
|
sandboxir::Action A(nullptr, {VAdd0, VAdd1}, {}, 0);
|
|
IMaps.registerVector({VAdd0, VAdd1}, &A);
|
|
EXPECT_EQ(*IMaps.getOrigLane(&A, VAdd0), 0U);
|
|
EXPECT_EQ(*IMaps.getOrigLane(&A, VAdd1), 2U);
|
|
}
|
|
}
|