//===- SandboxIRTest.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/AsmParser/Parser.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Function.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Module.h" #include "llvm/SandboxIR/BasicBlock.h" #include "llvm/SandboxIR/Constant.h" #include "llvm/SandboxIR/Function.h" #include "llvm/SandboxIR/Instruction.h" #include "llvm/SandboxIR/Module.h" #include "llvm/SandboxIR/Utils.h" #include "llvm/SandboxIR/Value.h" #include "llvm/Support/SourceMgr.h" #include "gmock/gmock.h" #include "gtest/gtest.h" using namespace llvm; struct SandboxIRTest : public testing::Test { LLVMContext C; std::unique_ptr M; void parseIR(LLVMContext &C, const char *IR) { SMDiagnostic Err; M = parseAssemblyString(IR, Err, C); if (!M) Err.print("SandboxIRTest", errs()); } BasicBlock *getBasicBlockByName(Function &F, StringRef Name) { for (BasicBlock &BB : F) if (BB.getName() == Name) return &BB; llvm_unreachable("Expected to find basic block!"); } }; TEST_F(SandboxIRTest, ClassID) { parseIR(C, R"IR( define void @foo(i32 %v1) { %add = add i32 %v1, 42 ret void } )IR"); llvm::Function *LLVMF = &*M->getFunction("foo"); llvm::BasicBlock *LLVMBB = &*LLVMF->begin(); llvm::Instruction *LLVMAdd = &*LLVMBB->begin(); auto *LLVMC = cast(LLVMAdd->getOperand(1)); sandboxir::Context Ctx(C); sandboxir::Function *F = Ctx.createFunction(LLVMF); sandboxir::Argument *Arg0 = F->getArg(0); sandboxir::BasicBlock *BB = &*F->begin(); sandboxir::Instruction *AddI = &*BB->begin(); sandboxir::Constant *Const0 = cast(Ctx.getValue(LLVMC)); EXPECT_TRUE(isa(F)); EXPECT_FALSE(isa(Arg0)); EXPECT_FALSE(isa(BB)); EXPECT_FALSE(isa(AddI)); EXPECT_FALSE(isa(Const0)); EXPECT_FALSE(isa(F)); EXPECT_TRUE(isa(Arg0)); EXPECT_FALSE(isa(BB)); EXPECT_FALSE(isa(AddI)); EXPECT_FALSE(isa(Const0)); EXPECT_TRUE(isa(F)); EXPECT_FALSE(isa(Arg0)); EXPECT_FALSE(isa(BB)); EXPECT_FALSE(isa(AddI)); EXPECT_TRUE(isa(Const0)); EXPECT_FALSE(isa(F)); EXPECT_FALSE(isa(Arg0)); EXPECT_FALSE(isa(BB)); EXPECT_FALSE(isa(AddI)); EXPECT_FALSE(isa(Const0)); EXPECT_FALSE(isa(F)); EXPECT_FALSE(isa(Arg0)); EXPECT_FALSE(isa(BB)); EXPECT_TRUE(isa(AddI)); EXPECT_FALSE(isa(Const0)); EXPECT_TRUE(isa(F)); EXPECT_FALSE(isa(Arg0)); EXPECT_FALSE(isa(BB)); EXPECT_TRUE(isa(AddI)); EXPECT_TRUE(isa(Const0)); #ifndef NDEBUG std::string Buff; raw_string_ostream BS(Buff); F->dumpOS(BS); Arg0->dumpOS(BS); BB->dumpOS(BS); AddI->dumpOS(BS); Const0->dumpOS(BS); #endif } TEST_F(SandboxIRTest, ConstantInt) { parseIR(C, R"IR( define void @foo(i32 %v0) { %add0 = add i32 %v0, 42 ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); auto *LLVMBB = &*LLVMF.begin(); auto *LLVMAdd0 = &*LLVMBB->begin(); auto *LLVMFortyTwo = cast(LLVMAdd0->getOperand(1)); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto &BB = *F.begin(); auto It = BB.begin(); auto *Add0 = cast(&*It++); auto *FortyTwo = cast(Add0->getOperand(1)); // Check that creating an identical constant gives us the same object. auto *NewCI = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 42); EXPECT_EQ(NewCI, FortyTwo); { // Check getTrue(Ctx). auto *True = sandboxir::ConstantInt::getTrue(Ctx); EXPECT_EQ(True, Ctx.getValue(llvm::ConstantInt::getTrue(C))); // Check getFalse(Ctx). auto *False = sandboxir::ConstantInt::getFalse(Ctx); EXPECT_EQ(False, Ctx.getValue(llvm::ConstantInt::getFalse(C))); // Check getBool(Ctx). auto *Bool = sandboxir::ConstantInt::getBool(Ctx, true); EXPECT_EQ(Bool, Ctx.getValue(llvm::ConstantInt::getBool(C, true))); } { auto *Int1Ty = sandboxir::Type::getInt1Ty(Ctx); auto *LLVMInt1Ty = llvm::Type::getInt1Ty(C); // Check getTrue(Ty). auto *True = sandboxir::ConstantInt::getTrue(Int1Ty); EXPECT_EQ(True, Ctx.getValue(llvm::ConstantInt::getTrue(LLVMInt1Ty))); // Check getFalse(Ty). auto *False = sandboxir::ConstantInt::getFalse(Int1Ty); EXPECT_EQ(False, Ctx.getValue(llvm::ConstantInt::getFalse(LLVMInt1Ty))); // Check getBool(Ty). auto *Bool = sandboxir::ConstantInt::getBool(Int1Ty, true); EXPECT_EQ(Bool, Ctx.getValue(llvm::ConstantInt::getBool(LLVMInt1Ty, true))); } auto *Int32Ty = sandboxir::Type::getInt32Ty(Ctx); auto *LLVMInt32Ty = llvm::Type::getInt32Ty(C); { // Check get(Type, V). auto *FortyThree = sandboxir::ConstantInt::get(Int32Ty, 43); auto *LLVMFortyThree = llvm::ConstantInt::get(LLVMInt32Ty, 43); EXPECT_NE(FortyThree, FortyTwo); EXPECT_EQ(FortyThree, Ctx.getValue(LLVMFortyThree)); } { // Check get(Type, V, IsSigned). auto *FortyThree = sandboxir::ConstantInt::get(Int32Ty, 43, /*IsSigned=*/true); auto *LLVMFortyThree = llvm::ConstantInt::get(LLVMInt32Ty, 43, /*IsSigned=*/true); EXPECT_NE(FortyThree, FortyTwo); EXPECT_EQ(FortyThree, Ctx.getValue(LLVMFortyThree)); } { // Check get(IntegerType, V). auto *FortyThree = sandboxir::ConstantInt::get(sandboxir::IntegerType::get(Ctx, 32), 43); auto *LLVMFortyThree = llvm::ConstantInt::get(llvm::IntegerType::get(C, 32), 43); EXPECT_NE(FortyThree, FortyTwo); EXPECT_EQ(FortyThree, Ctx.getValue(LLVMFortyThree)); } { // Check get(IntegerType, V, IsSigned). auto *FortyThree = sandboxir::ConstantInt::get( sandboxir::IntegerType::get(Ctx, 32), 43, /*IsSigned=*/true); auto *LLVMFortyThree = llvm::ConstantInt::get(llvm::IntegerType::get(C, 32), 43, /*IsSigned=*/true); EXPECT_NE(FortyThree, FortyTwo); EXPECT_EQ(FortyThree, Ctx.getValue(LLVMFortyThree)); } { // Check getSigned(IntegerType, V). auto *FortyThree = sandboxir::ConstantInt::getSigned( sandboxir::IntegerType::get(Ctx, 32), 43); auto *LLVMFortyThree = llvm::ConstantInt::getSigned(llvm::IntegerType::get(C, 32), 43); EXPECT_NE(FortyThree, FortyTwo); EXPECT_EQ(FortyThree, Ctx.getValue(LLVMFortyThree)); } { // Check getSigned(Type, V). auto *FortyThree = sandboxir::ConstantInt::getSigned(Int32Ty, 43); auto *LLVMFortyThree = llvm::ConstantInt::getSigned(LLVMInt32Ty, 43); EXPECT_NE(FortyThree, FortyTwo); EXPECT_EQ(FortyThree, Ctx.getValue(LLVMFortyThree)); } { // Check get(Ctx, APInt). APInt APInt43(32, 43); auto *FortyThree = sandboxir::ConstantInt::get(Ctx, APInt43); auto *LLVMFortyThree = llvm::ConstantInt::get(C, APInt43); EXPECT_NE(FortyThree, FortyTwo); EXPECT_EQ(FortyThree, Ctx.getValue(LLVMFortyThree)); } { // Check get(Ty, Str, Radix). StringRef Str("43"); uint8_t Radix(10); auto *FortyThree = sandboxir::ConstantInt::get( sandboxir::IntegerType::get(Ctx, 32), Str, Radix); auto *LLVMFortyThree = llvm::ConstantInt::get(llvm::IntegerType::get(C, 32), Str, Radix); EXPECT_NE(FortyThree, FortyTwo); EXPECT_EQ(FortyThree, Ctx.getValue(LLVMFortyThree)); } { // Check get(Ty, APInt). APInt APInt43(32, 43); auto *FortyThree = sandboxir::ConstantInt::get(Int32Ty, APInt43); auto *LLVMFortyThree = llvm::ConstantInt::get(LLVMInt32Ty, APInt43); EXPECT_NE(FortyThree, FortyTwo); EXPECT_EQ(FortyThree, Ctx.getValue(LLVMFortyThree)); } // Check getValue(). EXPECT_EQ(FortyTwo->getValue(), LLVMFortyTwo->getValue()); // Check getBitWidth(). EXPECT_EQ(FortyTwo->getBitWidth(), LLVMFortyTwo->getBitWidth()); // Check getZExtValue(). EXPECT_EQ(FortyTwo->getZExtValue(), LLVMFortyTwo->getZExtValue()); // Check getSExtValue(). EXPECT_EQ(FortyTwo->getSExtValue(), LLVMFortyTwo->getSExtValue()); // Check getMaybeAlignValue(). auto *SixtyFour = cast(sandboxir::ConstantInt::get(Int32Ty, 64)); auto *LLVMSixtyFour = cast(llvm::ConstantInt::get(LLVMInt32Ty, 64)); EXPECT_EQ(SixtyFour->getMaybeAlignValue(), LLVMSixtyFour->getMaybeAlignValue()); // Check getAlignValue(). EXPECT_EQ(SixtyFour->getAlignValue(), LLVMSixtyFour->getAlignValue()); // Check equalsInt(). EXPECT_TRUE(FortyTwo->equalsInt(42)); EXPECT_FALSE(FortyTwo->equalsInt(43)); // Check getIntegerType(). EXPECT_EQ(FortyTwo->getIntegerType(), sandboxir::IntegerType::get(Ctx, 32)); // Check isValueValidForType(). EXPECT_TRUE( sandboxir::ConstantInt::isValueValidForType(Int32Ty, (uint64_t)42)); EXPECT_TRUE( sandboxir::ConstantInt::isValueValidForType(Int32Ty, (int64_t)42)); // Check isNegative(). EXPECT_FALSE(FortyTwo->isNegative()); EXPECT_TRUE(sandboxir::ConstantInt::get(Int32Ty, -42)); // Check isZero(). EXPECT_FALSE(FortyTwo->isZero()); EXPECT_TRUE(sandboxir::ConstantInt::get(Int32Ty, 0)->isZero()); // Check isOne(). EXPECT_FALSE(FortyTwo->isOne()); EXPECT_TRUE(sandboxir::ConstantInt::get(Int32Ty, 1)->isOne()); // Check isMinusOne(). EXPECT_FALSE(FortyTwo->isMinusOne()); EXPECT_TRUE(sandboxir::ConstantInt::get(Int32Ty, -1)->isMinusOne()); // Check isMaxValue(). EXPECT_FALSE(FortyTwo->isMaxValue(/*Signed=*/true)); EXPECT_TRUE( sandboxir::ConstantInt::get(Int32Ty, std::numeric_limits::max()) ->isMaxValue(/*Signed=*/true)); // Check isMinValue(). EXPECT_FALSE(FortyTwo->isMinValue(/*Signed=*/true)); EXPECT_TRUE( sandboxir::ConstantInt::get(Int32Ty, std::numeric_limits::min()) ->isMinValue(/*Signed=*/true)); // Check uge(). EXPECT_TRUE(FortyTwo->uge(41)); EXPECT_FALSE(FortyTwo->uge(43)); // Check getLimitedValue(). EXPECT_EQ(FortyTwo->getLimitedValue(40u), 40u); EXPECT_EQ(FortyTwo->getLimitedValue(50u), 42u); } TEST_F(SandboxIRTest, ConstantFP) { parseIR(C, R"IR( define void @foo(float %v0, double %v1) { %fadd0 = fadd float %v0, 42.0 %fadd1 = fadd double %v1, 43.0 ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto &BB = *F.begin(); auto It = BB.begin(); auto *FAdd0 = cast(&*It++); auto *FAdd1 = cast(&*It++); auto *FortyTwo = cast(FAdd0->getOperand(1)); [[maybe_unused]] auto *FortyThree = cast(FAdd1->getOperand(1)); auto *FloatTy = sandboxir::Type::getFloatTy(Ctx); auto *DoubleTy = sandboxir::Type::getDoubleTy(Ctx); auto *LLVMFloatTy = Type::getFloatTy(C); auto *LLVMDoubleTy = Type::getDoubleTy(C); // Check that creating an identical constant gives us the same object. auto *NewFortyTwo = sandboxir::ConstantFP::get(FloatTy, 42.0); EXPECT_EQ(NewFortyTwo, FortyTwo); // Check get(Type, double). auto *FortyFour = cast(sandboxir::ConstantFP::get(FloatTy, 44.0)); auto *LLVMFortyFour = cast(llvm::ConstantFP::get(LLVMFloatTy, 44.0)); EXPECT_NE(FortyFour, FortyTwo); EXPECT_EQ(FortyFour, Ctx.getValue(LLVMFortyFour)); // Check get(Type, APFloat). auto *FortyFive = cast( sandboxir::ConstantFP::get(DoubleTy, APFloat(45.0))); auto *LLVMFortyFive = cast( llvm::ConstantFP::get(LLVMDoubleTy, APFloat(45.0))); EXPECT_EQ(FortyFive, Ctx.getValue(LLVMFortyFive)); // Check get(Type, StringRef). auto *FortySix = sandboxir::ConstantFP::get(FloatTy, "46.0"); EXPECT_EQ(FortySix, Ctx.getValue(llvm::ConstantFP::get(LLVMFloatTy, "46.0"))); // Check get(APFloat). auto *FortySeven = sandboxir::ConstantFP::get(APFloat(47.0), Ctx); EXPECT_EQ(FortySeven, Ctx.getValue(llvm::ConstantFP::get(C, APFloat(47.0)))); // Check getNaN(). { auto *NaN = sandboxir::ConstantFP::getNaN(FloatTy); EXPECT_EQ(NaN, Ctx.getValue(llvm::ConstantFP::getNaN(LLVMFloatTy))); } { auto *NaN = sandboxir::ConstantFP::getNaN(FloatTy, /*Negative=*/true); EXPECT_EQ(NaN, Ctx.getValue(llvm::ConstantFP::getNaN(LLVMFloatTy, /*Negative=*/true))); } { auto *NaN = sandboxir::ConstantFP::getNaN(FloatTy, /*Negative=*/true, /*Payload=*/1); EXPECT_EQ(NaN, Ctx.getValue(llvm::ConstantFP::getNaN( LLVMFloatTy, /*Negative=*/true, /*Payload=*/1))); } // Check getQNaN(). { auto *QNaN = sandboxir::ConstantFP::getQNaN(FloatTy); EXPECT_EQ(QNaN, Ctx.getValue(llvm::ConstantFP::getQNaN(LLVMFloatTy))); } { auto *QNaN = sandboxir::ConstantFP::getQNaN(FloatTy, /*Negative=*/true); EXPECT_EQ(QNaN, Ctx.getValue(llvm::ConstantFP::getQNaN(LLVMFloatTy, /*Negative=*/true))); } { APInt Payload(1, 1); auto *QNaN = sandboxir::ConstantFP::getQNaN(FloatTy, /*Negative=*/true, &Payload); EXPECT_EQ(QNaN, Ctx.getValue(llvm::ConstantFP::getQNaN( LLVMFloatTy, /*Negative=*/true, &Payload))); } // Check getSNaN(). { auto *SNaN = sandboxir::ConstantFP::getSNaN(FloatTy); EXPECT_EQ(SNaN, Ctx.getValue(llvm::ConstantFP::getSNaN(LLVMFloatTy))); } { auto *SNaN = sandboxir::ConstantFP::getSNaN(FloatTy, /*Negative=*/true); EXPECT_EQ(SNaN, Ctx.getValue(llvm::ConstantFP::getSNaN(LLVMFloatTy, /*Negative=*/true))); } { APInt Payload(1, 1); auto *SNaN = sandboxir::ConstantFP::getSNaN(FloatTy, /*Negative=*/true, &Payload); EXPECT_EQ(SNaN, Ctx.getValue(llvm::ConstantFP::getSNaN( LLVMFloatTy, /*Negative=*/true, &Payload))); } // Check getZero(). { auto *Zero = sandboxir::ConstantFP::getZero(FloatTy); EXPECT_EQ(Zero, Ctx.getValue(llvm::ConstantFP::getZero(LLVMFloatTy))); } { auto *Zero = sandboxir::ConstantFP::getZero(FloatTy, /*Negative=*/true); EXPECT_EQ(Zero, Ctx.getValue(llvm::ConstantFP::getZero(LLVMFloatTy, /*Negative=*/true))); } // Check getNegativeZero(). auto *NegZero = cast( sandboxir::ConstantFP::getNegativeZero(FloatTy)); EXPECT_EQ(NegZero, Ctx.getValue(llvm::ConstantFP::getNegativeZero(LLVMFloatTy))); // Check getInfinity(). { auto *Inf = sandboxir::ConstantFP::getInfinity(FloatTy); EXPECT_EQ(Inf, Ctx.getValue(llvm::ConstantFP::getInfinity(LLVMFloatTy))); } { auto *Inf = sandboxir::ConstantFP::getInfinity(FloatTy, /*Negative=*/true); EXPECT_EQ(Inf, Ctx.getValue(llvm::ConstantFP::getInfinity( LLVMFloatTy, /*Negative=*/true))); } // Check isValueValidForType(). APFloat V(1.1); EXPECT_EQ(sandboxir::ConstantFP::isValueValidForType(FloatTy, V), llvm::ConstantFP::isValueValidForType(LLVMFloatTy, V)); // Check getValueAPF(). EXPECT_EQ(FortyFour->getValueAPF(), LLVMFortyFour->getValueAPF()); // Check getValue(). EXPECT_EQ(FortyFour->getValue(), LLVMFortyFour->getValue()); // Check isZero(). EXPECT_EQ(FortyFour->isZero(), LLVMFortyFour->isZero()); EXPECT_TRUE(sandboxir::ConstantFP::getZero(FloatTy)); EXPECT_TRUE(sandboxir::ConstantFP::getZero(FloatTy, /*Negative=*/true)); // Check isNegative(). EXPECT_TRUE(cast( sandboxir::ConstantFP::getZero(FloatTy, /*Negative=*/true)) ->isNegative()); // Check isInfinity(). EXPECT_TRUE( cast(sandboxir::ConstantFP::getInfinity(FloatTy)) ->isInfinity()); // Check isNaN(). EXPECT_TRUE( cast(sandboxir::ConstantFP::getNaN(FloatTy)) ->isNaN()); // Check isExactlyValue(APFloat). EXPECT_TRUE(NegZero->isExactlyValue(NegZero->getValueAPF())); // Check isExactlyValue(double). EXPECT_TRUE(NegZero->isExactlyValue(-0.0)); } // Tests ConstantArray, ConstantStruct and ConstantVector. TEST_F(SandboxIRTest, ConstantAggregate) { // Note: we are using i42 to avoid the creation of ConstantDataVector or // ConstantDataArray. parseIR(C, R"IR( define void @foo() { %array = extractvalue [2 x i42] [i42 0, i42 1], 0 %struct = extractvalue {i42, i42} {i42 0, i42 1}, 0 %vector = extractelement <2 x i42> , i32 0 ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto &BB = *F.begin(); auto It = BB.begin(); auto *I0 = &*It++; auto *I1 = &*It++; auto *I2 = &*It++; // Check classof() and creation. auto *Array = cast(I0->getOperand(0)); EXPECT_TRUE(isa(Array)); auto *Struct = cast(I1->getOperand(0)); EXPECT_TRUE(isa(Struct)); auto *Vector = cast(I2->getOperand(0)); EXPECT_TRUE(isa(Vector)); auto *ZeroI42 = cast(Array->getOperand(0)); auto *OneI42 = cast(Array->getOperand(1)); // Check ConstantArray::get(), getType(). auto *NewCA = sandboxir::ConstantArray::get(Array->getType(), {ZeroI42, OneI42}); EXPECT_EQ(NewCA, Array); // Check ConstantStruct::get(), getType(). auto *NewCS = sandboxir::ConstantStruct::get(Struct->getType(), {ZeroI42, OneI42}); EXPECT_EQ(NewCS, Struct); // Check ConstantStruct::get(...). auto *NewCS2 = sandboxir::ConstantStruct::get(Struct->getType(), ZeroI42, OneI42); EXPECT_EQ(NewCS2, Struct); // Check ConstantStruct::getAnon(ArayRef). auto *AnonCS = sandboxir::ConstantStruct::getAnon({ZeroI42, OneI42}); EXPECT_FALSE(cast(AnonCS->getType())->isPacked()); auto *AnonCSPacked = sandboxir::ConstantStruct::getAnon({ZeroI42, OneI42}, /*Packed=*/true); EXPECT_TRUE(cast(AnonCSPacked->getType())->isPacked()); // Check ConstantStruct::getAnon(Ctx, ArrayRef). auto *AnonCS2 = sandboxir::ConstantStruct::getAnon(Ctx, {ZeroI42, OneI42}); EXPECT_EQ(AnonCS2, AnonCS); auto *AnonCS2Packed = sandboxir::ConstantStruct::getAnon( Ctx, {ZeroI42, OneI42}, /*Packed=*/true); EXPECT_EQ(AnonCS2Packed, AnonCSPacked); // Check ConstantStruct::getTypeForElements(Ctx, ArrayRef). auto *StructTy = sandboxir::ConstantStruct::getTypeForElements(Ctx, {ZeroI42, OneI42}); EXPECT_EQ(StructTy, Struct->getType()); EXPECT_FALSE(StructTy->isPacked()); // Check ConstantStruct::getTypeForElements(Ctx, ArrayRef, Packed). auto *StructTyPacked = sandboxir::ConstantStruct::getTypeForElements( Ctx, {ZeroI42, OneI42}, /*Packed=*/true); EXPECT_TRUE(StructTyPacked->isPacked()); // Check ConstantStruct::getTypeForElements(ArrayRef). auto *StructTy2 = sandboxir::ConstantStruct::getTypeForElements(Ctx, {ZeroI42, OneI42}); EXPECT_EQ(StructTy2, Struct->getType()); // Check ConstantStruct::getTypeForElements(ArrayRef, Packed). auto *StructTy2Packed = sandboxir::ConstantStruct::getTypeForElements( Ctx, {ZeroI42, OneI42}, /*Packed=*/true); EXPECT_EQ(StructTy2Packed, StructTyPacked); } TEST_F(SandboxIRTest, ConstantAggregateZero) { parseIR(C, R"IR( define void @foo(ptr %ptr, {i32, i8} %v1, <2 x i8> %v2) { %extr0 = extractvalue [2 x i8] zeroinitializer, 0 %extr1 = extractvalue {i32, i8} zeroinitializer, 0 %extr2 = extractelement <2 x i8> zeroinitializer, i32 0 ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto &BB = *F.begin(); auto It = BB.begin(); auto *Extr0 = &*It++; auto *Extr1 = &*It++; auto *Extr2 = &*It++; [[maybe_unused]] auto *Ret = cast(&*It++); auto *Zero32 = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 0); auto *Zero8 = sandboxir::ConstantInt::get(sandboxir::Type::getInt8Ty(Ctx), 0); auto *Int8Ty = sandboxir::Type::getInt8Ty(Ctx); auto *Int32Ty = sandboxir::Type::getInt32Ty(Ctx); auto *ArrayTy = sandboxir::ArrayType::get(Int8Ty, 2u); auto *StructTy = sandboxir::StructType::get(Ctx, {Int32Ty, Int8Ty}); auto *VectorTy = sandboxir::VectorType::get(Int8Ty, ElementCount::getFixed(2u)); // Check creation and classof(). auto *ArrayCAZ = cast(Extr0->getOperand(0)); EXPECT_EQ(ArrayCAZ->getType(), ArrayTy); auto *StructCAZ = cast(Extr1->getOperand(0)); EXPECT_EQ(StructCAZ->getType(), StructTy); auto *VectorCAZ = cast(Extr2->getOperand(0)); EXPECT_EQ(VectorCAZ->getType(), VectorTy); // Check get(). auto *SameVectorCAZ = sandboxir::ConstantAggregateZero::get(sandboxir::VectorType::get( sandboxir::Type::getInt8Ty(Ctx), ElementCount::getFixed(2))); EXPECT_EQ(SameVectorCAZ, VectorCAZ); // Should be uniqued. auto *NewVectorCAZ = sandboxir::ConstantAggregateZero::get(sandboxir::VectorType::get( sandboxir::Type::getInt8Ty(Ctx), ElementCount::getFixed(4))); EXPECT_NE(NewVectorCAZ, VectorCAZ); // Check getSequentialElement(). auto *SeqElm = VectorCAZ->getSequentialElement(); EXPECT_EQ(SeqElm, sandboxir::ConstantInt::get(sandboxir::Type::getInt8Ty(Ctx), 0)); // Check getStructElement(). auto *StructElm0 = StructCAZ->getStructElement(0); auto *StructElm1 = StructCAZ->getStructElement(1); EXPECT_EQ(StructElm0, Zero32); EXPECT_EQ(StructElm1, Zero8); // Check getElementValue(Constant). EXPECT_EQ(ArrayCAZ->getElementValue(Zero32), Zero8); EXPECT_EQ(StructCAZ->getElementValue(Zero32), Zero32); EXPECT_EQ(VectorCAZ->getElementValue(Zero32), Zero8); // Check getElementValue(unsigned). EXPECT_EQ(ArrayCAZ->getElementValue(0u), Zero8); EXPECT_EQ(StructCAZ->getElementValue(0u), Zero32); EXPECT_EQ(VectorCAZ->getElementValue(0u), Zero8); // Check getElementCount(). EXPECT_EQ(ArrayCAZ->getElementCount(), ElementCount::getFixed(2)); EXPECT_EQ(NewVectorCAZ->getElementCount(), ElementCount::getFixed(4)); } TEST_F(SandboxIRTest, ConstantPointerNull) { parseIR(C, R"IR( define ptr @foo() { ret ptr null } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto &BB = *F.begin(); auto It = BB.begin(); auto *Ret = cast(&*It++); // Check classof() and creation. auto *CPNull = cast(Ret->getReturnValue()); // Check get(). auto *NewCPNull = sandboxir::ConstantPointerNull::get(sandboxir::PointerType::get(Ctx, 0u)); EXPECT_EQ(NewCPNull, CPNull); auto *NewCPNull2 = sandboxir::ConstantPointerNull::get(sandboxir::PointerType::get(Ctx, 1u)); EXPECT_NE(NewCPNull2, CPNull); // Check getType(). EXPECT_EQ(CPNull->getType(), sandboxir::PointerType::get(Ctx, 0u)); EXPECT_EQ(NewCPNull2->getType(), sandboxir::PointerType::get(Ctx, 1u)); } TEST_F(SandboxIRTest, PoisonValue) { parseIR(C, R"IR( define void @foo() { %i0 = add i32 poison, poison %i1 = add <2 x i32> poison, poison %i2 = extractvalue {i32, i8} poison, 0 ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto &BB = *F.begin(); auto It = BB.begin(); auto *I0 = &*It++; auto *I1 = &*It++; auto *I2 = &*It++; auto *Int32Ty = sandboxir::Type::getInt32Ty(Ctx); auto *Int8Ty = sandboxir::Type::getInt8Ty(Ctx); auto *Zero32 = sandboxir::ConstantInt::get(Int32Ty, 0u); auto *One32 = sandboxir::ConstantInt::get(Int32Ty, 1u); // Check classof() and creation. auto *Poison = cast(I0->getOperand(0)); EXPECT_EQ(Poison->getType(), Int32Ty); EXPECT_TRUE(isa(Poison)); // Poison is Undef // Check get(). auto *NewPoison = sandboxir::PoisonValue::get(Int32Ty); EXPECT_EQ(NewPoison, Poison); auto *NewPoison2 = sandboxir::PoisonValue::get(sandboxir::PointerType::get(Ctx, 0u)); EXPECT_NE(NewPoison2, Poison); // Check getSequentialElement(). auto *PoisonVector = cast(I1->getOperand(0)); auto *SeqElm = PoisonVector->getSequentialElement(); EXPECT_EQ(SeqElm->getType(), Int32Ty); // Check getStructElement(). auto *PoisonStruct = cast(I2->getOperand(0)); auto *StrElm0 = PoisonStruct->getStructElement(0); auto *StrElm1 = PoisonStruct->getStructElement(1); EXPECT_EQ(StrElm0->getType(), Int32Ty); EXPECT_EQ(StrElm1->getType(), Int8Ty); // Check getElementValue(Constant) EXPECT_EQ(PoisonStruct->getElementValue(Zero32), sandboxir::PoisonValue::get(Int32Ty)); EXPECT_EQ(PoisonStruct->getElementValue(One32), sandboxir::PoisonValue::get(Int8Ty)); // Check getElementValue(unsigned) EXPECT_EQ(PoisonStruct->getElementValue(0u), sandboxir::PoisonValue::get(Int32Ty)); EXPECT_EQ(PoisonStruct->getElementValue(1u), sandboxir::PoisonValue::get(Int8Ty)); } TEST_F(SandboxIRTest, UndefValue) { parseIR(C, R"IR( define void @foo() { %i0 = add i32 undef, undef %i1 = add <2 x i32> undef, undef %i2 = extractvalue {i32, i8} undef, 0 ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto &BB = *F.begin(); auto It = BB.begin(); auto *I0 = &*It++; auto *I1 = &*It++; auto *I2 = &*It++; auto *Int32Ty = sandboxir::Type::getInt32Ty(Ctx); auto *Int8Ty = sandboxir::Type::getInt8Ty(Ctx); auto *Zero32 = sandboxir::ConstantInt::get(Int32Ty, 0u); auto *One32 = sandboxir::ConstantInt::get(Int32Ty, 1u); // Check classof() and creation. auto *Undef = cast(I0->getOperand(0)); EXPECT_EQ(Undef->getType(), Int32Ty); EXPECT_FALSE(isa(Undef)); // Undef is not Poison // Check get(). auto *NewUndef = sandboxir::UndefValue::get(Int32Ty); EXPECT_EQ(NewUndef, Undef); auto *NewUndef2 = sandboxir::UndefValue::get(sandboxir::PointerType::get(Ctx, 0u)); EXPECT_NE(NewUndef2, Undef); // Check getSequentialElement(). auto *UndefVector = cast(I1->getOperand(0)); auto *SeqElm = UndefVector->getSequentialElement(); EXPECT_EQ(SeqElm->getType(), Int32Ty); // Check getStructElement(). auto *UndefStruct = cast(I2->getOperand(0)); auto *StrElm0 = UndefStruct->getStructElement(0); auto *StrElm1 = UndefStruct->getStructElement(1); EXPECT_EQ(StrElm0->getType(), Int32Ty); EXPECT_EQ(StrElm1->getType(), Int8Ty); // Check getElementValue(Constant) EXPECT_EQ(UndefStruct->getElementValue(Zero32), sandboxir::UndefValue::get(Int32Ty)); EXPECT_EQ(UndefStruct->getElementValue(One32), sandboxir::UndefValue::get(Int8Ty)); // Check getElementValue(unsigned) EXPECT_EQ(UndefStruct->getElementValue(0u), sandboxir::UndefValue::get(Int32Ty)); EXPECT_EQ(UndefStruct->getElementValue(1u), sandboxir::UndefValue::get(Int8Ty)); // Check getNumElements(). EXPECT_EQ(UndefVector->getNumElements(), 2u); EXPECT_EQ(UndefStruct->getNumElements(), 2u); } TEST_F(SandboxIRTest, GlobalValue) { parseIR(C, R"IR( declare external void @bar() define void @foo() { call void @bar() ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); auto *LLVMBB = &*LLVMF.begin(); auto LLVMIt = LLVMBB->begin(); auto *LLVMCall = cast(&*LLVMIt++); auto *LLVMGV = cast(LLVMCall->getCalledOperand()); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *BB = &*F.begin(); auto It = BB->begin(); auto *Call = cast(&*It++); [[maybe_unused]] auto *Ret = cast(&*It++); // Check classof(), creation, getFunction(), getBasicBlock(). auto *GV = cast(Call->getCalledOperand()); // Check getAddressSpace(). EXPECT_EQ(GV->getAddressSpace(), LLVMGV->getAddressSpace()); // Check hasGlobalUnnamedAddr(). EXPECT_EQ(GV->hasGlobalUnnamedAddr(), LLVMGV->hasGlobalUnnamedAddr()); // Check hasAtLeastLocalUnnamedAddr(). EXPECT_EQ(GV->hasAtLeastLocalUnnamedAddr(), LLVMGV->hasAtLeastLocalUnnamedAddr()); // Check getUnnamedAddr(). EXPECT_EQ(GV->getUnnamedAddr(), LLVMGV->getUnnamedAddr()); // Check setUnnamedAddr(). auto OrigUnnamedAddr = GV->getUnnamedAddr(); auto NewUnnamedAddr = sandboxir::GlobalValue::UnnamedAddr::Global; EXPECT_NE(NewUnnamedAddr, OrigUnnamedAddr); GV->setUnnamedAddr(NewUnnamedAddr); EXPECT_EQ(GV->getUnnamedAddr(), NewUnnamedAddr); GV->setUnnamedAddr(OrigUnnamedAddr); EXPECT_EQ(GV->getUnnamedAddr(), OrigUnnamedAddr); // Check getMinUnnamedAddr(). EXPECT_EQ( sandboxir::GlobalValue::getMinUnnamedAddr(OrigUnnamedAddr, NewUnnamedAddr), llvm::GlobalValue::getMinUnnamedAddr(OrigUnnamedAddr, NewUnnamedAddr)); // Check hasComdat(). EXPECT_EQ(GV->hasComdat(), LLVMGV->hasComdat()); // Check getVisibility(). EXPECT_EQ(GV->getVisibility(), LLVMGV->getVisibility()); // Check hasDefaultVisibility(). EXPECT_EQ(GV->hasDefaultVisibility(), LLVMGV->hasDefaultVisibility()); // Check hasHiddenVisibility(). EXPECT_EQ(GV->hasHiddenVisibility(), LLVMGV->hasHiddenVisibility()); // Check hasProtectedVisibility(). EXPECT_EQ(GV->hasProtectedVisibility(), LLVMGV->hasProtectedVisibility()); // Check setVisibility(). auto OrigVisibility = GV->getVisibility(); auto NewVisibility = sandboxir::GlobalValue::VisibilityTypes::ProtectedVisibility; EXPECT_NE(NewVisibility, OrigVisibility); GV->setVisibility(NewVisibility); EXPECT_EQ(GV->getVisibility(), NewVisibility); GV->setVisibility(OrigVisibility); EXPECT_EQ(GV->getVisibility(), OrigVisibility); } TEST_F(SandboxIRTest, GlobalObject) { parseIR(C, R"IR( declare external void @bar() define void @foo() { call void @bar() ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); auto *LLVMBB = &*LLVMF.begin(); auto LLVMIt = LLVMBB->begin(); auto *LLVMCall = cast(&*LLVMIt++); auto *LLVMGO = cast(LLVMCall->getCalledOperand()); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *BB = &*F.begin(); auto It = BB->begin(); auto *Call = cast(&*It++); // Check classof(), creation. auto *GO = cast(Call->getCalledOperand()); // Check getAlignment(). EXPECT_EQ(GO->getAlignment(), LLVMGO->getAlignment()); // Check getAlign(). EXPECT_EQ(GO->getAlign(), LLVMGO->getAlign()); // Check setAlignment(). auto OrigMaybeAlign = GO->getAlign(); auto NewMaybeAlign = MaybeAlign(128); EXPECT_NE(NewMaybeAlign, OrigMaybeAlign); GO->setAlignment(NewMaybeAlign); EXPECT_EQ(GO->getAlign(), NewMaybeAlign); GO->setAlignment(OrigMaybeAlign); EXPECT_EQ(GO->getAlign(), OrigMaybeAlign); // Check getGlobalObjectSubClassData(). EXPECT_EQ(GO->getGlobalObjectSubClassData(), LLVMGO->getGlobalObjectSubClassData()); // Check setGlobalObjectSubClassData(). auto OrigGOSCD = GO->getGlobalObjectSubClassData(); auto NewGOSCD = 1u; EXPECT_NE(NewGOSCD, OrigGOSCD); GO->setGlobalObjectSubClassData(NewGOSCD); EXPECT_EQ(GO->getGlobalObjectSubClassData(), NewGOSCD); GO->setGlobalObjectSubClassData(OrigGOSCD); EXPECT_EQ(GO->getGlobalObjectSubClassData(), OrigGOSCD); // Check hasSection(). EXPECT_EQ(GO->hasSection(), LLVMGO->hasSection()); // Check getSection(). EXPECT_EQ(GO->getSection(), LLVMGO->getSection()); // Check setSection(). auto OrigSection = GO->getSection(); auto NewSection = ".some_section"; EXPECT_NE(NewSection, OrigSection); GO->setSection(NewSection); EXPECT_EQ(GO->getSection(), NewSection); GO->setSection(OrigSection); EXPECT_EQ(GO->getSection(), OrigSection); // Check hasComdat(). EXPECT_EQ(GO->hasComdat(), LLVMGO->hasComdat()); // Check getVCallVisibility(). EXPECT_EQ(GO->getVCallVisibility(), LLVMGO->getVCallVisibility()); // Check canIncreaseAlignment(). EXPECT_EQ(GO->canIncreaseAlignment(), LLVMGO->canIncreaseAlignment()); } TEST_F(SandboxIRTest, GlobalIFunc) { parseIR(C, R"IR( declare external void @bar() @ifunc0 = ifunc void(), ptr @foo @ifunc1 = ifunc void(), ptr @foo define void @foo() { call void @ifunc0() call void @ifunc1() call void @bar() ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); auto *LLVMBB = &*LLVMF.begin(); auto LLVMIt = LLVMBB->begin(); auto *LLVMCall0 = cast(&*LLVMIt++); auto *LLVMIFunc0 = cast(LLVMCall0->getCalledOperand()); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *BB = &*F.begin(); auto It = BB->begin(); auto *Call0 = cast(&*It++); auto *Call1 = cast(&*It++); auto *CallBar = cast(&*It++); // Check classof(), creation. auto *IFunc0 = cast(Call0->getCalledOperand()); auto *IFunc1 = cast(Call1->getCalledOperand()); auto *Bar = cast(CallBar->getCalledOperand()); // Check getIterator(). { auto It0 = IFunc0->getIterator(); auto It1 = IFunc1->getIterator(); EXPECT_EQ(&*It0, IFunc0); EXPECT_EQ(&*It1, IFunc1); EXPECT_EQ(std::next(It0), It1); EXPECT_EQ(std::prev(It1), It0); EXPECT_EQ(&*std::next(It0), IFunc1); EXPECT_EQ(&*std::prev(It1), IFunc0); } // Check getReverseIterator(). { auto RevIt0 = IFunc0->getReverseIterator(); auto RevIt1 = IFunc1->getReverseIterator(); EXPECT_EQ(&*RevIt0, IFunc0); EXPECT_EQ(&*RevIt1, IFunc1); EXPECT_EQ(std::prev(RevIt0), RevIt1); EXPECT_EQ(std::next(RevIt1), RevIt0); EXPECT_EQ(&*std::prev(RevIt0), IFunc1); EXPECT_EQ(&*std::next(RevIt1), IFunc0); } // Check setResolver(), getResolver(). EXPECT_EQ(IFunc0->getResolver(), Ctx.getValue(LLVMIFunc0->getResolver())); auto *OrigResolver = IFunc0->getResolver(); auto *NewResolver = Bar; EXPECT_NE(NewResolver, OrigResolver); IFunc0->setResolver(NewResolver); EXPECT_EQ(IFunc0->getResolver(), NewResolver); IFunc0->setResolver(OrigResolver); EXPECT_EQ(IFunc0->getResolver(), OrigResolver); // Check getResolverFunction(). EXPECT_EQ(IFunc0->getResolverFunction(), Ctx.getValue(LLVMIFunc0->getResolverFunction())); // Check isValidLinkage(). for (auto L : {GlobalValue::ExternalLinkage, GlobalValue::AvailableExternallyLinkage, GlobalValue::LinkOnceAnyLinkage, GlobalValue::LinkOnceODRLinkage, GlobalValue::WeakAnyLinkage, GlobalValue::WeakODRLinkage, GlobalValue::AppendingLinkage, GlobalValue::InternalLinkage, GlobalValue::PrivateLinkage, GlobalValue::ExternalWeakLinkage, GlobalValue::CommonLinkage}) { EXPECT_EQ(IFunc0->isValidLinkage(L), LLVMIFunc0->isValidLinkage(L)); } } TEST_F(SandboxIRTest, GlobalVariable) { parseIR(C, R"IR( @glob0 = global i32 42 @glob1 = global i32 43 define void @foo() { %ld0 = load i32, ptr @glob0 %ld1 = load i32, ptr @glob1 ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); auto *LLVMBB = &*LLVMF.begin(); auto LLVMIt = LLVMBB->begin(); auto *LLVMLd0 = cast(&*LLVMIt++); auto *LLVMGV0 = cast(LLVMLd0->getPointerOperand()); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *BB = &*F.begin(); auto It = BB->begin(); auto *Ld0 = cast(&*It++); auto *Ld1 = cast(&*It++); // Check classof(), creation. auto *GV0 = cast(Ld0->getPointerOperand()); auto *GV1 = cast(Ld1->getPointerOperand()); // Check getIterator(). { auto It0 = GV0->getIterator(); auto It1 = GV1->getIterator(); EXPECT_EQ(&*It0, GV0); EXPECT_EQ(&*It1, GV1); EXPECT_EQ(std::next(It0), It1); EXPECT_EQ(std::prev(It1), It0); EXPECT_EQ(&*std::next(It0), GV1); EXPECT_EQ(&*std::prev(It1), GV0); } // Check getReverseIterator(). { auto RevIt0 = GV0->getReverseIterator(); auto RevIt1 = GV1->getReverseIterator(); EXPECT_EQ(&*RevIt0, GV0); EXPECT_EQ(&*RevIt1, GV1); EXPECT_EQ(std::prev(RevIt0), RevIt1); EXPECT_EQ(std::next(RevIt1), RevIt0); EXPECT_EQ(&*std::prev(RevIt0), GV1); EXPECT_EQ(&*std::next(RevIt1), GV0); } // Check hasInitializer(). EXPECT_EQ(GV0->hasInitializer(), LLVMGV0->hasInitializer()); // Check hasDefinitiveInitializer(). EXPECT_EQ(GV0->hasDefinitiveInitializer(), LLVMGV0->hasDefinitiveInitializer()); // Check hasUniqueInitializer(). EXPECT_EQ(GV0->hasUniqueInitializer(), LLVMGV0->hasUniqueInitializer()); // Check getInitializer(). EXPECT_EQ(GV0->getInitializer(), Ctx.getValue(LLVMGV0->getInitializer())); // Check setInitializer(). auto *OrigInitializer = GV0->getInitializer(); auto *NewInitializer = GV1->getInitializer(); EXPECT_NE(NewInitializer, OrigInitializer); GV0->setInitializer(NewInitializer); EXPECT_EQ(GV0->getInitializer(), NewInitializer); GV0->setInitializer(OrigInitializer); EXPECT_EQ(GV0->getInitializer(), OrigInitializer); // Check isConstant(). EXPECT_EQ(GV0->isConstant(), LLVMGV0->isConstant()); // Check setConstant(). bool OrigIsConstant = GV0->isConstant(); bool NewIsConstant = !OrigIsConstant; GV0->setConstant(NewIsConstant); EXPECT_EQ(GV0->isConstant(), NewIsConstant); GV0->setConstant(OrigIsConstant); EXPECT_EQ(GV0->isConstant(), OrigIsConstant); // Check isExternallyInitialized(). EXPECT_EQ(GV0->isExternallyInitialized(), LLVMGV0->isExternallyInitialized()); // Check setExternallyInitialized(). bool OrigIsExtInit = GV0->isExternallyInitialized(); bool NewIsExtInit = !OrigIsExtInit; GV0->setExternallyInitialized(NewIsExtInit); EXPECT_EQ(GV0->isExternallyInitialized(), NewIsExtInit); GV0->setExternallyInitialized(OrigIsExtInit); EXPECT_EQ(GV0->isExternallyInitialized(), OrigIsExtInit); for (auto KindIdx : seq(0, Attribute::AttrKind::EndAttrKinds)) { // Check hasAttribute(AttrKind). auto Kind = static_cast(KindIdx); EXPECT_EQ(GV0->hasAttribute(Kind), LLVMGV0->hasAttribute(Kind)); // Check hasAttribute(StringRef). StringRef KindStr = Attribute::getNameFromAttrKind(Kind); EXPECT_EQ(GV0->hasAttribute(KindStr), LLVMGV0->hasAttribute(KindStr)); } // Check hasAttributes(). EXPECT_EQ(GV0->hasAttributes(), LLVMGV0->hasAttributes()); for (auto KindIdx : seq(0, Attribute::AttrKind::EndAttrKinds)) { // Check getAttribute(AttrKind). auto Kind = static_cast(KindIdx); EXPECT_EQ(GV0->getAttribute(Kind), LLVMGV0->getAttribute(Kind)); // Check getAttribute(StringRef). StringRef KindStr = Attribute::getNameFromAttrKind(Kind); EXPECT_EQ(GV0->getAttribute(KindStr), LLVMGV0->getAttribute(KindStr)); } // Check getAttributes(). EXPECT_EQ(GV0->getAttributes(), LLVMGV0->getAttributes()); // Check getAttributesAsList(). EXPECT_THAT(GV0->getAttributesAsList(0u), testing::ContainerEq(LLVMGV0->getAttributesAsList(0u))); // Check hasImplicitSection(). EXPECT_EQ(GV0->hasImplicitSection(), LLVMGV0->hasImplicitSection()); // Check getCodeModelRaw(). EXPECT_EQ(GV0->getCodeModelRaw(), LLVMGV0->getCodeModelRaw()); // Check getCodeModel(). EXPECT_EQ(GV0->getCodeModel(), LLVMGV0->getCodeModel()); } TEST_F(SandboxIRTest, GlobalAlias) { parseIR(C, R"IR( @alias0 = dso_local alias void(), ptr @foo @alias1 = dso_local alias void(), ptr @foo declare void @bar(); define void @foo() { call void @alias0() call void @alias1() call void @bar() ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); auto *LLVMBB = &*LLVMF.begin(); auto LLVMIt = LLVMBB->begin(); auto *LLVMCall0 = cast(&*LLVMIt++); auto *LLVMAlias0 = cast(LLVMCall0->getCalledOperand()); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *BB = &*F.begin(); auto It = BB->begin(); auto *Call0 = cast(&*It++); auto *Call1 = cast(&*It++); auto *CallBar = cast(&*It++); auto *CalleeBar = cast(CallBar->getCalledOperand()); // Check classof(), creation. auto *Alias0 = cast(Call0->getCalledOperand()); auto *Alias1 = cast(Call1->getCalledOperand()); // Check getIterator(). { auto It0 = Alias0->getIterator(); auto It1 = Alias1->getIterator(); EXPECT_EQ(&*It0, Alias0); EXPECT_EQ(&*It1, Alias1); EXPECT_EQ(std::next(It0), It1); EXPECT_EQ(std::prev(It1), It0); EXPECT_EQ(&*std::next(It0), Alias1); EXPECT_EQ(&*std::prev(It1), Alias0); } // Check getReverseIterator(). { auto RevIt0 = Alias0->getReverseIterator(); auto RevIt1 = Alias1->getReverseIterator(); EXPECT_EQ(&*RevIt0, Alias0); EXPECT_EQ(&*RevIt1, Alias1); EXPECT_EQ(std::prev(RevIt0), RevIt1); EXPECT_EQ(std::next(RevIt1), RevIt0); EXPECT_EQ(&*std::prev(RevIt0), Alias1); EXPECT_EQ(&*std::next(RevIt1), Alias0); } // Check getAliasee(). EXPECT_EQ(Alias0->getAliasee(), Ctx.getValue(LLVMAlias0->getAliasee())); // Check setAliasee(). auto *OrigAliasee = Alias0->getAliasee(); auto *NewAliasee = CalleeBar; EXPECT_NE(NewAliasee, OrigAliasee); Alias0->setAliasee(NewAliasee); EXPECT_EQ(Alias0->getAliasee(), NewAliasee); Alias0->setAliasee(OrigAliasee); EXPECT_EQ(Alias0->getAliasee(), OrigAliasee); // Check getAliaseeObject(). EXPECT_EQ(Alias0->getAliaseeObject(), Ctx.getValue(LLVMAlias0->getAliaseeObject())); } TEST_F(SandboxIRTest, NoCFIValue) { parseIR(C, R"IR( define void @foo() { call void no_cfi @foo() ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *BB = &*F.begin(); auto It = BB->begin(); auto *Call = cast(&*It++); // Check classof(), creation. auto *NoCFI = cast(Call->getCalledOperand()); // Check get(). auto *NewNoCFI = sandboxir::NoCFIValue::get(&F); EXPECT_EQ(NewNoCFI, NoCFI); // Check getGlobalValue(). EXPECT_EQ(NoCFI->getGlobalValue(), &F); // Check getType(). EXPECT_EQ(NoCFI->getType(), F.getType()); } TEST_F(SandboxIRTest, ConstantPtrAuth) { parseIR(C, R"IR( define ptr @foo() { ret ptr ptrauth (ptr @foo, i32 2, i64 1234) } )IR"); Function &LLVMF = *M->getFunction("foo"); auto *LLVMBB = &*LLVMF.begin(); auto *LLVMRet = cast(&*LLVMBB->begin()); auto *LLVMPtrAuth = cast(LLVMRet->getReturnValue()); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *BB = &*F.begin(); auto It = BB->begin(); auto *Ret = cast(&*It++); // Check classof(), creation. auto *PtrAuth = cast(Ret->getReturnValue()); // Check get(), getKey(), getDiscriminator(), getAddrDiscriminator(). auto *NewPtrAuth = sandboxir::ConstantPtrAuth::get( &F, PtrAuth->getKey(), PtrAuth->getDiscriminator(), PtrAuth->getAddrDiscriminator()); EXPECT_EQ(NewPtrAuth, PtrAuth); // Check hasAddressDiscriminator(). EXPECT_EQ(PtrAuth->hasAddressDiscriminator(), LLVMPtrAuth->hasAddressDiscriminator()); // Check hasSpecialAddressDiscriminator(). EXPECT_EQ(PtrAuth->hasSpecialAddressDiscriminator(0u), LLVMPtrAuth->hasSpecialAddressDiscriminator(0u)); // Check isKnownCompatibleWith(). const DataLayout &DL = M->getDataLayout(); EXPECT_TRUE(PtrAuth->isKnownCompatibleWith(PtrAuth->getKey(), PtrAuth->getDiscriminator(), DL)); // Check getWithSameSchema(). EXPECT_EQ(PtrAuth->getWithSameSchema(&F), PtrAuth); } TEST_F(SandboxIRTest, ConstantExpr) { parseIR(C, R"IR( define i32 @foo() { ret i32 ptrtoint (ptr @foo to i32) } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *BB = &*F.begin(); auto It = BB->begin(); auto *Ret = cast(&*It++); // Check classof(), creation. [[maybe_unused]] auto *ConstExpr = cast(Ret->getReturnValue()); } TEST_F(SandboxIRTest, BlockAddress) { parseIR(C, R"IR( define void @foo(ptr %ptr) { bb0: store ptr blockaddress(@foo, %bb0), ptr %ptr ret void bb1: ret void bb2: ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *BB0 = cast( Ctx.getValue(getBasicBlockByName(LLVMF, "bb0"))); auto *BB1 = cast( Ctx.getValue(getBasicBlockByName(LLVMF, "bb1"))); auto *BB2 = cast( Ctx.getValue(getBasicBlockByName(LLVMF, "bb2"))); auto It = BB0->begin(); auto *SI = cast(&*It++); [[maybe_unused]] auto *Ret = cast(&*It++); // Check classof(), creation, getFunction(), getBasicBlock(). auto *BB0Addr = cast(SI->getValueOperand()); EXPECT_EQ(BB0Addr->getBasicBlock(), BB0); EXPECT_EQ(BB0Addr->getFunction(), &F); // Check get(F, BB). auto *NewBB0Addr = sandboxir::BlockAddress::get(&F, BB0); EXPECT_EQ(NewBB0Addr, BB0Addr); // Check get(BB). auto *NewBB0Addr2 = sandboxir::BlockAddress::get(BB0); EXPECT_EQ(NewBB0Addr2, BB0Addr); auto *BB1Addr = sandboxir::BlockAddress::get(BB1); EXPECT_EQ(BB1Addr->getBasicBlock(), BB1); EXPECT_NE(BB1Addr, BB0Addr); // Check lookup(). auto *LookupBB0Addr = sandboxir::BlockAddress::lookup(BB0); EXPECT_EQ(LookupBB0Addr, BB0Addr); auto *LookupBB1Addr = sandboxir::BlockAddress::lookup(BB1); EXPECT_EQ(LookupBB1Addr, BB1Addr); auto *LookupBB2Addr = sandboxir::BlockAddress::lookup(BB2); EXPECT_EQ(LookupBB2Addr, nullptr); } TEST_F(SandboxIRTest, DSOLocalEquivalent) { parseIR(C, R"IR( declare void @bar() define void @foo() { call void dso_local_equivalent @bar() ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *BB = &*F.begin(); auto It = BB->begin(); auto *CI = cast(&*It++); // Check classof(). auto *DSOLE = cast(CI->getCalledOperand()); // Check getGlobalValue(). auto *GV = DSOLE->getGlobalValue(); // Check get(). auto *NewDSOLE = sandboxir::DSOLocalEquivalent::get(GV); EXPECT_EQ(NewDSOLE, DSOLE); } TEST_F(SandboxIRTest, ConstantTokenNone) { parseIR(C, R"IR( define void @foo(ptr %ptr) { bb0: %cs = catchswitch within none [label %handler] unwind to caller handler: ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); [[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF); auto *BB0 = cast( Ctx.getValue(getBasicBlockByName(LLVMF, "bb0"))); auto *CS = cast(&*BB0->begin()); // Check classof(), creation, getFunction(), getBasicBlock(). auto *CTN = cast(CS->getParentPad()); // Check get(). auto *NewCTN = sandboxir::ConstantTokenNone::get(Ctx); EXPECT_EQ(NewCTN, CTN); } TEST_F(SandboxIRTest, Use) { parseIR(C, R"IR( define i32 @foo(i32 %v0, i32 %v1) { %add0 = add i32 %v0, %v1 ret i32 %add0 } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); BasicBlock *LLVMBB = &*LLVMF.begin(); auto LLVMBBIt = LLVMBB->begin(); Instruction *LLVMI0 = &*LLVMBBIt++; Instruction *LLVMRet = &*LLVMBBIt++; Argument *LLVMArg0 = LLVMF.getArg(0); Argument *LLVMArg1 = LLVMF.getArg(1); auto &F = *Ctx.createFunction(&LLVMF); auto &BB = *F.begin(); auto *Arg0 = F.getArg(0); auto *Arg1 = F.getArg(1); auto It = BB.begin(); auto *I0 = &*It++; auto *Ret = cast(&*It++); SmallVector Args{Arg0, Arg1}; unsigned OpIdx = 0; for (sandboxir::Use Use : I0->operands()) { // Check Use.getOperandNo(). EXPECT_EQ(Use.getOperandNo(), OpIdx); // Check Use.getUser(). EXPECT_EQ(Use.getUser(), I0); // Check Use.getContext(). EXPECT_EQ(Use.getContext(), &Ctx); // Check Use.get(). sandboxir::Value *Op = Use.get(); EXPECT_EQ(Op, Ctx.getValue(LLVMI0->getOperand(OpIdx))); // Check Use.getUser(). EXPECT_EQ(Use.getUser(), I0); // Check implicit cast to Value. sandboxir::Value *Cast = Use; EXPECT_EQ(Cast, Op); // Check that Use points to the correct operand. EXPECT_EQ(Op, Args[OpIdx]); // Check getOperand(). EXPECT_EQ(Op, I0->getOperand(OpIdx)); // Check getOperandUse(). EXPECT_EQ(Use, I0->getOperandUse(OpIdx)); ++OpIdx; } EXPECT_EQ(OpIdx, 2u); // Check Use.operator==() and Use.operator!=(). sandboxir::Use UseA = I0->getOperandUse(0); sandboxir::Use UseB = I0->getOperandUse(0); EXPECT_TRUE(UseA == UseB); EXPECT_FALSE(UseA != UseB); // Check getNumOperands(). EXPECT_EQ(I0->getNumOperands(), 2u); EXPECT_EQ(Ret->getNumOperands(), 1u); EXPECT_EQ(Ret->getOperand(0), I0); #ifndef NDEBUG // Check Use.dump(() std::string Buff; raw_string_ostream BS(Buff); BS << "\n"; I0->getOperandUse(0).dumpOS(BS); EXPECT_EQ(Buff, R"IR( Def: i32 %v0 ; SB2. (Argument) User: %add0 = add i32 %v0, %v1 ; SB5. (BinaryOperator) OperandNo: 0 )IR"); #endif // NDEBUG // Check Value.user_begin(). sandboxir::Value::user_iterator UIt = I0->user_begin(); sandboxir::User *U = *UIt; EXPECT_EQ(U, Ret); // Check Value.uses(). EXPECT_EQ(range_size(I0->uses()), 1u); EXPECT_EQ((*I0->uses().begin()).getUser(), Ret); // Check Value.users(). EXPECT_EQ(range_size(I0->users()), 1u); EXPECT_EQ(*I0->users().begin(), Ret); // Check Value.getNumUses(). EXPECT_EQ(I0->getNumUses(), 1u); // Check Value.hasNUsesOrMore(). EXPECT_TRUE(I0->hasNUsesOrMore(0u)); EXPECT_TRUE(I0->hasNUsesOrMore(1u)); EXPECT_FALSE(I0->hasNUsesOrMore(2u)); // Check Value.hasNUses(). EXPECT_FALSE(I0->hasNUses(0u)); EXPECT_TRUE(I0->hasNUses(1u)); EXPECT_FALSE(I0->hasNUses(2u)); // Check Value.getExpectedType // Check User.setOperand(). Ret->setOperand(0, Arg0); EXPECT_EQ(Ret->getOperand(0), Arg0); EXPECT_EQ(Ret->getOperandUse(0).get(), Arg0); EXPECT_EQ(LLVMRet->getOperand(0), LLVMArg0); Ret->setOperand(0, Arg1); EXPECT_EQ(Ret->getOperand(0), Arg1); EXPECT_EQ(Ret->getOperandUse(0).get(), Arg1); EXPECT_EQ(LLVMRet->getOperand(0), LLVMArg1); } TEST_F(SandboxIRTest, RUOW) { parseIR(C, R"IR( declare void @bar0() declare void @bar1() @glob0 = global ptr @bar0 @glob1 = global ptr @bar1 define i32 @foo(i32 %arg0, i32 %arg1) { %add0 = add i32 %arg0, %arg1 %gep1 = getelementptr i8, ptr @glob0, i32 1 %gep2 = getelementptr i8, ptr @glob1, i32 1 ret i32 %add0 } )IR"); llvm::Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto &BB = *F.begin(); auto *Arg0 = F.getArg(0); auto *Arg1 = F.getArg(1); auto It = BB.begin(); auto *I0 = &*It++; auto *I1 = &*It++; auto *I2 = &*It++; auto *Ret = cast(&*It++); bool Replaced; // Try to replace an operand that doesn't match. Replaced = I0->replaceUsesOfWith(Ret, Arg1); EXPECT_FALSE(Replaced); EXPECT_EQ(I0->getOperand(0), Arg0); EXPECT_EQ(I0->getOperand(1), Arg1); // Replace I0 operands when operands differ. Replaced = I0->replaceUsesOfWith(Arg0, Arg1); EXPECT_TRUE(Replaced); EXPECT_EQ(I0->getOperand(0), Arg1); EXPECT_EQ(I0->getOperand(1), Arg1); // Replace I0 operands when operands are the same. Replaced = I0->replaceUsesOfWith(Arg1, Arg0); EXPECT_TRUE(Replaced); EXPECT_EQ(I0->getOperand(0), Arg0); EXPECT_EQ(I0->getOperand(1), Arg0); // Replace Ret operand. Replaced = Ret->replaceUsesOfWith(I0, Arg0); EXPECT_TRUE(Replaced); EXPECT_EQ(Ret->getOperand(0), Arg0); // Check RAUW on constant. auto *Glob0 = cast(I1->getOperand(0)); auto *Glob1 = cast(I2->getOperand(0)); auto *Glob0Op = Glob0->getOperand(0); Glob0->replaceUsesOfWith(Glob0Op, Glob1); EXPECT_EQ(Glob0->getOperand(0), Glob1); } TEST_F(SandboxIRTest, RAUW_RUWIf) { parseIR(C, R"IR( define void @foo(ptr %ptr) { %ld0 = load float, ptr %ptr %ld1 = load float, ptr %ptr store float %ld0, ptr %ptr store float %ld0, ptr %ptr ret void } )IR"); llvm::Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); llvm::BasicBlock *LLVMBB = &*LLVMF.begin(); Ctx.createFunction(&LLVMF); auto *BB = cast(Ctx.getValue(LLVMBB)); auto It = BB->begin(); sandboxir::Instruction *Ld0 = &*It++; sandboxir::Instruction *Ld1 = &*It++; sandboxir::Instruction *St0 = &*It++; sandboxir::Instruction *St1 = &*It++; // Check RUWIf when the lambda returns false. Ld0->replaceUsesWithIf(Ld1, [](const sandboxir::Use &Use) { return false; }); EXPECT_EQ(St0->getOperand(0), Ld0); EXPECT_EQ(St1->getOperand(0), Ld0); // Check RUWIf when the lambda returns true. Ld0->replaceUsesWithIf(Ld1, [](const sandboxir::Use &Use) { return true; }); EXPECT_EQ(St0->getOperand(0), Ld1); EXPECT_EQ(St1->getOperand(0), Ld1); St0->setOperand(0, Ld0); St1->setOperand(0, Ld0); // Check RUWIf user == St0. Ld0->replaceUsesWithIf( Ld1, [St0](const sandboxir::Use &Use) { return Use.getUser() == St0; }); EXPECT_EQ(St0->getOperand(0), Ld1); EXPECT_EQ(St1->getOperand(0), Ld0); St0->setOperand(0, Ld0); // Check RUWIf user == St1. Ld0->replaceUsesWithIf( Ld1, [St1](const sandboxir::Use &Use) { return Use.getUser() == St1; }); EXPECT_EQ(St0->getOperand(0), Ld0); EXPECT_EQ(St1->getOperand(0), Ld1); St1->setOperand(0, Ld0); // Check RAUW. Ld1->replaceAllUsesWith(Ld0); EXPECT_EQ(St0->getOperand(0), Ld0); EXPECT_EQ(St1->getOperand(0), Ld0); } // Check that the operands/users are counted correctly. // I1 // / \ // \ / // I2 TEST_F(SandboxIRTest, DuplicateUses) { parseIR(C, R"IR( define void @foo(i8 %v) { %I1 = add i8 %v, %v %I2 = add i8 %I1, %I1 ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto *F = Ctx.createFunction(&LLVMF); auto *BB = &*F->begin(); auto It = BB->begin(); auto *I1 = &*It++; auto *I2 = &*It++; EXPECT_EQ(range_size(I1->users()), 2u); EXPECT_EQ(range_size(I2->operands()), 2u); } TEST_F(SandboxIRTest, Function) { parseIR(C, R"IR( define void @foo0(i32 %arg0, i32 %arg1) { bb0: br label %bb1 bb1: ret void } define void @foo1() { ret void } )IR"); llvm::Function *LLVMF0 = &*M->getFunction("foo0"); llvm::Function *LLVMF1 = &*M->getFunction("foo1"); llvm::Argument *LLVMArg0 = LLVMF0->getArg(0); llvm::Argument *LLVMArg1 = LLVMF0->getArg(1); sandboxir::Context Ctx(C); sandboxir::Function *F0 = Ctx.createFunction(LLVMF0); sandboxir::Function *F1 = Ctx.createFunction(LLVMF1); // Check getIterator(). { auto It0 = F0->getIterator(); auto It1 = F1->getIterator(); EXPECT_EQ(&*It0, F0); EXPECT_EQ(&*It1, F1); EXPECT_EQ(std::next(It0), It1); EXPECT_EQ(std::prev(It1), It0); EXPECT_EQ(&*std::next(It0), F1); EXPECT_EQ(&*std::prev(It1), F0); } // Check getReverseIterator(). { auto RevIt0 = F0->getReverseIterator(); auto RevIt1 = F1->getReverseIterator(); EXPECT_EQ(&*RevIt0, F0); EXPECT_EQ(&*RevIt1, F1); EXPECT_EQ(std::prev(RevIt0), RevIt1); EXPECT_EQ(std::next(RevIt1), RevIt0); EXPECT_EQ(&*std::prev(RevIt0), F1); EXPECT_EQ(&*std::next(RevIt1), F0); } // Check F arguments EXPECT_EQ(F0->arg_size(), 2u); EXPECT_FALSE(F0->arg_empty()); EXPECT_EQ(F0->getArg(0), Ctx.getValue(LLVMArg0)); EXPECT_EQ(F0->getArg(1), Ctx.getValue(LLVMArg1)); // Check F.begin(), F.end(), Function::iterator llvm::BasicBlock *LLVMBB = &*LLVMF0->begin(); for (sandboxir::BasicBlock &BB : *F0) { EXPECT_EQ(&BB, Ctx.getValue(LLVMBB)); LLVMBB = LLVMBB->getNextNode(); } #ifndef NDEBUG { // Check F.dumpNameAndArgs() std::string Buff; raw_string_ostream BS(Buff); F0->dumpNameAndArgs(BS); EXPECT_EQ(Buff, "void @foo0(i32 %arg0, i32 %arg1)"); } { // Check F.dump() std::string Buff; raw_string_ostream BS(Buff); BS << "\n"; F0->dumpOS(BS); EXPECT_EQ(Buff, R"IR( void @foo0(i32 %arg0, i32 %arg1) { bb0: br label %bb1 ; SB4. (Br) bb1: ret void ; SB6. (Ret) } )IR"); } #endif // NDEBUG } TEST_F(SandboxIRTest, Module) { parseIR(C, R"IR( @glob0 = global i32 42 @glob1 = global i32 43 @internal0 = internal global i32 42 @const0 = constant i32 42 @alias0 = dso_local alias void(), ptr @foo @ifunc = ifunc void(), ptr @foo define void @foo() { ret void } define void @bar() { ret void } )IR"); llvm::Module *LLVMM = &*M; llvm::Function *LLVMFFoo = &*M->getFunction("foo"); llvm::Function *LLVMFBar = &*M->getFunction("bar"); sandboxir::Context Ctx(C); auto *M = Ctx.createModule(LLVMM); // Check getContext(). EXPECT_EQ(&M->getContext(), &Ctx); // Check getFunction(). auto *FFoo = M->getFunction("foo"); auto *FBar = M->getFunction("bar"); EXPECT_EQ(FFoo, Ctx.getValue(LLVMFFoo)); EXPECT_EQ(FBar, Ctx.getValue(LLVMFBar)); // Check getDataLayout(). EXPECT_EQ(&M->getDataLayout(), &LLVMM->getDataLayout()); // Check getSourceFileName(). EXPECT_EQ(M->getSourceFileName(), LLVMM->getSourceFileName()); // Check getGlobalVariable(). for (const char *Name : {"global0", "global1", "internal0"}) EXPECT_EQ(M->getGlobalVariable(Name), Ctx.getValue(LLVMM->getGlobalVariable(Name))); // Check getGlobalVariable(AllowInternal). { auto *Internal0 = M->getGlobalVariable("internal0", /*AllowInternal=*/true); EXPECT_TRUE(Internal0 != nullptr); EXPECT_EQ(Internal0, Ctx.getValue(LLVMM->getNamedGlobal("internal0"))); } // Check getNamedGlobal(). { auto *Internal = M->getNamedGlobal("internal0"); EXPECT_TRUE(Internal != nullptr); EXPECT_EQ(Internal, Ctx.getValue(LLVMM->getNamedGlobal("internal0"))); } // Check getNamedAlias(). auto *Alias0 = M->getNamedAlias("alias0"); EXPECT_EQ(Alias0, Ctx.getValue(LLVMM->getNamedAlias("alias0"))); EXPECT_EQ(M->getNamedAlias("aliasFOO"), nullptr); // Check getNamedIFunc(). auto *IFunc0 = M->getNamedIFunc("ifunc0"); EXPECT_EQ(IFunc0, Ctx.getValue(LLVMM->getNamedAlias("ifunc0"))); EXPECT_EQ(M->getNamedIFunc("ifuncFOO"), nullptr); } TEST_F(SandboxIRTest, BasicBlock) { parseIR(C, R"IR( define void @foo(i32 %v1) { bb0: br label %bb1 bb1: ret void } )IR"); llvm::Function *LLVMF = &*M->getFunction("foo"); llvm::BasicBlock *LLVMBB0 = getBasicBlockByName(*LLVMF, "bb0"); llvm::BasicBlock *LLVMBB1 = getBasicBlockByName(*LLVMF, "bb1"); sandboxir::Context Ctx(C); sandboxir::Function *F = Ctx.createFunction(LLVMF); auto &BB0 = cast(*Ctx.getValue(LLVMBB0)); auto &BB1 = cast(*Ctx.getValue(LLVMBB1)); // Check BB::classof() EXPECT_TRUE(isa(BB0)); EXPECT_FALSE(isa(BB0)); EXPECT_FALSE(isa(BB0)); EXPECT_FALSE(isa(BB0)); EXPECT_FALSE(isa(BB0)); // Check BB.getParent() EXPECT_EQ(BB0.getParent(), F); EXPECT_EQ(BB1.getParent(), F); // Check BBIterator, BB.begin(), BB.end(). llvm::Instruction *LLVMI = &*LLVMBB0->begin(); for (sandboxir::Instruction &I : BB0) { EXPECT_EQ(&I, Ctx.getValue(LLVMI)); LLVMI = LLVMI->getNextNode(); // Check getNodeParent(). EXPECT_EQ(I.getIterator().getNodeParent(), &BB0); } LLVMI = &*LLVMBB1->begin(); for (sandboxir::Instruction &I : BB1) { EXPECT_EQ(&I, Ctx.getValue(LLVMI)); LLVMI = LLVMI->getNextNode(); } // Check NodeParent() for BB::end(). EXPECT_EQ(BB0.end().getNodeParent(), &BB0); // Check BB.getTerminator() EXPECT_EQ(BB0.getTerminator(), Ctx.getValue(LLVMBB0->getTerminator())); EXPECT_EQ(BB1.getTerminator(), Ctx.getValue(LLVMBB1->getTerminator())); // Check BB.rbegin(), BB.rend() EXPECT_EQ(&*BB0.rbegin(), BB0.getTerminator()); EXPECT_EQ(&*std::prev(BB0.rend()), &*BB0.begin()); #ifndef NDEBUG { // Check BB.dump() std::string Buff; raw_string_ostream BS(Buff); BS << "\n"; BB0.dumpOS(BS); EXPECT_EQ(Buff, R"IR( bb0: br label %bb1 ; SB3. (Br) )IR"); } #endif // NDEBUG } TEST_F(SandboxIRTest, Instruction) { parseIR(C, R"IR( define void @foo(i8 %v1, ptr %ptr) { bb0: %add0 = add i8 %v1, %v1 %sub1 = sub i8 %add0, %v1 ret void bb1: %add1 = add i8 %v1, %v1 %sub2 = sub i8 %add1, %v1 %ld0 = load i8, ptr %ptr store i8 %ld0, ptr %ptr store volatile i8 %ld0, ptr %ptr %atomicrmw = atomicrmw add ptr %ptr, i8 %v1 acquire %udiv = udiv i8 %ld0, %v1 %urem = urem i8 %ld0, %v1 call void @foo(), !dbg !1 ret void, !tbaa !2 } !1 = !{} !2 = !{} )IR"); llvm::Function *LLVMF = &*M->getFunction("foo"); llvm::BasicBlock *LLVMBB1 = getBasicBlockByName(*LLVMF, "bb1"); sandboxir::Context Ctx(C); sandboxir::Function *F = Ctx.createFunction(LLVMF); auto *Arg = F->getArg(0); auto *BB = cast( Ctx.getValue(getBasicBlockByName(*LLVMF, "bb0"))); auto It = BB->begin(); auto *I0 = &*It++; auto *I1 = &*It++; auto *Ret = cast(&*It++); // Check getPrevNode(). EXPECT_EQ(Ret->getPrevNode(), I1); EXPECT_EQ(I1->getPrevNode(), I0); EXPECT_EQ(I0->getPrevNode(), nullptr); // Check getNextNode(). EXPECT_EQ(I0->getNextNode(), I1); EXPECT_EQ(I1->getNextNode(), Ret); EXPECT_EQ(Ret->getNextNode(), nullptr); // Check getIterator(). EXPECT_EQ(I0->getIterator(), std::next(BB->begin(), 0)); EXPECT_EQ(I1->getIterator(), std::next(BB->begin(), 1)); EXPECT_EQ(Ret->getIterator(), std::next(BB->begin(), 2)); // Check getOpcode(). EXPECT_EQ(I0->getOpcode(), sandboxir::Instruction::Opcode::Add); EXPECT_EQ(I1->getOpcode(), sandboxir::Instruction::Opcode::Sub); EXPECT_EQ(Ret->getOpcode(), sandboxir::Instruction::Opcode::Ret); // Check getOpcodeName(). EXPECT_STREQ(I0->getOpcodeName(), "Add"); EXPECT_STREQ(I1->getOpcodeName(), "Sub"); EXPECT_STREQ(Ret->getOpcodeName(), "Ret"); EXPECT_STREQ(sandboxir::Instruction::getOpcodeName( sandboxir::Instruction::Opcode::Alloca), "Alloca"); // Check moveBefore(I). I1->moveBefore(I0); EXPECT_EQ(I0->getPrevNode(), I1); EXPECT_EQ(I1->getNextNode(), I0); // Check moveAfter(I). I1->moveAfter(I0); EXPECT_EQ(I0->getNextNode(), I1); EXPECT_EQ(I1->getPrevNode(), I0); // Check comesBefore(I). EXPECT_TRUE(I0->comesBefore(I1)); EXPECT_FALSE(I1->comesBefore(I0)); // Check moveBefore(BB, It). I1->moveBefore(*BB, BB->begin()); EXPECT_EQ(I1->getPrevNode(), nullptr); EXPECT_EQ(I1->getNextNode(), I0); I1->moveBefore(*BB, BB->end()); EXPECT_EQ(I1->getNextNode(), nullptr); EXPECT_EQ(Ret->getNextNode(), I1); I1->moveBefore(*BB, std::next(BB->begin())); EXPECT_EQ(I0->getNextNode(), I1); EXPECT_EQ(I1->getNextNode(), Ret); // Check removeFromParent(). I0->removeFromParent(); #ifndef NDEBUG EXPECT_DEATH(I0->getPrevNode(), ".*Detached.*"); EXPECT_DEATH(I0->getNextNode(), ".*Detached.*"); #endif // NDEBUG EXPECT_EQ(I0->getParent(), nullptr); EXPECT_EQ(I1->getPrevNode(), nullptr); EXPECT_EQ(I0->getOperand(0), Arg); // Check insertBefore(). I0->insertBefore(I1); EXPECT_EQ(I1->getPrevNode(), I0); // Check insertInto(). I0->removeFromParent(); I0->insertInto(BB, BB->end()); EXPECT_EQ(Ret->getNextNode(), I0); I0->moveBefore(I1); EXPECT_EQ(I0->getNextNode(), I1); // Check eraseFromParent(). #ifndef NDEBUG EXPECT_DEATH(I0->eraseFromParent(), "Still connected to users.*"); #endif I1->eraseFromParent(); EXPECT_EQ(I0->getNumUses(), 0u); EXPECT_EQ(I0->getNextNode(), Ret); for (auto &LLVMI : *LLVMBB1) { auto &I = cast(*Ctx.getValue(&LLVMI)); // Check isTerminator(). EXPECT_EQ(LLVMI.isTerminator(), I.isTerminator()); // Check isUnaryOp(). EXPECT_EQ(LLVMI.isUnaryOp(), I.isUnaryOp()); // Check isBinaryOp(). EXPECT_EQ(LLVMI.isBinaryOp(), I.isBinaryOp()); // Check isIntDivRem(). EXPECT_EQ(LLVMI.isIntDivRem(), I.isIntDivRem()); // Check isShift(). EXPECT_EQ(LLVMI.isShift(), I.isShift()); // Check isCast(). EXPECT_EQ(LLVMI.isCast(), I.isCast()); // Check isFuncletPad(). EXPECT_EQ(LLVMI.isFuncletPad(), I.isFuncletPad()); // Check isSpecialTerminator(). EXPECT_EQ(LLVMI.isSpecialTerminator(), I.isSpecialTerminator()); // Check isOnlyUserOfAnyOperand(). EXPECT_EQ(LLVMI.isOnlyUserOfAnyOperand(), I.isOnlyUserOfAnyOperand()); // Check isLogicalShift(). EXPECT_EQ(LLVMI.isLogicalShift(), I.isLogicalShift()); // Check hasMetadata(). EXPECT_EQ(LLVMI.hasMetadata(), I.hasMetadata()); // Check hasMetadataOtherThanDebugLoc(). EXPECT_EQ(LLVMI.hasMetadataOtherThanDebugLoc(), I.hasMetadataOtherThanDebugLoc()); // Check isAssociative(). EXPECT_EQ(LLVMI.isAssociative(), I.isAssociative()); // Check isCommutative(). EXPECT_EQ(LLVMI.isCommutative(), I.isCommutative()); // Check isIdempotent(). EXPECT_EQ(LLVMI.isIdempotent(), I.isIdempotent()); // Check isNilpotent(). EXPECT_EQ(LLVMI.isNilpotent(), I.isNilpotent()); // Check mayWriteToMemory(). EXPECT_EQ(LLVMI.mayWriteToMemory(), I.mayWriteToMemory()); // Check mayReadFromMemory(). EXPECT_EQ(LLVMI.mayReadFromMemory(), I.mayReadFromMemory()); // Check mayReadOrWriteMemory(). EXPECT_EQ(LLVMI.mayReadOrWriteMemory(), I.mayReadOrWriteMemory()); // Check isAtomic(). EXPECT_EQ(LLVMI.isAtomic(), I.isAtomic()); if (I.isAtomic()) { // Check hasAtomicLoad(). EXPECT_EQ(LLVMI.hasAtomicLoad(), I.hasAtomicLoad()); // Check hasAtomicStore(). EXPECT_EQ(LLVMI.hasAtomicStore(), I.hasAtomicStore()); } // Check isVolatile(). EXPECT_EQ(LLVMI.isVolatile(), I.isVolatile()); // Check getAccessType(). EXPECT_EQ(Ctx.getType(LLVMI.getAccessType()), I.getAccessType()); // Check mayThrow(). EXPECT_EQ(LLVMI.mayThrow(), I.mayThrow()); // Check isFenceLike(). EXPECT_EQ(LLVMI.isFenceLike(), I.isFenceLike()); // Check mayHaveSideEffects(). EXPECT_EQ(LLVMI.mayHaveSideEffects(), I.mayHaveSideEffects()); } } TEST_F(SandboxIRTest, VAArgInst) { parseIR(C, R"IR( define void @foo(ptr %va) { %va_arg = va_arg ptr %va, i32 ret void } )IR"); llvm::Function *LLVMF = &*M->getFunction("foo"); sandboxir::Context Ctx(C); sandboxir::Function *F = Ctx.createFunction(LLVMF); auto *Arg = F->getArg(0); auto *BB = &*F->begin(); auto It = BB->begin(); auto *VA = cast(&*It++); auto *Ret = cast(&*It++); // Check getPointerOperand(). EXPECT_EQ(VA->getPointerOperand(), Arg); // Check getPOinterOperandIndex(). EXPECT_EQ(sandboxir::VAArgInst::getPointerOperandIndex(), llvm::VAArgInst::getPointerOperandIndex()); // Check create(). auto *NewVATy = sandboxir::Type::getInt8Ty(Ctx); auto *NewVA = sandboxir::VAArgInst::create(Arg, NewVATy, Ret->getIterator(), Ctx, "NewVA"); EXPECT_EQ(NewVA->getNextNode(), Ret); EXPECT_EQ(NewVA->getType(), NewVATy); #ifndef NDEBUG EXPECT_EQ(NewVA->getName(), "NewVA"); #endif // NDEBUG } TEST_F(SandboxIRTest, FreezeInst) { parseIR(C, R"IR( define void @foo(i8 %arg) { freeze i8 %arg ret void } )IR"); llvm::Function *LLVMF = &*M->getFunction("foo"); sandboxir::Context Ctx(C); sandboxir::Function *F = Ctx.createFunction(LLVMF); auto *Arg = F->getArg(0); auto *BB = &*F->begin(); auto It = BB->begin(); auto *Freeze = cast(&*It++); auto *Ret = cast(&*It++); EXPECT_TRUE(isa(Freeze)); EXPECT_EQ(Freeze->getOperand(0), Arg); // Check create(). auto *NewFreeze = sandboxir::FreezeInst::create(Arg, Ret->getIterator(), Ctx, "NewFreeze"); EXPECT_EQ(NewFreeze->getNextNode(), Ret); #ifndef NDEBUG EXPECT_EQ(NewFreeze->getName(), "NewFreeze"); #endif // NDEBUG } TEST_F(SandboxIRTest, FenceInst) { parseIR(C, R"IR( define void @foo() { fence syncscope("singlethread") seq_cst ret void } )IR"); llvm::Function *LLVMF = &*M->getFunction("foo"); llvm::BasicBlock *LLVMBB = &*LLVMF->begin(); auto *LLVMFence = cast(&*LLVMBB->begin()); sandboxir::Context Ctx(C); sandboxir::Function *F = Ctx.createFunction(LLVMF); auto *BB = &*F->begin(); auto It = BB->begin(); auto *Fence = cast(&*It++); auto *Ret = cast(&*It++); // Check getOrdering(). EXPECT_EQ(Fence->getOrdering(), LLVMFence->getOrdering()); // Check setOrdering(). auto OrigOrdering = Fence->getOrdering(); auto NewOrdering = AtomicOrdering::Release; EXPECT_NE(NewOrdering, OrigOrdering); Fence->setOrdering(NewOrdering); EXPECT_EQ(Fence->getOrdering(), NewOrdering); Fence->setOrdering(OrigOrdering); EXPECT_EQ(Fence->getOrdering(), OrigOrdering); // Check getSyncScopeID(). EXPECT_EQ(Fence->getSyncScopeID(), LLVMFence->getSyncScopeID()); // Check setSyncScopeID(). auto OrigSSID = Fence->getSyncScopeID(); auto NewSSID = SyncScope::System; EXPECT_NE(NewSSID, OrigSSID); Fence->setSyncScopeID(NewSSID); EXPECT_EQ(Fence->getSyncScopeID(), NewSSID); Fence->setSyncScopeID(OrigSSID); EXPECT_EQ(Fence->getSyncScopeID(), OrigSSID); // Check create(). auto *NewFence = sandboxir::FenceInst::create(AtomicOrdering::Release, Ret->getIterator(), Ctx, SyncScope::SingleThread); EXPECT_EQ(NewFence->getNextNode(), Ret); EXPECT_EQ(NewFence->getOrdering(), AtomicOrdering::Release); EXPECT_EQ(NewFence->getSyncScopeID(), SyncScope::SingleThread); } TEST_F(SandboxIRTest, SelectInst) { parseIR(C, R"IR( define void @foo(i1 %c0, i8 %v0, i8 %v1, i1 %c1) { %sel = select i1 %c0, i8 %v0, i8 %v1 ret void } )IR"); llvm::Function *LLVMF = &*M->getFunction("foo"); sandboxir::Context Ctx(C); sandboxir::Function *F = Ctx.createFunction(LLVMF); auto *Cond0 = F->getArg(0); auto *V0 = F->getArg(1); auto *V1 = F->getArg(2); auto *Cond1 = F->getArg(3); auto *BB = &*F->begin(); auto It = BB->begin(); auto *Select = cast(&*It++); const auto *ConstSelect = Select; // To test the const getters. auto *Ret = &*It++; // Check getCondition(). EXPECT_EQ(Select->getCondition(), Cond0); EXPECT_EQ(ConstSelect->getCondition(), Cond0); // Check getTrueValue(). EXPECT_EQ(Select->getTrueValue(), V0); EXPECT_EQ(ConstSelect->getTrueValue(), V0); // Check getFalseValue(). EXPECT_EQ(Select->getFalseValue(), V1); EXPECT_EQ(ConstSelect->getFalseValue(), V1); // Check setCondition(). Select->setCondition(Cond1); EXPECT_EQ(Select->getCondition(), Cond1); // Check setTrueValue(). Select->setTrueValue(V1); EXPECT_EQ(Select->getTrueValue(), V1); // Check setFalseValue(). Select->setFalseValue(V0); EXPECT_EQ(Select->getFalseValue(), V0); // Check swapValues(). Select->swapValues(); EXPECT_EQ(Select->getTrueValue(), V0); EXPECT_EQ(Select->getFalseValue(), V1); // Check areInvalidOperands. EXPECT_EQ(sandboxir::SelectInst::areInvalidOperands(Cond0, V0, V1), nullptr); EXPECT_NE(sandboxir::SelectInst::areInvalidOperands(V0, V1, Cond0), nullptr); { // Check SelectInst::create() InsertBefore. auto *NewSel = cast(sandboxir::SelectInst::create( Cond0, V0, V1, /*InsertBefore=*/Ret->getIterator(), Ctx)); EXPECT_EQ(NewSel->getCondition(), Cond0); EXPECT_EQ(NewSel->getTrueValue(), V0); EXPECT_EQ(NewSel->getFalseValue(), V1); EXPECT_EQ(NewSel->getNextNode(), Ret); } { // Check SelectInst::create() InsertAtEnd. auto *NewSel = cast( sandboxir::SelectInst::create(Cond0, V0, V1, /*InsertAtEnd=*/BB, Ctx)); EXPECT_EQ(NewSel->getCondition(), Cond0); EXPECT_EQ(NewSel->getTrueValue(), V0); EXPECT_EQ(NewSel->getFalseValue(), V1); EXPECT_EQ(NewSel->getPrevNode(), Ret); } { // Check SelectInst::create() Folded. auto *False = sandboxir::ConstantInt::get(sandboxir::Type::getInt1Ty(Ctx), 0, /*IsSigned=*/false); auto *FortyTwo = sandboxir::ConstantInt::get(sandboxir::Type::getInt1Ty(Ctx), 42, /*IsSigned=*/false); auto *NewSel = sandboxir::SelectInst::create(False, FortyTwo, FortyTwo, Ret->getIterator(), Ctx); EXPECT_TRUE(isa(NewSel)); EXPECT_EQ(NewSel, FortyTwo); } } TEST_F(SandboxIRTest, ExtractElementInst) { parseIR(C, R"IR( define void @foo(<2 x i8> %vec, i32 %idx) { %ins0 = extractelement <2 x i8> %vec, i32 %idx ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *ArgVec = F.getArg(0); auto *ArgIdx = F.getArg(1); auto *BB = &*F.begin(); auto It = BB->begin(); auto *EI = cast(&*It++); auto *Ret = &*It++; EXPECT_EQ(EI->getOpcode(), sandboxir::Instruction::Opcode::ExtractElement); EXPECT_EQ(EI->getOperand(0), ArgVec); EXPECT_EQ(EI->getOperand(1), ArgIdx); EXPECT_EQ(EI->getVectorOperand(), ArgVec); EXPECT_EQ(EI->getIndexOperand(), ArgIdx); EXPECT_EQ(EI->getVectorOperandType(), ArgVec->getType()); auto *NewI1 = cast(sandboxir::ExtractElementInst::create( ArgVec, ArgIdx, Ret->getIterator(), Ctx, "NewExtrBeforeRet")); EXPECT_EQ(NewI1->getOperand(0), ArgVec); EXPECT_EQ(NewI1->getOperand(1), ArgIdx); EXPECT_EQ(NewI1->getNextNode(), Ret); auto *NewI2 = cast(sandboxir::ExtractElementInst::create( ArgVec, ArgIdx, BB, Ctx, "NewExtrAtEndOfBB")); EXPECT_EQ(NewI2->getPrevNode(), Ret); auto *LLVMArgVec = LLVMF.getArg(0); auto *LLVMArgIdx = LLVMF.getArg(1); EXPECT_EQ(sandboxir::ExtractElementInst::isValidOperands(ArgVec, ArgIdx), llvm::ExtractElementInst::isValidOperands(LLVMArgVec, LLVMArgIdx)); EXPECT_EQ(sandboxir::ExtractElementInst::isValidOperands(ArgIdx, ArgVec), llvm::ExtractElementInst::isValidOperands(LLVMArgIdx, LLVMArgVec)); } TEST_F(SandboxIRTest, InsertElementInst) { parseIR(C, R"IR( define void @foo(i8 %v0, i8 %v1, <2 x i8> %vec) { %ins0 = insertelement <2 x i8> poison, i8 %v0, i32 0 %ins1 = insertelement <2 x i8> %ins0, i8 %v1, i32 1 ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *Arg0 = F.getArg(0); auto *Arg1 = F.getArg(1); auto *ArgVec = F.getArg(2); auto *BB = &*F.begin(); auto It = BB->begin(); auto *Ins0 = cast(&*It++); auto *Ins1 = cast(&*It++); auto *Ret = &*It++; EXPECT_EQ(Ins0->getOpcode(), sandboxir::Instruction::Opcode::InsertElement); EXPECT_EQ(Ins0->getOperand(1), Arg0); EXPECT_EQ(Ins1->getOperand(1), Arg1); EXPECT_EQ(Ins1->getOperand(0), Ins0); auto *Poison = Ins0->getOperand(0); auto *Idx = Ins0->getOperand(2); auto *NewI1 = cast(sandboxir::InsertElementInst::create( Poison, Arg0, Idx, Ret->getIterator(), Ctx, "NewIns1")); EXPECT_EQ(NewI1->getOperand(0), Poison); EXPECT_EQ(NewI1->getNextNode(), Ret); auto *NewI2 = cast(sandboxir::InsertElementInst::create( Poison, Arg0, Idx, BB, Ctx, "NewIns2")); EXPECT_EQ(NewI2->getPrevNode(), Ret); auto *LLVMArg0 = LLVMF.getArg(0); auto *LLVMArgVec = LLVMF.getArg(2); auto *Zero = sandboxir::ConstantInt::get(sandboxir::Type::getInt8Ty(Ctx), 0); auto *LLVMZero = llvm::ConstantInt::get(Type::getInt8Ty(C), 0); EXPECT_EQ( sandboxir::InsertElementInst::isValidOperands(ArgVec, Arg0, Zero), llvm::InsertElementInst::isValidOperands(LLVMArgVec, LLVMArg0, LLVMZero)); EXPECT_EQ( sandboxir::InsertElementInst::isValidOperands(Arg0, ArgVec, Zero), llvm::InsertElementInst::isValidOperands(LLVMArg0, LLVMArgVec, LLVMZero)); } TEST_F(SandboxIRTest, ShuffleVectorInst) { parseIR(C, R"IR( define void @foo(<2 x i8> %v1, <2 x i8> %v2) { %shuf = shufflevector <2 x i8> %v1, <2 x i8> %v2, <2 x i32> %extr = extractelement <2 x i8> , i32 0 ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *ArgV1 = F.getArg(0); auto *ArgV2 = F.getArg(1); auto *BB = &*F.begin(); auto It = BB->begin(); auto *SVI = cast(&*It++); auto *EEI = cast(&*It++); auto *Ret = &*It++; EXPECT_EQ(SVI->getOpcode(), sandboxir::Instruction::Opcode::ShuffleVector); EXPECT_EQ(SVI->getOperand(0), ArgV1); EXPECT_EQ(SVI->getOperand(1), ArgV2); // In order to test all the methods we need masks of different lengths, so we // can't simply reuse one of the instructions created above. This helper // creates a new `shufflevector %v1, %2, ` with the given mask indices. auto CreateShuffleWithMask = [&](auto &&...Indices) { SmallVector Mask = {Indices...}; return cast( sandboxir::ShuffleVectorInst::create(ArgV1, ArgV2, Mask, Ret->getIterator(), Ctx)); }; // create (InsertBefore) auto *NewI1 = cast(sandboxir::ShuffleVectorInst::create( ArgV1, ArgV2, ArrayRef({0, 2, 1, 3}), Ret->getIterator(), Ctx, "NewShuffleBeforeRet")); EXPECT_EQ(NewI1->getOperand(0), ArgV1); EXPECT_EQ(NewI1->getOperand(1), ArgV2); EXPECT_EQ(NewI1->getNextNode(), Ret); #ifndef NDEBUG EXPECT_EQ(NewI1->getName(), "NewShuffleBeforeRet"); #endif // create (InsertAtEnd) auto *NewI2 = cast(sandboxir::ShuffleVectorInst::create( ArgV1, ArgV2, ArrayRef({0, 1}), BB, Ctx, "NewShuffleAtEndOfBB")); EXPECT_EQ(NewI2->getPrevNode(), Ret); // Test the path that creates a folded constant. We're currently using an // extractelement instruction with a constant operand in the textual IR above // to obtain a constant vector to work with. // TODO: Refactor this once sandboxir::ConstantVector lands. auto *ShouldBeConstant = sandboxir::ShuffleVectorInst::create( EEI->getOperand(0), EEI->getOperand(0), ArrayRef({0, 3}), BB, Ctx); EXPECT_TRUE(isa(ShouldBeConstant)); // isValidOperands auto *LLVMArgV1 = LLVMF.getArg(0); auto *LLVMArgV2 = LLVMF.getArg(1); SmallVector Mask({1, 2}); EXPECT_EQ( sandboxir::ShuffleVectorInst::isValidOperands(ArgV1, ArgV2, Mask), llvm::ShuffleVectorInst::isValidOperands(LLVMArgV1, LLVMArgV2, Mask)); EXPECT_EQ(sandboxir::ShuffleVectorInst::isValidOperands(ArgV1, ArgV1, ArgV1), llvm::ShuffleVectorInst::isValidOperands(LLVMArgV1, LLVMArgV1, LLVMArgV1)); // commute { auto *I = CreateShuffleWithMask(0, 2); I->commute(); EXPECT_EQ(I->getOperand(0), ArgV2); EXPECT_EQ(I->getOperand(1), ArgV1); EXPECT_THAT(I->getShuffleMask(), testing::ElementsAre(2, 0)); } // getType EXPECT_EQ(SVI->getType(), ArgV1->getType()); // getMaskValue EXPECT_EQ(SVI->getMaskValue(0), 0); EXPECT_EQ(SVI->getMaskValue(1), 2); // getShuffleMask / getShuffleMaskForBitcode { EXPECT_THAT(SVI->getShuffleMask(), testing::ElementsAre(0, 2)); SmallVector Result; SVI->getShuffleMask(Result); EXPECT_THAT(Result, testing::ElementsAre(0, 2)); Result.clear(); sandboxir::ShuffleVectorInst::getShuffleMask( SVI->getShuffleMaskForBitcode(), Result); EXPECT_THAT(Result, testing::ElementsAre(0, 2)); } // convertShuffleMaskForBitcode { auto *C = sandboxir::ShuffleVectorInst::convertShuffleMaskForBitcode( ArrayRef({2, 3}), ArgV1->getType()); SmallVector Result; sandboxir::ShuffleVectorInst::getShuffleMask(C, Result); EXPECT_THAT(Result, testing::ElementsAre(2, 3)); } // setShuffleMask { auto *I = CreateShuffleWithMask(0, 1); I->setShuffleMask(ArrayRef({2, 3})); EXPECT_THAT(I->getShuffleMask(), testing::ElementsAre(2, 3)); } // The following functions check different mask properties. Note that most // of these come in three different flavors: a method that checks the mask // in the current instructions and two static member functions that check // a mask given as an ArrayRef or Constant*, so there's quite a bit of // repetition in order to check all of them. // changesLength / increasesLength { auto *I = CreateShuffleWithMask(1); EXPECT_TRUE(I->changesLength()); EXPECT_FALSE(I->increasesLength()); } { auto *I = CreateShuffleWithMask(1, 1); EXPECT_FALSE(I->changesLength()); EXPECT_FALSE(I->increasesLength()); } { auto *I = CreateShuffleWithMask(1, 1, 1); EXPECT_TRUE(I->changesLength()); EXPECT_TRUE(I->increasesLength()); } // isSingleSource / isSingleSourceMask { auto *I = CreateShuffleWithMask(0, 1); EXPECT_TRUE(I->isSingleSource()); EXPECT_TRUE(sandboxir::ShuffleVectorInst::isSingleSourceMask( I->getShuffleMaskForBitcode(), 2)); EXPECT_TRUE(sandboxir::ShuffleVectorInst::isSingleSourceMask( I->getShuffleMask(), 2)); } { auto *I = CreateShuffleWithMask(0, 2); EXPECT_FALSE(I->isSingleSource()); EXPECT_FALSE(sandboxir::ShuffleVectorInst::isSingleSourceMask( I->getShuffleMaskForBitcode(), 2)); EXPECT_FALSE(sandboxir::ShuffleVectorInst::isSingleSourceMask( I->getShuffleMask(), 2)); } // isIdentity / isIdentityMask { auto *I = CreateShuffleWithMask(0, 1); EXPECT_TRUE(I->isIdentity()); EXPECT_TRUE(sandboxir::ShuffleVectorInst::isIdentityMask( I->getShuffleMaskForBitcode(), 2)); EXPECT_TRUE( sandboxir::ShuffleVectorInst::isIdentityMask(I->getShuffleMask(), 2)); } { auto *I = CreateShuffleWithMask(1, 0); EXPECT_FALSE(I->isIdentity()); EXPECT_FALSE(sandboxir::ShuffleVectorInst::isIdentityMask( I->getShuffleMaskForBitcode(), 2)); EXPECT_FALSE( sandboxir::ShuffleVectorInst::isIdentityMask(I->getShuffleMask(), 2)); } // isIdentityWithPadding EXPECT_TRUE(CreateShuffleWithMask(0, 1, -1, -1)->isIdentityWithPadding()); EXPECT_FALSE(CreateShuffleWithMask(0, 1)->isIdentityWithPadding()); // isIdentityWithExtract EXPECT_TRUE(CreateShuffleWithMask(0)->isIdentityWithExtract()); EXPECT_FALSE(CreateShuffleWithMask(0, 1)->isIdentityWithExtract()); EXPECT_FALSE(CreateShuffleWithMask(0, 1, 2)->isIdentityWithExtract()); EXPECT_FALSE(CreateShuffleWithMask(1)->isIdentityWithExtract()); // isConcat EXPECT_TRUE(CreateShuffleWithMask(0, 1, 2, 3)->isConcat()); EXPECT_FALSE(CreateShuffleWithMask(0, 3)->isConcat()); // isSelect / isSelectMask { auto *I = CreateShuffleWithMask(0, 3); EXPECT_TRUE(I->isSelect()); EXPECT_TRUE(sandboxir::ShuffleVectorInst::isSelectMask( I->getShuffleMaskForBitcode(), 2)); EXPECT_TRUE( sandboxir::ShuffleVectorInst::isSelectMask(I->getShuffleMask(), 2)); } { auto *I = CreateShuffleWithMask(0, 2); EXPECT_FALSE(I->isSelect()); EXPECT_FALSE(sandboxir::ShuffleVectorInst::isSelectMask( I->getShuffleMaskForBitcode(), 2)); EXPECT_FALSE( sandboxir::ShuffleVectorInst::isSelectMask(I->getShuffleMask(), 2)); } // isReverse / isReverseMask { auto *I = CreateShuffleWithMask(1, 0); EXPECT_TRUE(I->isReverse()); EXPECT_TRUE(sandboxir::ShuffleVectorInst::isReverseMask( I->getShuffleMaskForBitcode(), 2)); EXPECT_TRUE( sandboxir::ShuffleVectorInst::isReverseMask(I->getShuffleMask(), 2)); } { auto *I = CreateShuffleWithMask(1, 2); EXPECT_FALSE(I->isReverse()); EXPECT_FALSE(sandboxir::ShuffleVectorInst::isReverseMask( I->getShuffleMaskForBitcode(), 2)); EXPECT_FALSE( sandboxir::ShuffleVectorInst::isReverseMask(I->getShuffleMask(), 2)); } // isZeroEltSplat / isZeroEltSplatMask { auto *I = CreateShuffleWithMask(0, 0); EXPECT_TRUE(I->isZeroEltSplat()); EXPECT_TRUE(sandboxir::ShuffleVectorInst::isZeroEltSplatMask( I->getShuffleMaskForBitcode(), 2)); EXPECT_TRUE(sandboxir::ShuffleVectorInst::isZeroEltSplatMask( I->getShuffleMask(), 2)); } { auto *I = CreateShuffleWithMask(1, 1); EXPECT_FALSE(I->isZeroEltSplat()); EXPECT_FALSE(sandboxir::ShuffleVectorInst::isZeroEltSplatMask( I->getShuffleMaskForBitcode(), 2)); EXPECT_FALSE(sandboxir::ShuffleVectorInst::isZeroEltSplatMask( I->getShuffleMask(), 2)); } // isTranspose / isTransposeMask { auto *I = CreateShuffleWithMask(0, 2); EXPECT_TRUE(I->isTranspose()); EXPECT_TRUE(sandboxir::ShuffleVectorInst::isTransposeMask( I->getShuffleMaskForBitcode(), 2)); EXPECT_TRUE( sandboxir::ShuffleVectorInst::isTransposeMask(I->getShuffleMask(), 2)); } { auto *I = CreateShuffleWithMask(1, 1); EXPECT_FALSE(I->isTranspose()); EXPECT_FALSE(sandboxir::ShuffleVectorInst::isTransposeMask( I->getShuffleMaskForBitcode(), 2)); EXPECT_FALSE( sandboxir::ShuffleVectorInst::isTransposeMask(I->getShuffleMask(), 2)); } // isSplice / isSpliceMask { auto *I = CreateShuffleWithMask(1, 2); int Index; EXPECT_TRUE(I->isSplice(Index)); EXPECT_EQ(Index, 1); EXPECT_TRUE(sandboxir::ShuffleVectorInst::isSpliceMask( I->getShuffleMaskForBitcode(), 2, Index)); EXPECT_TRUE(sandboxir::ShuffleVectorInst::isSpliceMask(I->getShuffleMask(), 2, Index)); } { auto *I = CreateShuffleWithMask(2, 1); int Index; EXPECT_FALSE(I->isSplice(Index)); EXPECT_FALSE(sandboxir::ShuffleVectorInst::isSpliceMask( I->getShuffleMaskForBitcode(), 2, Index)); EXPECT_FALSE(sandboxir::ShuffleVectorInst::isSpliceMask(I->getShuffleMask(), 2, Index)); } // isExtractSubvectorMask { auto *I = CreateShuffleWithMask(1); int Index; EXPECT_TRUE(I->isExtractSubvectorMask(Index)); EXPECT_EQ(Index, 1); EXPECT_TRUE(sandboxir::ShuffleVectorInst::isExtractSubvectorMask( I->getShuffleMaskForBitcode(), 2, Index)); EXPECT_TRUE(sandboxir::ShuffleVectorInst::isExtractSubvectorMask( I->getShuffleMask(), 2, Index)); } { auto *I = CreateShuffleWithMask(1, 2); int Index; EXPECT_FALSE(I->isExtractSubvectorMask(Index)); EXPECT_FALSE(sandboxir::ShuffleVectorInst::isExtractSubvectorMask( I->getShuffleMaskForBitcode(), 2, Index)); EXPECT_FALSE(sandboxir::ShuffleVectorInst::isExtractSubvectorMask( I->getShuffleMask(), 2, Index)); } // isInsertSubvectorMask { auto *I = CreateShuffleWithMask(0, 2); int NumSubElts, Index; EXPECT_TRUE(I->isInsertSubvectorMask(NumSubElts, Index)); EXPECT_EQ(Index, 1); EXPECT_EQ(NumSubElts, 1); EXPECT_TRUE(sandboxir::ShuffleVectorInst::isInsertSubvectorMask( I->getShuffleMaskForBitcode(), 2, NumSubElts, Index)); EXPECT_TRUE(sandboxir::ShuffleVectorInst::isInsertSubvectorMask( I->getShuffleMask(), 2, NumSubElts, Index)); } { auto *I = CreateShuffleWithMask(0, 1); int NumSubElts, Index; EXPECT_FALSE(I->isInsertSubvectorMask(NumSubElts, Index)); EXPECT_FALSE(sandboxir::ShuffleVectorInst::isInsertSubvectorMask( I->getShuffleMaskForBitcode(), 2, NumSubElts, Index)); EXPECT_FALSE(sandboxir::ShuffleVectorInst::isInsertSubvectorMask( I->getShuffleMask(), 2, NumSubElts, Index)); } // isReplicationMask { auto *I = CreateShuffleWithMask(0, 0, 0, 1, 1, 1); int ReplicationFactor, VF; EXPECT_TRUE(I->isReplicationMask(ReplicationFactor, VF)); EXPECT_EQ(ReplicationFactor, 3); EXPECT_EQ(VF, 2); EXPECT_TRUE(sandboxir::ShuffleVectorInst::isReplicationMask( I->getShuffleMaskForBitcode(), ReplicationFactor, VF)); EXPECT_TRUE(sandboxir::ShuffleVectorInst::isReplicationMask( I->getShuffleMask(), ReplicationFactor, VF)); } { auto *I = CreateShuffleWithMask(1, 2); int ReplicationFactor, VF; EXPECT_FALSE(I->isReplicationMask(ReplicationFactor, VF)); EXPECT_FALSE(sandboxir::ShuffleVectorInst::isReplicationMask( I->getShuffleMaskForBitcode(), ReplicationFactor, VF)); EXPECT_FALSE(sandboxir::ShuffleVectorInst::isReplicationMask( I->getShuffleMask(), ReplicationFactor, VF)); } // isOneUseSingleSourceMask { auto *I = CreateShuffleWithMask(0, 1, 1, 0); EXPECT_TRUE(I->isOneUseSingleSourceMask(2)); EXPECT_TRUE(sandboxir::ShuffleVectorInst::isOneUseSingleSourceMask( I->getShuffleMask(), 2)); } { auto *I = CreateShuffleWithMask(0, 1, 0, 0); EXPECT_FALSE(I->isOneUseSingleSourceMask(2)); EXPECT_FALSE(sandboxir::ShuffleVectorInst::isOneUseSingleSourceMask( I->getShuffleMask(), 2)); } // commuteShuffleMask { SmallVector M = {0, 2, 1, 3}; ShuffleVectorInst::commuteShuffleMask(M, 2); EXPECT_THAT(M, testing::ElementsAre(2, 0, 3, 1)); } // isInterleave / isInterleaveMask { auto *I = CreateShuffleWithMask(0, 2, 1, 3); EXPECT_TRUE(I->isInterleave(2)); EXPECT_TRUE(sandboxir::ShuffleVectorInst::isInterleaveMask( I->getShuffleMask(), 2, 4)); SmallVector StartIndexes; EXPECT_TRUE(sandboxir::ShuffleVectorInst::isInterleaveMask( I->getShuffleMask(), 2, 4, StartIndexes)); EXPECT_THAT(StartIndexes, testing::ElementsAre(0, 2)); } { auto *I = CreateShuffleWithMask(0, 3, 1, 2); EXPECT_FALSE(I->isInterleave(2)); EXPECT_FALSE(sandboxir::ShuffleVectorInst::isInterleaveMask( I->getShuffleMask(), 2, 4)); } // isDeInterleaveMaskOfFactor { EXPECT_TRUE(sandboxir::ShuffleVectorInst::isDeInterleaveMaskOfFactor( ArrayRef({0, 2}), 2)); EXPECT_FALSE(sandboxir::ShuffleVectorInst::isDeInterleaveMaskOfFactor( ArrayRef({0, 1}), 2)); unsigned Index; EXPECT_TRUE(sandboxir::ShuffleVectorInst::isDeInterleaveMaskOfFactor( ArrayRef({1, 3}), 2, Index)); EXPECT_EQ(Index, 1u); } // isBitRotateMask { unsigned NumSubElts, RotateAmt; EXPECT_TRUE(sandboxir::ShuffleVectorInst::isBitRotateMask( ArrayRef({1, 0, 3, 2, 5, 4, 7, 6}), 8, 2, 2, NumSubElts, RotateAmt)); EXPECT_EQ(NumSubElts, 2u); EXPECT_EQ(RotateAmt, 8u); EXPECT_FALSE(sandboxir::ShuffleVectorInst::isBitRotateMask( ArrayRef({0, 7, 1, 6, 2, 5, 3, 4}), 8, 2, 2, NumSubElts, RotateAmt)); } } TEST_F(SandboxIRTest, ExtractValueInst) { parseIR(C, R"IR( define void @foo({i32, float} %agg) { %ext_simple = extractvalue {i32, float} %agg, 0 %ext_nested = extractvalue {float, {i32}} undef, 1, 0 %const1 = extractvalue {i32, float} {i32 0, float 99.0}, 0 ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); auto *LLVMBB = &*LLVMF.begin(); auto LLVMIt = LLVMBB->begin(); [[maybe_unused]] auto *LLVMExtSimple = cast(&*LLVMIt++); auto *LLVMExtNested = cast(&*LLVMIt++); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *ArgAgg = F.getArg(0); auto *BB = &*F.begin(); auto It = BB->begin(); auto *ExtSimple = cast(&*It++); auto *ExtNested = cast(&*It++); auto *Const1 = cast(&*It++); auto *Ret = &*It++; EXPECT_EQ(ExtSimple->getOperand(0), ArgAgg); // create before instruction auto *NewExtBeforeRet = cast(sandboxir::ExtractValueInst::create( ArgAgg, ArrayRef({0}), Ret->getIterator(), Ctx, "NewExtBeforeRet")); EXPECT_EQ(NewExtBeforeRet->getNextNode(), Ret); #ifndef NDEBUG EXPECT_EQ(NewExtBeforeRet->getName(), "NewExtBeforeRet"); #endif // NDEBUG // create at end of BB auto *NewExtAtEnd = cast(sandboxir::ExtractValueInst::create( ArgAgg, ArrayRef({0}), BB->end(), Ctx, "NewExtAtEnd")); EXPECT_EQ(NewExtAtEnd->getPrevNode(), Ret); #ifndef NDEBUG EXPECT_EQ(NewExtAtEnd->getName(), "NewExtAtEnd"); #endif // NDEBUG // Test the path that creates a folded constant. auto *ShouldBeConstant = sandboxir::ExtractValueInst::create( Const1->getOperand(0), ArrayRef({0}), BB->end(), Ctx); EXPECT_TRUE(isa(ShouldBeConstant)); auto *Zero = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 0); EXPECT_EQ(ShouldBeConstant, Zero); // getIndexedType sandboxir::Type *AggType = ExtNested->getAggregateOperand()->getType(); llvm::Type *LLVMAggType = LLVMExtNested->getAggregateOperand()->getType(); EXPECT_EQ(sandboxir::ExtractValueInst::getIndexedType( AggType, ArrayRef({1, 0})), Ctx.getType(llvm::ExtractValueInst::getIndexedType( LLVMAggType, ArrayRef({1, 0})))); EXPECT_EQ(sandboxir::ExtractValueInst::getIndexedType( AggType, ArrayRef({2})), nullptr); // idx_begin / idx_end { SmallVector IndicesSimple(ExtSimple->idx_begin(), ExtSimple->idx_end()); EXPECT_THAT(IndicesSimple, testing::ElementsAre(0u)); SmallVector IndicesNested(ExtNested->idx_begin(), ExtNested->idx_end()); EXPECT_THAT(IndicesNested, testing::ElementsAre(1u, 0u)); } // indices { SmallVector IndicesSimple(ExtSimple->indices()); EXPECT_THAT(IndicesSimple, testing::ElementsAre(0u)); SmallVector IndicesNested(ExtNested->indices()); EXPECT_THAT(IndicesNested, testing::ElementsAre(1u, 0u)); } // getAggregateOperand EXPECT_EQ(ExtSimple->getAggregateOperand(), ArgAgg); const auto *ConstExtSimple = ExtSimple; EXPECT_EQ(ConstExtSimple->getAggregateOperand(), ArgAgg); // getAggregateOperandIndex EXPECT_EQ(sandboxir::ExtractValueInst::getAggregateOperandIndex(), llvm::ExtractValueInst::getAggregateOperandIndex()); // getIndices EXPECT_EQ(ExtSimple->getIndices().size(), 1u); EXPECT_EQ(ExtSimple->getIndices()[0], 0u); // getNumIndices EXPECT_EQ(ExtSimple->getNumIndices(), 1u); // hasIndices EXPECT_EQ(ExtSimple->hasIndices(), true); } TEST_F(SandboxIRTest, InsertValueInst) { parseIR(C, R"IR( define void @foo({i32, float} %agg, i32 %i) { %ins_simple = insertvalue {i32, float} %agg, i32 %i, 0 %ins_nested = insertvalue {float, {i32}} undef, i32 %i, 1, 0 %const1 = insertvalue {i32, float} {i32 99, float 99.0}, i32 %i, 0 %const2 = insertvalue {i32, float} {i32 0, float 99.0}, i32 %i, 0 ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *ArgAgg = F.getArg(0); auto *ArgInt = F.getArg(1); auto *BB = &*F.begin(); auto It = BB->begin(); auto *InsSimple = cast(&*It++); auto *InsNested = cast(&*It++); // These "const" instructions are helpers to create constant struct operands. // TODO: Remove them once sandboxir::ConstantStruct gets added. auto *Const1 = cast(&*It++); auto *Const2 = cast(&*It++); auto *Ret = &*It++; EXPECT_EQ(InsSimple->getOperand(0), ArgAgg); EXPECT_EQ(InsSimple->getOperand(1), ArgInt); // create before instruction auto *NewInsBeforeRet = cast(sandboxir::InsertValueInst::create( ArgAgg, ArgInt, ArrayRef({0}), Ret->getIterator(), Ctx, "NewInsBeforeRet")); EXPECT_EQ(NewInsBeforeRet->getNextNode(), Ret); #ifndef NDEBUG EXPECT_EQ(NewInsBeforeRet->getName(), "NewInsBeforeRet"); #endif // NDEBUG // create at end of BB auto *NewInsAtEnd = cast(sandboxir::InsertValueInst::create( ArgAgg, ArgInt, ArrayRef({0}), BB, Ctx, "NewInsAtEnd")); EXPECT_EQ(NewInsAtEnd->getPrevNode(), Ret); #ifndef NDEBUG EXPECT_EQ(NewInsAtEnd->getName(), "NewInsAtEnd"); #endif // NDEBUG // Test the path that creates a folded constant. auto *Zero = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 0); auto *ShouldBeConstant = sandboxir::InsertValueInst::create( Const1->getOperand(0), Zero, ArrayRef({0}), BB, Ctx); auto *ExpectedConstant = Const2->getOperand(0); EXPECT_TRUE(isa(ShouldBeConstant)); EXPECT_EQ(ShouldBeConstant, ExpectedConstant); // idx_begin / idx_end { SmallVector IndicesSimple(InsSimple->idx_begin(), InsSimple->idx_end()); EXPECT_THAT(IndicesSimple, testing::ElementsAre(0u)); SmallVector IndicesNested(InsNested->idx_begin(), InsNested->idx_end()); EXPECT_THAT(IndicesNested, testing::ElementsAre(1u, 0u)); } // indices { SmallVector IndicesSimple(InsSimple->indices()); EXPECT_THAT(IndicesSimple, testing::ElementsAre(0u)); SmallVector IndicesNested(InsNested->indices()); EXPECT_THAT(IndicesNested, testing::ElementsAre(1u, 0u)); } // getAggregateOperand EXPECT_EQ(InsSimple->getAggregateOperand(), ArgAgg); const auto *ConstInsSimple = InsSimple; EXPECT_EQ(ConstInsSimple->getAggregateOperand(), ArgAgg); // getAggregateOperandIndex EXPECT_EQ(sandboxir::InsertValueInst::getAggregateOperandIndex(), llvm::InsertValueInst::getAggregateOperandIndex()); // getInsertedValueOperand EXPECT_EQ(InsSimple->getInsertedValueOperand(), ArgInt); EXPECT_EQ(ConstInsSimple->getInsertedValueOperand(), ArgInt); // getInsertedValueOperandIndex EXPECT_EQ(sandboxir::InsertValueInst::getInsertedValueOperandIndex(), llvm::InsertValueInst::getInsertedValueOperandIndex()); // getIndices EXPECT_EQ(InsSimple->getIndices().size(), 1u); EXPECT_EQ(InsSimple->getIndices()[0], 0u); // getNumIndices EXPECT_EQ(InsSimple->getNumIndices(), 1u); // hasIndices EXPECT_EQ(InsSimple->hasIndices(), true); } TEST_F(SandboxIRTest, BranchInst) { parseIR(C, R"IR( define void @foo(i1 %cond0, i1 %cond2) { bb0: br i1 %cond0, label %bb1, label %bb2 bb1: ret void bb2: ret void } )IR"); llvm::Function *LLVMF = &*M->getFunction("foo"); sandboxir::Context Ctx(C); sandboxir::Function *F = Ctx.createFunction(LLVMF); auto *Cond0 = F->getArg(0); auto *Cond1 = F->getArg(1); auto *BB0 = cast( Ctx.getValue(getBasicBlockByName(*LLVMF, "bb0"))); auto *BB1 = cast( Ctx.getValue(getBasicBlockByName(*LLVMF, "bb1"))); auto *Ret1 = BB1->getTerminator(); auto *BB2 = cast( Ctx.getValue(getBasicBlockByName(*LLVMF, "bb2"))); auto *Ret2 = BB2->getTerminator(); auto It = BB0->begin(); auto *Br0 = cast(&*It++); // Check isUnconditional(). EXPECT_FALSE(Br0->isUnconditional()); // Check isConditional(). EXPECT_TRUE(Br0->isConditional()); // Check getCondition(). EXPECT_EQ(Br0->getCondition(), Cond0); // Check setCondition(). Br0->setCondition(Cond1); EXPECT_EQ(Br0->getCondition(), Cond1); // Check getNumSuccessors(). EXPECT_EQ(Br0->getNumSuccessors(), 2u); // Check getSuccessor(). EXPECT_EQ(Br0->getSuccessor(0), BB1); EXPECT_EQ(Br0->getSuccessor(1), BB2); // Check swapSuccessors(). Br0->swapSuccessors(); EXPECT_EQ(Br0->getSuccessor(0), BB2); EXPECT_EQ(Br0->getSuccessor(1), BB1); // Check successors(). EXPECT_EQ(range_size(Br0->successors()), 2u); unsigned SuccIdx = 0; SmallVector ExpectedSuccs({BB1, BB2}); for (sandboxir::BasicBlock *Succ : Br0->successors()) EXPECT_EQ(Succ, ExpectedSuccs[SuccIdx++]); { // Check unconditional BranchInst::create() InsertBefore. auto *Br = sandboxir::BranchInst::create(BB1, Ret1->getIterator(), Ctx); EXPECT_FALSE(Br->isConditional()); EXPECT_TRUE(Br->isUnconditional()); #ifndef NDEBUG EXPECT_DEATH(Br->getCondition(), ".*condition.*"); #endif // NDEBUG unsigned SuccIdx = 0; SmallVector ExpectedSuccs({BB1}); for (sandboxir::BasicBlock *Succ : Br->successors()) EXPECT_EQ(Succ, ExpectedSuccs[SuccIdx++]); EXPECT_EQ(Br->getNextNode(), Ret1); } { // Check unconditional BranchInst::create() InsertAtEnd. auto *Br = sandboxir::BranchInst::create(BB1, /*InsertAtEnd=*/BB1, Ctx); EXPECT_FALSE(Br->isConditional()); EXPECT_TRUE(Br->isUnconditional()); #ifndef NDEBUG EXPECT_DEATH(Br->getCondition(), ".*condition.*"); #endif // NDEBUG unsigned SuccIdx = 0; SmallVector ExpectedSuccs({BB1}); for (sandboxir::BasicBlock *Succ : Br->successors()) EXPECT_EQ(Succ, ExpectedSuccs[SuccIdx++]); EXPECT_EQ(Br->getPrevNode(), Ret1); } { // Check conditional BranchInst::create() InsertBefore. auto *Br = sandboxir::BranchInst::create(BB1, BB2, Cond0, Ret1->getIterator(), Ctx); EXPECT_TRUE(Br->isConditional()); EXPECT_EQ(Br->getCondition(), Cond0); unsigned SuccIdx = 0; SmallVector ExpectedSuccs({BB2, BB1}); for (sandboxir::BasicBlock *Succ : Br->successors()) EXPECT_EQ(Succ, ExpectedSuccs[SuccIdx++]); EXPECT_EQ(Br->getNextNode(), Ret1); } { // Check conditional BranchInst::create() InsertAtEnd. auto *Br = sandboxir::BranchInst::create(BB1, BB2, Cond0, /*InsertAtEnd=*/BB2, Ctx); EXPECT_TRUE(Br->isConditional()); EXPECT_EQ(Br->getCondition(), Cond0); unsigned SuccIdx = 0; SmallVector ExpectedSuccs({BB2, BB1}); for (sandboxir::BasicBlock *Succ : Br->successors()) EXPECT_EQ(Succ, ExpectedSuccs[SuccIdx++]); EXPECT_EQ(Br->getPrevNode(), Ret2); } } TEST_F(SandboxIRTest, LoadInst) { parseIR(C, R"IR( define void @foo(ptr %arg0, ptr %arg1) { %ld = load i8, ptr %arg0, align 64 %vld = load volatile i8, ptr %arg0, align 64 ret void } )IR"); llvm::Function *LLVMF = &*M->getFunction("foo"); sandboxir::Context Ctx(C); sandboxir::Function *F = Ctx.createFunction(LLVMF); auto *Arg0 = F->getArg(0); auto *Arg1 = F->getArg(1); auto *BB = &*F->begin(); auto It = BB->begin(); auto *Ld = cast(&*It++); EXPECT_TRUE(isa(Ld)); auto *VLd = cast(&*It++); auto *Ret = cast(&*It++); bool OrigVolatileValue; // Check isVolatile() EXPECT_FALSE(Ld->isVolatile()); // Check isVolatile() EXPECT_TRUE(VLd->isVolatile()); // Check getPointerOperand() EXPECT_EQ(Ld->getPointerOperand(), Arg0); // Check getAlign() EXPECT_EQ(Ld->getAlign(), 64); // Check create(InsertBefore) sandboxir::LoadInst *NewLd = sandboxir::LoadInst::create( Ld->getType(), Arg1, Align(8), Ret->getIterator(), Ctx, "NewLd"); EXPECT_FALSE(NewLd->isVolatile()); OrigVolatileValue = NewLd->isVolatile(); NewLd->setVolatile(true); EXPECT_TRUE(NewLd->isVolatile()); NewLd->setVolatile(OrigVolatileValue); EXPECT_FALSE(NewLd->isVolatile()); EXPECT_EQ(NewLd->getType(), Ld->getType()); EXPECT_EQ(NewLd->getPointerOperand(), Arg1); EXPECT_EQ(NewLd->getAlign(), 8); EXPECT_EQ(NewLd->getName(), "NewLd"); // Check create(InsertBefore, IsVolatile=true) sandboxir::LoadInst *NewVLd = sandboxir::LoadInst::create( VLd->getType(), Arg1, Align(8), Ret->getIterator(), /*IsVolatile=*/true, Ctx, "NewVLd"); EXPECT_TRUE(NewVLd->isVolatile()); OrigVolatileValue = NewVLd->isVolatile(); NewVLd->setVolatile(false); EXPECT_FALSE(NewVLd->isVolatile()); NewVLd->setVolatile(OrigVolatileValue); EXPECT_TRUE(NewVLd->isVolatile()); EXPECT_EQ(NewVLd->getName(), "NewVLd"); // Check create(InsertAtEnd) sandboxir::LoadInst *NewLdEnd = sandboxir::LoadInst::create(Ld->getType(), Arg1, Align(8), /*InsertAtEnd=*/BB, Ctx, "NewLdEnd"); EXPECT_FALSE(NewLdEnd->isVolatile()); EXPECT_EQ(NewLdEnd->getName(), "NewLdEnd"); EXPECT_EQ(NewLdEnd->getType(), Ld->getType()); EXPECT_EQ(NewLdEnd->getPointerOperand(), Arg1); EXPECT_EQ(NewLdEnd->getAlign(), 8); EXPECT_EQ(NewLdEnd->getParent(), BB); EXPECT_EQ(NewLdEnd->getNextNode(), nullptr); // Check create(InsertAtEnd, IsVolatile=true) sandboxir::LoadInst *NewVLdEnd = sandboxir::LoadInst::create(VLd->getType(), Arg1, Align(8), /*InsertAtEnd=*/BB, /*IsVolatile=*/true, Ctx, "NewVLdEnd"); EXPECT_TRUE(NewVLdEnd->isVolatile()); EXPECT_EQ(NewVLdEnd->getName(), "NewVLdEnd"); EXPECT_EQ(NewVLdEnd->getType(), VLd->getType()); EXPECT_EQ(NewVLdEnd->getPointerOperand(), Arg1); EXPECT_EQ(NewVLdEnd->getAlign(), 8); EXPECT_EQ(NewVLdEnd->getParent(), BB); EXPECT_EQ(NewVLdEnd->getNextNode(), nullptr); } TEST_F(SandboxIRTest, StoreInst) { parseIR(C, R"IR( define void @foo(i8 %val, ptr %ptr) { store i8 %val, ptr %ptr, align 64 store volatile i8 %val, ptr %ptr, align 64 ret void } )IR"); llvm::Function *LLVMF = &*M->getFunction("foo"); sandboxir::Context Ctx(C); sandboxir::Function *F = Ctx.createFunction(LLVMF); auto *Val = F->getArg(0); auto *Ptr = F->getArg(1); auto *BB = &*F->begin(); auto It = BB->begin(); auto *St = cast(&*It++); auto *VSt = cast(&*It++); auto *Ret = cast(&*It++); bool OrigVolatileValue; // Check that the StoreInst has been created correctly. EXPECT_FALSE(St->isVolatile()); EXPECT_TRUE(VSt->isVolatile()); // Check getPointerOperand() EXPECT_EQ(St->getValueOperand(), Val); EXPECT_EQ(St->getPointerOperand(), Ptr); // Check getAlign() EXPECT_EQ(St->getAlign(), 64); // Check create(InsertBefore) sandboxir::StoreInst *NewSt = sandboxir::StoreInst::create(Val, Ptr, Align(8), Ret->getIterator(), Ctx); EXPECT_FALSE(NewSt->isVolatile()); OrigVolatileValue = NewSt->isVolatile(); NewSt->setVolatile(true); EXPECT_TRUE(NewSt->isVolatile()); NewSt->setVolatile(OrigVolatileValue); EXPECT_FALSE(NewSt->isVolatile()); EXPECT_EQ(NewSt->getType(), St->getType()); EXPECT_EQ(NewSt->getValueOperand(), Val); EXPECT_EQ(NewSt->getPointerOperand(), Ptr); EXPECT_EQ(NewSt->getAlign(), 8); EXPECT_EQ(NewSt->getNextNode(), Ret); // Check create(InsertBefore, IsVolatile=true) sandboxir::StoreInst *NewVSt = sandboxir::StoreInst::create(Val, Ptr, Align(8), Ret->getIterator(), /*IsVolatile=*/true, Ctx); EXPECT_TRUE(NewVSt->isVolatile()); OrigVolatileValue = NewVSt->isVolatile(); NewVSt->setVolatile(false); EXPECT_FALSE(NewVSt->isVolatile()); NewVSt->setVolatile(OrigVolatileValue); EXPECT_TRUE(NewVSt->isVolatile()); EXPECT_EQ(NewVSt->getType(), VSt->getType()); EXPECT_EQ(NewVSt->getValueOperand(), Val); EXPECT_EQ(NewVSt->getPointerOperand(), Ptr); EXPECT_EQ(NewVSt->getAlign(), 8); EXPECT_EQ(NewVSt->getNextNode(), Ret); // Check create(InsertAtEnd) sandboxir::StoreInst *NewStEnd = sandboxir::StoreInst::create(Val, Ptr, Align(8), /*InsertAtEnd=*/BB, Ctx); EXPECT_FALSE(NewStEnd->isVolatile()); EXPECT_EQ(NewStEnd->getType(), St->getType()); EXPECT_EQ(NewStEnd->getValueOperand(), Val); EXPECT_EQ(NewStEnd->getPointerOperand(), Ptr); EXPECT_EQ(NewStEnd->getAlign(), 8); EXPECT_EQ(NewStEnd->getParent(), BB); EXPECT_EQ(NewStEnd->getNextNode(), nullptr); // Check create(InsertAtEnd, IsVolatile=true) sandboxir::StoreInst *NewVStEnd = sandboxir::StoreInst::create(Val, Ptr, Align(8), /*InsertAtEnd=*/BB, /*IsVolatile=*/true, Ctx); EXPECT_TRUE(NewVStEnd->isVolatile()); EXPECT_EQ(NewVStEnd->getType(), VSt->getType()); EXPECT_EQ(NewVStEnd->getValueOperand(), Val); EXPECT_EQ(NewVStEnd->getPointerOperand(), Ptr); EXPECT_EQ(NewVStEnd->getAlign(), 8); EXPECT_EQ(NewVStEnd->getParent(), BB); EXPECT_EQ(NewVStEnd->getNextNode(), nullptr); } TEST_F(SandboxIRTest, ReturnInst) { parseIR(C, R"IR( define i8 @foo(i8 %val) { %add = add i8 %val, 42 ret i8 %val } )IR"); llvm::Function *LLVMF = &*M->getFunction("foo"); sandboxir::Context Ctx(C); sandboxir::Function *F = Ctx.createFunction(LLVMF); auto *Val = F->getArg(0); auto *BB = &*F->begin(); auto It = BB->begin(); It++; auto *Ret = cast(&*It++); // Check that the ReturnInst has been created correctly. // Check getReturnValue(). EXPECT_EQ(Ret->getReturnValue(), Val); // Check create(InsertBefore) a void ReturnInst. auto *NewRet1 = cast( sandboxir::ReturnInst::create(nullptr, Ret->getIterator(), Ctx)); EXPECT_EQ(NewRet1->getReturnValue(), nullptr); // Check create(InsertBefore) a non-void ReturnInst. auto *NewRet2 = cast( sandboxir::ReturnInst::create(Val, Ret->getIterator(), Ctx)); EXPECT_EQ(NewRet2->getReturnValue(), Val); // Check create(InsertAtEnd) a void ReturnInst. auto *NewRet3 = cast( sandboxir::ReturnInst::create(nullptr, /*InsertAtEnd=*/BB, Ctx)); EXPECT_EQ(NewRet3->getReturnValue(), nullptr); // Check create(InsertAtEnd) a non-void ReturnInst. auto *NewRet4 = cast( sandboxir::ReturnInst::create(Val, /*InsertAtEnd=*/BB, Ctx)); EXPECT_EQ(NewRet4->getReturnValue(), Val); } TEST_F(SandboxIRTest, CallBase) { parseIR(C, R"IR( declare void @bar1(i8) declare void @bar2() declare void @bar3() declare void @variadic(ptr, ...) define i8 @foo(i8 %arg0, i32 %arg1, ptr %indirectFoo) { %call = call i8 @foo(i8 %arg0, i32 %arg1) call void @bar1(i8 %arg0) call void @bar2() call void %indirectFoo() call void @bar2() noreturn tail call fastcc void @bar2() call void (ptr, ...) @variadic(ptr %indirectFoo, i32 1) ret i8 %call } )IR"); llvm::Function &LLVMF = *M->getFunction("foo"); unsigned ArgIdx = 0; llvm::Argument *LLVMArg0 = LLVMF.getArg(ArgIdx++); llvm::Argument *LLVMArg1 = LLVMF.getArg(ArgIdx++); llvm::BasicBlock *LLVMBB = &*LLVMF.begin(); SmallVector LLVMCalls; auto LLVMIt = LLVMBB->begin(); while (isa(&*LLVMIt)) LLVMCalls.push_back(cast(&*LLVMIt++)); sandboxir::Context Ctx(C); sandboxir::Function &F = *Ctx.createFunction(&LLVMF); for (llvm::CallBase *LLVMCall : LLVMCalls) { // Check classof(Instruction *). auto *Call = cast(Ctx.getValue(LLVMCall)); // Check classof(Value *). EXPECT_TRUE(isa((sandboxir::Value *)Call)); // Check getFunctionType(). EXPECT_EQ(Call->getFunctionType(), Ctx.getType(LLVMCall->getFunctionType())); // Check data_ops(). EXPECT_EQ(range_size(Call->data_ops()), range_size(LLVMCall->data_ops())); auto DataOpIt = Call->data_operands_begin(); for (llvm::Use &LLVMUse : LLVMCall->data_ops()) { Value *LLVMOp = LLVMUse.get(); sandboxir::Use Use = *DataOpIt++; EXPECT_EQ(Ctx.getValue(LLVMOp), Use.get()); // Check isDataOperand(). EXPECT_EQ(Call->isDataOperand(Use), LLVMCall->isDataOperand(&LLVMUse)); // Check getDataOperandNo(). EXPECT_EQ(Call->getDataOperandNo(Use), LLVMCall->getDataOperandNo(&LLVMUse)); // Check isArgOperand(). EXPECT_EQ(Call->isArgOperand(Use), LLVMCall->isArgOperand(&LLVMUse)); // Check isCallee(). EXPECT_EQ(Call->isCallee(Use), LLVMCall->isCallee(&LLVMUse)); } // Check data_operands_empty(). EXPECT_EQ(Call->data_operands_empty(), LLVMCall->data_operands_empty()); // Check data_operands_size(). EXPECT_EQ(Call->data_operands_size(), LLVMCall->data_operands_size()); // Check getNumTotalBundleOperands(). EXPECT_EQ(Call->getNumTotalBundleOperands(), LLVMCall->getNumTotalBundleOperands()); // Check args(). EXPECT_EQ(range_size(Call->args()), range_size(LLVMCall->args())); auto ArgIt = Call->arg_begin(); for (llvm::Use &LLVMUse : LLVMCall->args()) { Value *LLVMArg = LLVMUse.get(); sandboxir::Use Use = *ArgIt++; EXPECT_EQ(Ctx.getValue(LLVMArg), Use.get()); } // Check arg_empty(). EXPECT_EQ(Call->arg_empty(), LLVMCall->arg_empty()); // Check arg_size(). EXPECT_EQ(Call->arg_size(), LLVMCall->arg_size()); for (unsigned ArgIdx = 0, E = Call->arg_size(); ArgIdx != E; ++ArgIdx) { // Check getArgOperand(). EXPECT_EQ(Call->getArgOperand(ArgIdx), Ctx.getValue(LLVMCall->getArgOperand(ArgIdx))); // Check getArgOperandUse(). sandboxir::Use Use = Call->getArgOperandUse(ArgIdx); llvm::Use &LLVMUse = LLVMCall->getArgOperandUse(ArgIdx); EXPECT_EQ(Use.get(), Ctx.getValue(LLVMUse.get())); // Check getArgOperandNo(). EXPECT_EQ(Call->getArgOperandNo(Use), LLVMCall->getArgOperandNo(&LLVMUse)); } // Check hasArgument(). SmallVector TestArgs( {LLVMArg0, LLVMArg1, &LLVMF, LLVMBB, LLVMCall}); for (llvm::Value *LLVMV : TestArgs) { sandboxir::Value *V = Ctx.getValue(LLVMV); EXPECT_EQ(Call->hasArgument(V), LLVMCall->hasArgument(LLVMV)); } // Check getCalledOperand(). EXPECT_EQ(Call->getCalledOperand(), Ctx.getValue(LLVMCall->getCalledOperand())); // Check getCalledOperandUse(). EXPECT_EQ(Call->getCalledOperandUse().get(), Ctx.getValue(LLVMCall->getCalledOperandUse())); // Check getCalledFunction(). if (LLVMCall->getCalledFunction() == nullptr) EXPECT_EQ(Call->getCalledFunction(), nullptr); else { auto *LLVMCF = cast(LLVMCall->getCalledFunction()); (void)LLVMCF; EXPECT_EQ(Call->getCalledFunction(), cast( Ctx.getValue(LLVMCall->getCalledFunction()))); } // Check isIndirectCall(). EXPECT_EQ(Call->isIndirectCall(), LLVMCall->isIndirectCall()); // Check getCaller(). EXPECT_EQ(Call->getCaller(), Ctx.getValue(LLVMCall->getCaller())); // Check isMustTailCall(). EXPECT_EQ(Call->isMustTailCall(), LLVMCall->isMustTailCall()); // Check isTailCall(). EXPECT_EQ(Call->isTailCall(), LLVMCall->isTailCall()); // Check getIntrinsicID(). EXPECT_EQ(Call->getIntrinsicID(), LLVMCall->getIntrinsicID()); // Check getCallingConv(). EXPECT_EQ(Call->getCallingConv(), LLVMCall->getCallingConv()); // Check isInlineAsm(). EXPECT_EQ(Call->isInlineAsm(), LLVMCall->isInlineAsm()); } auto *Arg0 = F.getArg(0); auto *Arg1 = F.getArg(1); auto *BB = &*F.begin(); auto It = BB->begin(); auto *Call0 = cast(&*It++); [[maybe_unused]] auto *Call1 = cast(&*It++); auto *Call2 = cast(&*It++); // Check setArgOperand Call0->setArgOperand(0, Arg1); EXPECT_EQ(Call0->getArgOperand(0), Arg1); Call0->setArgOperand(0, Arg0); EXPECT_EQ(Call0->getArgOperand(0), Arg0); auto *Bar3F = Ctx.createFunction(M->getFunction("bar3")); // Check setCalledOperand auto *SvOp = Call0->getCalledOperand(); Call0->setCalledOperand(Bar3F); EXPECT_EQ(Call0->getCalledOperand(), Bar3F); Call0->setCalledOperand(SvOp); // Check setCalledFunction Call2->setCalledFunction(Bar3F); EXPECT_EQ(Call2->getCalledFunction(), Bar3F); } TEST_F(SandboxIRTest, CallInst) { parseIR(C, R"IR( define i8 @foo(i8 %arg) { %call = call i8 @foo(i8 %arg) ret i8 %call } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); unsigned ArgIdx = 0; auto *Arg0 = F.getArg(ArgIdx++); auto *BB = &*F.begin(); auto It = BB->begin(); auto *Call = cast(&*It++); auto *Ret = cast(&*It++); EXPECT_EQ(Call->getNumOperands(), 2u); EXPECT_EQ(Ret->getOpcode(), sandboxir::Instruction::Opcode::Ret); sandboxir::FunctionType *FTy = F.getFunctionType(); SmallVector Args; Args.push_back(Arg0); { // Check create() WhereIt. auto *Call = cast(sandboxir::CallInst::create( FTy, &F, Args, /*WhereIt=*/Ret->getIterator(), Ctx)); EXPECT_EQ(Call->getNextNode(), Ret); EXPECT_EQ(Call->getCalledFunction(), &F); EXPECT_EQ(range_size(Call->args()), 1u); EXPECT_EQ(Call->getArgOperand(0), Arg0); } { // Check create() InsertBefore. auto *Call = cast( sandboxir::CallInst::create(FTy, &F, Args, Ret->getIterator(), Ctx)); EXPECT_EQ(Call->getNextNode(), Ret); EXPECT_EQ(Call->getCalledFunction(), &F); EXPECT_EQ(range_size(Call->args()), 1u); EXPECT_EQ(Call->getArgOperand(0), Arg0); } { // Check create() InsertAtEnd. auto *Call = cast( sandboxir::CallInst::create(FTy, &F, Args, /*InsertAtEnd=*/BB, Ctx)); EXPECT_EQ(Call->getPrevNode(), Ret); EXPECT_EQ(Call->getCalledFunction(), &F); EXPECT_EQ(range_size(Call->args()), 1u); EXPECT_EQ(Call->getArgOperand(0), Arg0); } } TEST_F(SandboxIRTest, InvokeInst) { parseIR(C, R"IR( define void @foo(i8 %arg) { bb0: invoke i8 @foo(i8 %arg) to label %normal_bb unwind label %exception_bb normal_bb: ret void exception_bb: %lpad = landingpad { ptr, i32} cleanup ret void other_bb: ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *Arg = F.getArg(0); auto *BB0 = cast( Ctx.getValue(getBasicBlockByName(LLVMF, "bb0"))); auto *NormalBB = cast( Ctx.getValue(getBasicBlockByName(LLVMF, "normal_bb"))); auto *ExceptionBB = cast( Ctx.getValue(getBasicBlockByName(LLVMF, "exception_bb"))); auto *LandingPad = &*ExceptionBB->begin(); auto *OtherBB = cast( Ctx.getValue(getBasicBlockByName(LLVMF, "other_bb"))); auto It = BB0->begin(); // Check classof(Instruction *). auto *Invoke = cast(&*It++); // Check getNormalDest(). EXPECT_EQ(Invoke->getNormalDest(), NormalBB); // Check getUnwindDest(). EXPECT_EQ(Invoke->getUnwindDest(), ExceptionBB); // Check getSuccessor(). EXPECT_EQ(Invoke->getSuccessor(0), NormalBB); EXPECT_EQ(Invoke->getSuccessor(1), ExceptionBB); // Check setNormalDest(). Invoke->setNormalDest(OtherBB); EXPECT_EQ(Invoke->getNormalDest(), OtherBB); EXPECT_EQ(Invoke->getUnwindDest(), ExceptionBB); // Check setUnwindDest(). Invoke->setUnwindDest(OtherBB); EXPECT_EQ(Invoke->getNormalDest(), OtherBB); EXPECT_EQ(Invoke->getUnwindDest(), OtherBB); // Check setSuccessor(). Invoke->setSuccessor(0, NormalBB); EXPECT_EQ(Invoke->getNormalDest(), NormalBB); Invoke->setSuccessor(1, ExceptionBB); EXPECT_EQ(Invoke->getUnwindDest(), ExceptionBB); // Check getLandingPadInst(). EXPECT_EQ(Invoke->getLandingPadInst(), LandingPad); { // Check create() WhereIt, WhereBB. SmallVector Args({Arg}); auto *InsertBefore = &*BB0->begin(); auto *NewInvoke = cast(sandboxir::InvokeInst::create( F.getFunctionType(), &F, NormalBB, ExceptionBB, Args, InsertBefore->getIterator(), Ctx)); EXPECT_EQ(NewInvoke->getNormalDest(), NormalBB); EXPECT_EQ(NewInvoke->getUnwindDest(), ExceptionBB); EXPECT_EQ(NewInvoke->getNextNode(), InsertBefore); } { // Check create() InsertBefore. SmallVector Args({Arg}); auto *InsertBefore = &*BB0->begin(); auto *NewInvoke = cast(sandboxir::InvokeInst::create( F.getFunctionType(), &F, NormalBB, ExceptionBB, Args, InsertBefore->getIterator(), Ctx)); EXPECT_EQ(NewInvoke->getNormalDest(), NormalBB); EXPECT_EQ(NewInvoke->getUnwindDest(), ExceptionBB); EXPECT_EQ(NewInvoke->getNextNode(), InsertBefore); } { // Check create() InsertAtEnd. SmallVector Args({Arg}); auto *NewInvoke = cast(sandboxir::InvokeInst::create( F.getFunctionType(), &F, NormalBB, ExceptionBB, Args, BB0, Ctx)); EXPECT_EQ(NewInvoke->getNormalDest(), NormalBB); EXPECT_EQ(NewInvoke->getUnwindDest(), ExceptionBB); EXPECT_EQ(NewInvoke->getParent(), BB0); EXPECT_EQ(NewInvoke->getNextNode(), nullptr); } } TEST_F(SandboxIRTest, CallBrInst) { parseIR(C, R"IR( define void @foo(i8 %arg) { bb0: callbr void asm "", ""() to label %bb1 [label %bb2] bb1: ret void bb2: ret void other_bb: ret void bb3: callbr void @foo(i8 %arg) to label %bb1 [label %bb2] } )IR"); Function &LLVMF = *M->getFunction("foo"); auto *LLVMBB0 = getBasicBlockByName(LLVMF, "bb0"); auto *LLVMCallBr = cast(&*LLVMBB0->begin()); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *Arg = F.getArg(0); auto *BB0 = cast( Ctx.getValue(getBasicBlockByName(LLVMF, "bb0"))); auto *BB1 = cast( Ctx.getValue(getBasicBlockByName(LLVMF, "bb1"))); auto *BB2 = cast( Ctx.getValue(getBasicBlockByName(LLVMF, "bb2"))); auto *BB3 = cast( Ctx.getValue(getBasicBlockByName(LLVMF, "bb3"))); auto *OtherBB = cast( Ctx.getValue(getBasicBlockByName(LLVMF, "other_bb"))); auto It = BB0->begin(); // Check classof(Instruction *). auto *CallBr0 = cast(&*It++); It = BB3->begin(); auto *CallBr1 = cast(&*It++); for (sandboxir::CallBrInst *CallBr : {CallBr0, CallBr1}) { // Check getNumIndirectDests(). EXPECT_EQ(CallBr->getNumIndirectDests(), 1u); // Check getIndirectDestLabel(). EXPECT_EQ(CallBr->getIndirectDestLabel(0), Ctx.getValue(LLVMCallBr->getIndirectDestLabel(0))); // Check getIndirectDestLabelUse(). EXPECT_EQ(CallBr->getIndirectDestLabelUse(0), Ctx.getValue(LLVMCallBr->getIndirectDestLabelUse(0))); // Check getDefaultDest(). EXPECT_EQ(CallBr->getDefaultDest(), Ctx.getValue(LLVMCallBr->getDefaultDest())); // Check getIndirectDest(). EXPECT_EQ(CallBr->getIndirectDest(0), Ctx.getValue(LLVMCallBr->getIndirectDest(0))); // Check getIndirectDests(). auto Dests = CallBr->getIndirectDests(); EXPECT_EQ(Dests.size(), LLVMCallBr->getIndirectDests().size()); EXPECT_EQ(Dests[0], Ctx.getValue(LLVMCallBr->getIndirectDests()[0])); // Check getNumSuccessors(). EXPECT_EQ(CallBr->getNumSuccessors(), LLVMCallBr->getNumSuccessors()); // Check getSuccessor(). for (unsigned SuccIdx = 0, E = CallBr->getNumSuccessors(); SuccIdx != E; ++SuccIdx) EXPECT_EQ(CallBr->getSuccessor(SuccIdx), Ctx.getValue(LLVMCallBr->getSuccessor(SuccIdx))); // Check setDefaultDest(). auto *SvDefaultDest = CallBr->getDefaultDest(); CallBr->setDefaultDest(OtherBB); EXPECT_EQ(CallBr->getDefaultDest(), OtherBB); CallBr->setDefaultDest(SvDefaultDest); // Check setIndirectDest(). auto *SvIndirectDest = CallBr->getIndirectDest(0); CallBr->setIndirectDest(0, OtherBB); EXPECT_EQ(CallBr->getIndirectDest(0), OtherBB); CallBr->setIndirectDest(0, SvIndirectDest); } { // Check create() WhereIt, WhereBB. SmallVector Args({Arg}); auto *NewCallBr = cast(sandboxir::CallBrInst::create( F.getFunctionType(), &F, BB1, {BB2}, Args, BB0->end(), Ctx)); EXPECT_EQ(NewCallBr->getDefaultDest(), BB1); EXPECT_EQ(NewCallBr->getIndirectDests().size(), 1u); EXPECT_EQ(NewCallBr->getIndirectDests()[0], BB2); EXPECT_EQ(NewCallBr->getNextNode(), nullptr); EXPECT_EQ(NewCallBr->getParent(), BB0); } { // Check create() InsertBefore SmallVector Args({Arg}); auto *InsertBefore = &*BB0->rbegin(); auto *NewCallBr = cast( sandboxir::CallBrInst::create(F.getFunctionType(), &F, BB1, {BB2}, Args, InsertBefore->getIterator(), Ctx)); EXPECT_EQ(NewCallBr->getDefaultDest(), BB1); EXPECT_EQ(NewCallBr->getIndirectDests().size(), 1u); EXPECT_EQ(NewCallBr->getIndirectDests()[0], BB2); EXPECT_EQ(NewCallBr->getNextNode(), InsertBefore); } { // Check create() InsertAtEnd. SmallVector Args({Arg}); auto *NewCallBr = cast(sandboxir::CallBrInst::create( F.getFunctionType(), &F, BB1, {BB2}, Args, BB0, Ctx)); EXPECT_EQ(NewCallBr->getDefaultDest(), BB1); EXPECT_EQ(NewCallBr->getIndirectDests().size(), 1u); EXPECT_EQ(NewCallBr->getIndirectDests()[0], BB2); EXPECT_EQ(NewCallBr->getNextNode(), nullptr); EXPECT_EQ(NewCallBr->getParent(), BB0); } } TEST_F(SandboxIRTest, LandingPadInst) { parseIR(C, R"IR( define void @foo() { entry: invoke void @foo() to label %bb unwind label %unwind unwind: %lpad = landingpad { ptr, i32 } catch ptr null ret void bb: ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); auto *LLVMUnwind = getBasicBlockByName(LLVMF, "unwind"); auto *LLVMLPad = cast(&*LLVMUnwind->begin()); sandboxir::Context Ctx(C); [[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF); auto *Unwind = cast(Ctx.getValue(LLVMUnwind)); auto *BB = cast( Ctx.getValue(getBasicBlockByName(LLVMF, "bb"))); auto It = Unwind->begin(); auto *LPad = cast(&*It++); [[maybe_unused]] auto *Ret = cast(&*It++); // Check isCleanup(). EXPECT_EQ(LPad->isCleanup(), LLVMLPad->isCleanup()); // Check setCleanup(). auto OrigIsCleanup = LPad->isCleanup(); auto NewIsCleanup = true; EXPECT_NE(NewIsCleanup, OrigIsCleanup); LPad->setCleanup(NewIsCleanup); EXPECT_EQ(LPad->isCleanup(), NewIsCleanup); LPad->setCleanup(OrigIsCleanup); EXPECT_EQ(LPad->isCleanup(), OrigIsCleanup); // Check getNumClauses(). EXPECT_EQ(LPad->getNumClauses(), LLVMLPad->getNumClauses()); // Check getClause(). for (auto Idx : seq(0, LPad->getNumClauses())) EXPECT_EQ(LPad->getClause(Idx), Ctx.getValue(LLVMLPad->getClause(Idx))); // Check isCatch(). for (auto Idx : seq(0, LPad->getNumClauses())) EXPECT_EQ(LPad->isCatch(Idx), LLVMLPad->isCatch(Idx)); // Check isFilter(). for (auto Idx : seq(0, LPad->getNumClauses())) EXPECT_EQ(LPad->isFilter(Idx), LLVMLPad->isFilter(Idx)); // Check create(). auto *BBRet = &*BB->begin(); auto *NewLPad = cast( sandboxir::LandingPadInst::create(sandboxir::Type::getInt8Ty(Ctx), 0, BBRet->getIterator(), Ctx, "NewLPad")); EXPECT_EQ(NewLPad->getNextNode(), BBRet); EXPECT_FALSE(NewLPad->isCleanup()); #ifndef NDEBUG EXPECT_EQ(NewLPad->getName(), "NewLPad"); #endif // NDEBUG } TEST_F(SandboxIRTest, FuncletPadInst_CatchPadInst_CleanupPadInst) { parseIR(C, R"IR( define void @foo() { dispatch: %cs = catchswitch within none [label %handler0] unwind to caller handler0: %catchpad = catchpad within %cs [ptr @foo] ret void handler1: %cleanuppad = cleanuppad within %cs [ptr @foo] ret void bb: ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); BasicBlock *LLVMDispatch = getBasicBlockByName(LLVMF, "dispatch"); BasicBlock *LLVMHandler0 = getBasicBlockByName(LLVMF, "handler0"); BasicBlock *LLVMHandler1 = getBasicBlockByName(LLVMF, "handler1"); auto *LLVMCP = cast(&*LLVMHandler0->begin()); auto *LLVMCLP = cast(&*LLVMHandler1->begin()); sandboxir::Context Ctx(C); [[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF); auto *Dispatch = cast(Ctx.getValue(LLVMDispatch)); auto *Handler0 = cast(Ctx.getValue(LLVMHandler0)); auto *Handler1 = cast(Ctx.getValue(LLVMHandler1)); auto *BB = cast( Ctx.getValue(getBasicBlockByName(LLVMF, "bb"))); auto *BBRet = cast(&*BB->begin()); auto *CS = cast(&*Dispatch->begin()); [[maybe_unused]] auto *CP = cast(&*Handler0->begin()); [[maybe_unused]] auto *CLP = cast(&*Handler1->begin()); // Check getCatchSwitch(). EXPECT_EQ(CP->getCatchSwitch(), CS); EXPECT_EQ(CP->getCatchSwitch(), Ctx.getValue(LLVMCP->getCatchSwitch())); for (llvm::FuncletPadInst *LLVMFPI : {static_cast(LLVMCP), static_cast(LLVMCLP)}) { auto *FPI = cast(Ctx.getValue(LLVMFPI)); // Check arg_size(). EXPECT_EQ(FPI->arg_size(), LLVMFPI->arg_size()); // Check getParentPad(). EXPECT_EQ(FPI->getParentPad(), Ctx.getValue(LLVMFPI->getParentPad())); // Check setParentPad(). auto *OrigParentPad = FPI->getParentPad(); auto *NewParentPad = Dispatch; EXPECT_NE(NewParentPad, OrigParentPad); FPI->setParentPad(NewParentPad); EXPECT_EQ(FPI->getParentPad(), NewParentPad); FPI->setParentPad(OrigParentPad); EXPECT_EQ(FPI->getParentPad(), OrigParentPad); // Check getArgOperand(). for (auto Idx : seq(0, FPI->arg_size())) EXPECT_EQ(FPI->getArgOperand(Idx), Ctx.getValue(LLVMFPI->getArgOperand(Idx))); // Check setArgOperand(). auto *OrigArgOperand = FPI->getArgOperand(0); auto *NewArgOperand = Dispatch; EXPECT_NE(NewArgOperand, OrigArgOperand); FPI->setArgOperand(0, NewArgOperand); EXPECT_EQ(FPI->getArgOperand(0), NewArgOperand); FPI->setArgOperand(0, OrigArgOperand); EXPECT_EQ(FPI->getArgOperand(0), OrigArgOperand); } // Check CatchPadInst::create(). auto *NewCPI = cast(sandboxir::CatchPadInst::create( CS, {}, BBRet->getIterator(), Ctx, "NewCPI")); EXPECT_EQ(NewCPI->getCatchSwitch(), CS); EXPECT_EQ(NewCPI->arg_size(), 0u); EXPECT_EQ(NewCPI->getNextNode(), BBRet); #ifndef NDEBUG EXPECT_EQ(NewCPI->getName(), "NewCPI"); #endif // NDEBUG // Check CleanupPadInst::create(). auto *NewCLPI = cast(sandboxir::CleanupPadInst::create( CS, {}, BBRet->getIterator(), Ctx, "NewCLPI")); EXPECT_EQ(NewCLPI->getParentPad(), CS); EXPECT_EQ(NewCLPI->arg_size(), 0u); EXPECT_EQ(NewCLPI->getNextNode(), BBRet); #ifndef NDEBUG EXPECT_EQ(NewCLPI->getName(), "NewCLPI"); #endif // NDEBUG } TEST_F(SandboxIRTest, CatchReturnInst) { parseIR(C, R"IR( define void @foo() { dispatch: %cs = catchswitch within none [label %catch] unwind to caller catch: %catchpad = catchpad within %cs [ptr @foo] catchret from %catchpad to label %continue continue: ret void catch2: %catchpad2 = catchpad within %cs [ptr @foo] ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); BasicBlock *LLVMCatch = getBasicBlockByName(LLVMF, "catch"); auto LLVMIt = LLVMCatch->begin(); [[maybe_unused]] auto *LLVMCP = cast(&*LLVMIt++); auto *LLVMCR = cast(&*LLVMIt++); sandboxir::Context Ctx(C); [[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF); auto *Catch = cast(Ctx.getValue(LLVMCatch)); auto *Catch2 = cast( Ctx.getValue(getBasicBlockByName(LLVMF, "catch2"))); auto It = Catch->begin(); [[maybe_unused]] auto *CP = cast(&*It++); auto *CR = cast(&*It++); auto *CP2 = cast(&*Catch2->begin()); // Check getCatchPad(). EXPECT_EQ(CR->getCatchPad(), Ctx.getValue(LLVMCR->getCatchPad())); // Check setCatchPad(). auto *OrigCP = CR->getCatchPad(); auto *NewCP = CP2; EXPECT_NE(NewCP, OrigCP); CR->setCatchPad(NewCP); EXPECT_EQ(CR->getCatchPad(), NewCP); CR->setCatchPad(OrigCP); EXPECT_EQ(CR->getCatchPad(), OrigCP); // Check getSuccessor(). EXPECT_EQ(CR->getSuccessor(), Ctx.getValue(LLVMCR->getSuccessor())); // Check setSuccessor(). auto *OrigSucc = CR->getSuccessor(); auto *NewSucc = Catch; EXPECT_NE(NewSucc, OrigSucc); CR->setSuccessor(NewSucc); EXPECT_EQ(CR->getSuccessor(), NewSucc); CR->setSuccessor(OrigSucc); EXPECT_EQ(CR->getSuccessor(), OrigSucc); // Check getNumSuccessors(). EXPECT_EQ(CR->getNumSuccessors(), LLVMCR->getNumSuccessors()); // Check getCatchSwitchParentPad(). EXPECT_EQ(CR->getCatchSwitchParentPad(), Ctx.getValue(LLVMCR->getCatchSwitchParentPad())); // Check create(). auto *CRI = cast( sandboxir::CatchReturnInst::create(CP, Catch, CP->getIterator(), Ctx)); EXPECT_EQ(CRI->getNextNode(), CP); EXPECT_EQ(CRI->getCatchPad(), CP); EXPECT_EQ(CRI->getSuccessor(), Catch); } TEST_F(SandboxIRTest, CleanupReturnInst) { parseIR(C, R"IR( define void @foo() { dispatch: invoke void @foo() to label %throw unwind label %cleanup throw: ret void cleanup: %cleanuppad = cleanuppad within none [] cleanupret from %cleanuppad unwind label %cleanup2 cleanup2: %cleanuppad2 = cleanuppad within none [] ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); BasicBlock *LLVMCleanup = getBasicBlockByName(LLVMF, "cleanup"); auto LLVMIt = LLVMCleanup->begin(); [[maybe_unused]] auto *LLVMCP = cast(&*LLVMIt++); auto *LLVMCRI = cast(&*LLVMIt++); sandboxir::Context Ctx(C); [[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF); auto *Throw = cast( Ctx.getValue(getBasicBlockByName(LLVMF, "throw"))); auto *Cleanup = cast(Ctx.getValue(LLVMCleanup)); auto *Cleanup2 = cast( Ctx.getValue(getBasicBlockByName(LLVMF, "cleanup2"))); auto It = Cleanup->begin(); [[maybe_unused]] auto *CP = cast(&*It++); auto *CRI = cast(&*It++); It = Cleanup2->begin(); auto *CP2 = cast(&*It++); auto *Ret = cast(&*It++); // Check hasUnwindDest(). EXPECT_EQ(CRI->hasUnwindDest(), LLVMCRI->hasUnwindDest()); // Check unwindsToCaller(). EXPECT_EQ(CRI->unwindsToCaller(), LLVMCRI->unwindsToCaller()); // Check getCleanupPad(). EXPECT_EQ(CRI->getCleanupPad(), Ctx.getValue(LLVMCRI->getCleanupPad())); // Check setCleanupPad(). auto *OrigCleanupPad = CRI->getCleanupPad(); auto *NewCleanupPad = CP2; EXPECT_NE(NewCleanupPad, OrigCleanupPad); CRI->setCleanupPad(NewCleanupPad); EXPECT_EQ(CRI->getCleanupPad(), NewCleanupPad); CRI->setCleanupPad(OrigCleanupPad); EXPECT_EQ(CRI->getCleanupPad(), OrigCleanupPad); // Check setNumSuccessors(). EXPECT_EQ(CRI->getNumSuccessors(), LLVMCRI->getNumSuccessors()); // Check getUnwindDest(). EXPECT_EQ(CRI->getUnwindDest(), Ctx.getValue(LLVMCRI->getUnwindDest())); // Check setUnwindDest(). auto *OrigUnwindDest = CRI->getUnwindDest(); auto *NewUnwindDest = Throw; EXPECT_NE(NewUnwindDest, OrigUnwindDest); CRI->setUnwindDest(NewUnwindDest); EXPECT_EQ(CRI->getUnwindDest(), NewUnwindDest); CRI->setUnwindDest(OrigUnwindDest); EXPECT_EQ(CRI->getUnwindDest(), OrigUnwindDest); // Check create(). auto *UnwindBB = Cleanup; auto *NewCRI = sandboxir::CleanupReturnInst::create(CP2, UnwindBB, Ret->getIterator(), Ctx); EXPECT_EQ(NewCRI->getCleanupPad(), CP2); EXPECT_EQ(NewCRI->getUnwindDest(), UnwindBB); EXPECT_EQ(NewCRI->getNextNode(), Ret); } TEST_F(SandboxIRTest, GetElementPtrInstruction) { parseIR(C, R"IR( define void @foo(ptr %ptr, <2 x ptr> %ptrs) { %gep0 = getelementptr i8, ptr %ptr, i32 0 %gep1 = getelementptr nusw i8, ptr %ptr, i32 0 %gep2 = getelementptr nuw i8, ptr %ptr, i32 0 %gep3 = getelementptr inbounds {i32, {i32, i8}}, ptr %ptr, i32 1, i32 0 %gep4 = getelementptr inbounds {i8, i8, {i32, i16}}, <2 x ptr> %ptrs, i32 2, <2 x i32> ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); BasicBlock *LLVMBB = &*LLVMF.begin(); auto LLVMIt = LLVMBB->begin(); SmallVector LLVMGEPs; while (isa(&*LLVMIt)) LLVMGEPs.push_back(cast(&*LLVMIt++)); auto *LLVMRet = cast(&*LLVMIt++); sandboxir::Context Ctx(C); [[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF); for (llvm::GetElementPtrInst *LLVMGEP : LLVMGEPs) { // Check classof(). auto *GEP = cast(Ctx.getValue(LLVMGEP)); // Check getSourceElementType(). EXPECT_EQ(GEP->getSourceElementType(), Ctx.getType(LLVMGEP->getSourceElementType())); // Check getResultElementType(). EXPECT_EQ(GEP->getResultElementType(), Ctx.getType(LLVMGEP->getResultElementType())); // Check getAddressSpace(). EXPECT_EQ(GEP->getAddressSpace(), LLVMGEP->getAddressSpace()); // Check indices(). EXPECT_EQ(range_size(GEP->indices()), range_size(LLVMGEP->indices())); auto IdxIt = GEP->idx_begin(); for (llvm::Value *LLVMIdxV : LLVMGEP->indices()) { sandboxir::Value *IdxV = *IdxIt++; EXPECT_EQ(IdxV, Ctx.getValue(LLVMIdxV)); } // Check getPointerOperand(). EXPECT_EQ(GEP->getPointerOperand(), Ctx.getValue(LLVMGEP->getPointerOperand())); // Check getPointerOperandIndex(). EXPECT_EQ(GEP->getPointerOperandIndex(), LLVMGEP->getPointerOperandIndex()); // Check getPointerOperandType(). EXPECT_EQ(GEP->getPointerOperandType(), Ctx.getType(LLVMGEP->getPointerOperandType())); // Check getPointerAddressSpace(). EXPECT_EQ(GEP->getPointerAddressSpace(), LLVMGEP->getPointerAddressSpace()); // Check getNumIndices(). EXPECT_EQ(GEP->getNumIndices(), LLVMGEP->getNumIndices()); // Check hasIndices(). EXPECT_EQ(GEP->hasIndices(), LLVMGEP->hasIndices()); // Check hasAllConstantIndices(). EXPECT_EQ(GEP->hasAllConstantIndices(), LLVMGEP->hasAllConstantIndices()); // Check getNoWrapFlags(). EXPECT_EQ(GEP->getNoWrapFlags(), LLVMGEP->getNoWrapFlags()); // Check isInBounds(). EXPECT_EQ(GEP->isInBounds(), LLVMGEP->isInBounds()); // Check hasNoUnsignedWrap(). EXPECT_EQ(GEP->hasNoUnsignedWrap(), LLVMGEP->hasNoUnsignedWrap()); // Check accumulateConstantOffset(). const DataLayout &DL = M->getDataLayout(); APInt Offset1 = APInt::getZero(DL.getIndexSizeInBits(GEP->getPointerAddressSpace())); APInt Offset2 = APInt::getZero(DL.getIndexSizeInBits(GEP->getPointerAddressSpace())); EXPECT_EQ(GEP->accumulateConstantOffset(DL, Offset1), LLVMGEP->accumulateConstantOffset(DL, Offset2)); EXPECT_EQ(Offset1, Offset2); } auto *BB = &*F.begin(); auto *GEP0 = cast(&*BB->begin()); auto *Ret = cast(Ctx.getValue(LLVMRet)); SmallVector Indices(GEP0->indices()); // Check create() WhereIt, WhereBB. auto *NewGEP0 = cast(sandboxir::GetElementPtrInst::create( GEP0->getType(), GEP0->getPointerOperand(), Indices, Ret->getIterator(), Ctx, "NewGEP0")); EXPECT_EQ(NewGEP0->getName(), "NewGEP0"); EXPECT_EQ(NewGEP0->getType(), GEP0->getType()); EXPECT_EQ(NewGEP0->getPointerOperand(), GEP0->getPointerOperand()); EXPECT_EQ(range_size(NewGEP0->indices()), range_size(GEP0->indices())); for (auto NewIt = NewGEP0->idx_begin(), NewItE = NewGEP0->idx_end(), OldIt = GEP0->idx_begin(); NewIt != NewItE; ++NewIt) { sandboxir::Value *NewIdxV = *NewIt; sandboxir::Value *OldIdxV = *OldIt; EXPECT_EQ(NewIdxV, OldIdxV); } EXPECT_EQ(NewGEP0->getNextNode(), Ret); // Check create() InsertBefore. auto *NewGEP1 = cast(sandboxir::GetElementPtrInst::create( GEP0->getType(), GEP0->getPointerOperand(), Indices, Ret->getIterator(), Ctx, "NewGEP1")); EXPECT_EQ(NewGEP1->getName(), "NewGEP1"); EXPECT_EQ(NewGEP1->getType(), GEP0->getType()); EXPECT_EQ(NewGEP1->getPointerOperand(), GEP0->getPointerOperand()); EXPECT_EQ(range_size(NewGEP1->indices()), range_size(GEP0->indices())); for (auto NewIt = NewGEP0->idx_begin(), NewItE = NewGEP0->idx_end(), OldIt = GEP0->idx_begin(); NewIt != NewItE; ++NewIt) { sandboxir::Value *NewIdxV = *NewIt; sandboxir::Value *OldIdxV = *OldIt; EXPECT_EQ(NewIdxV, OldIdxV); } EXPECT_EQ(NewGEP1->getNextNode(), Ret); // Check create() InsertAtEnd. auto *NewGEP2 = cast(sandboxir::GetElementPtrInst::create( GEP0->getType(), GEP0->getPointerOperand(), Indices, BB, Ctx, "NewGEP2")); EXPECT_EQ(NewGEP2->getName(), "NewGEP2"); EXPECT_EQ(NewGEP2->getType(), GEP0->getType()); EXPECT_EQ(NewGEP2->getPointerOperand(), GEP0->getPointerOperand()); EXPECT_EQ(range_size(NewGEP2->indices()), range_size(GEP0->indices())); for (auto NewIt = NewGEP0->idx_begin(), NewItE = NewGEP0->idx_end(), OldIt = GEP0->idx_begin(); NewIt != NewItE; ++NewIt) { sandboxir::Value *NewIdxV = *NewIt; sandboxir::Value *OldIdxV = *OldIt; EXPECT_EQ(NewIdxV, OldIdxV); } EXPECT_EQ(NewGEP2->getPrevNode(), Ret); EXPECT_EQ(NewGEP2->getNextNode(), nullptr); } TEST_F(SandboxIRTest, Flags) { parseIR(C, R"IR( define void @foo(i32 %arg, float %farg) { %add = add i32 %arg, %arg %fadd = fadd float %farg, %farg %udiv = udiv i32 %arg, %arg ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); BasicBlock *LLVMBB = &*LLVMF.begin(); auto LLVMIt = LLVMBB->begin(); auto *LLVMAdd = &*LLVMIt++; auto *LLVMFAdd = &*LLVMIt++; auto *LLVMUDiv = &*LLVMIt++; sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *BB = &*F.begin(); auto It = BB->begin(); auto *Add = &*It++; auto *FAdd = &*It++; auto *UDiv = &*It++; #define CHECK_FLAG(I, LLVMI, GETTER, SETTER) \ { \ EXPECT_EQ(I->GETTER(), LLVMI->GETTER()); \ bool NewFlagVal = !I->GETTER(); \ I->SETTER(NewFlagVal); \ EXPECT_EQ(I->GETTER(), NewFlagVal); \ EXPECT_EQ(I->GETTER(), LLVMI->GETTER()); \ } CHECK_FLAG(Add, LLVMAdd, hasNoUnsignedWrap, setHasNoUnsignedWrap); CHECK_FLAG(Add, LLVMAdd, hasNoSignedWrap, setHasNoSignedWrap); CHECK_FLAG(FAdd, LLVMFAdd, isFast, setFast); CHECK_FLAG(FAdd, LLVMFAdd, hasAllowReassoc, setHasAllowReassoc); CHECK_FLAG(UDiv, LLVMUDiv, isExact, setIsExact); CHECK_FLAG(FAdd, LLVMFAdd, hasNoNaNs, setHasNoNaNs); CHECK_FLAG(FAdd, LLVMFAdd, hasNoInfs, setHasNoInfs); CHECK_FLAG(FAdd, LLVMFAdd, hasNoSignedZeros, setHasNoSignedZeros); CHECK_FLAG(FAdd, LLVMFAdd, hasAllowReciprocal, setHasAllowReciprocal); CHECK_FLAG(FAdd, LLVMFAdd, hasAllowContract, setHasAllowContract); CHECK_FLAG(FAdd, LLVMFAdd, hasApproxFunc, setHasApproxFunc); // Check getFastMathFlags(), copyFastMathFlags(). FAdd->setFastMathFlags(FastMathFlags::getFast()); EXPECT_FALSE(FAdd->getFastMathFlags() != LLVMFAdd->getFastMathFlags()); FastMathFlags OrigFMF = FAdd->getFastMathFlags(); FastMathFlags NewFMF; NewFMF.setAllowReassoc(true); EXPECT_TRUE(NewFMF != OrigFMF); FAdd->setFastMathFlags(NewFMF); EXPECT_FALSE(FAdd->getFastMathFlags() != OrigFMF); FAdd->copyFastMathFlags(NewFMF); EXPECT_FALSE(FAdd->getFastMathFlags() != NewFMF); EXPECT_FALSE(FAdd->getFastMathFlags() != LLVMFAdd->getFastMathFlags()); } TEST_F(SandboxIRTest, CatchSwitchInst) { parseIR(C, R"IR( define void @foo(i32 %cond0, i32 %cond1) { bb0: %cs0 = catchswitch within none [label %handler0, label %handler1] unwind to caller bb1: %cs1 = catchswitch within %cs0 [label %handler0, label %handler1] unwind label %cleanup handler0: ret void handler1: ret void cleanup: ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); auto *LLVMBB0 = getBasicBlockByName(LLVMF, "bb0"); auto *LLVMBB1 = getBasicBlockByName(LLVMF, "bb1"); auto *LLVMHandler0 = getBasicBlockByName(LLVMF, "handler0"); auto *LLVMHandler1 = getBasicBlockByName(LLVMF, "handler1"); auto *LLVMCleanup = getBasicBlockByName(LLVMF, "cleanup"); auto *LLVMCS0 = cast(&*LLVMBB0->begin()); auto *LLVMCS1 = cast(&*LLVMBB1->begin()); sandboxir::Context Ctx(C); [[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF); auto *BB0 = cast(Ctx.getValue(LLVMBB0)); auto *BB1 = cast(Ctx.getValue(LLVMBB1)); auto *Handler0 = cast(Ctx.getValue(LLVMHandler0)); auto *Handler1 = cast(Ctx.getValue(LLVMHandler1)); auto *Cleanup = cast(Ctx.getValue(LLVMCleanup)); auto *CS0 = cast(&*BB0->begin()); auto *CS1 = cast(&*BB1->begin()); // Check getParentPad(). EXPECT_EQ(CS0->getParentPad(), Ctx.getValue(LLVMCS0->getParentPad())); EXPECT_EQ(CS1->getParentPad(), Ctx.getValue(LLVMCS1->getParentPad())); // Check setParentPad(). auto *OrigPad = CS0->getParentPad(); auto *NewPad = CS1; EXPECT_NE(NewPad, OrigPad); CS0->setParentPad(NewPad); EXPECT_EQ(CS0->getParentPad(), NewPad); CS0->setParentPad(OrigPad); EXPECT_EQ(CS0->getParentPad(), OrigPad); // Check hasUnwindDest(). EXPECT_EQ(CS0->hasUnwindDest(), LLVMCS0->hasUnwindDest()); EXPECT_EQ(CS1->hasUnwindDest(), LLVMCS1->hasUnwindDest()); // Check unwindsToCaller(). EXPECT_EQ(CS0->unwindsToCaller(), LLVMCS0->unwindsToCaller()); EXPECT_EQ(CS1->unwindsToCaller(), LLVMCS1->unwindsToCaller()); // Check getUnwindDest(). EXPECT_EQ(CS0->getUnwindDest(), Ctx.getValue(LLVMCS0->getUnwindDest())); EXPECT_EQ(CS1->getUnwindDest(), Ctx.getValue(LLVMCS1->getUnwindDest())); // Check setUnwindDest(). auto *OrigUnwindDest = CS1->getUnwindDest(); auto *NewUnwindDest = BB0; EXPECT_NE(NewUnwindDest, OrigUnwindDest); CS1->setUnwindDest(NewUnwindDest); EXPECT_EQ(CS1->getUnwindDest(), NewUnwindDest); CS1->setUnwindDest(OrigUnwindDest); EXPECT_EQ(CS1->getUnwindDest(), OrigUnwindDest); // Check getNumHandlers(). EXPECT_EQ(CS0->getNumHandlers(), LLVMCS0->getNumHandlers()); EXPECT_EQ(CS1->getNumHandlers(), LLVMCS1->getNumHandlers()); // Check handler_begin(), handler_end(). auto It = CS0->handler_begin(); EXPECT_EQ(*It++, Handler0); EXPECT_EQ(*It++, Handler1); EXPECT_EQ(It, CS0->handler_end()); // Check handlers(). SmallVector Handlers; for (sandboxir::BasicBlock *Handler : CS0->handlers()) Handlers.push_back(Handler); EXPECT_EQ(Handlers.size(), 2u); EXPECT_EQ(Handlers[0], Handler0); EXPECT_EQ(Handlers[1], Handler1); // Check addHandler(). CS0->addHandler(BB0); EXPECT_EQ(CS0->getNumHandlers(), 3u); EXPECT_EQ(*std::next(CS0->handler_begin(), 2), BB0); // Check getNumSuccessors(). EXPECT_EQ(CS0->getNumSuccessors(), LLVMCS0->getNumSuccessors()); EXPECT_EQ(CS1->getNumSuccessors(), LLVMCS1->getNumSuccessors()); // Check getSuccessor(). for (auto SuccIdx : seq(0, CS0->getNumSuccessors())) EXPECT_EQ(CS0->getSuccessor(SuccIdx), Ctx.getValue(LLVMCS0->getSuccessor(SuccIdx))); // Check setSuccessor(). auto *OrigSuccessor = CS0->getSuccessor(0); auto *NewSuccessor = BB0; EXPECT_NE(NewSuccessor, OrigSuccessor); CS0->setSuccessor(0, NewSuccessor); EXPECT_EQ(CS0->getSuccessor(0), NewSuccessor); CS0->setSuccessor(0, OrigSuccessor); EXPECT_EQ(CS0->getSuccessor(0), OrigSuccessor); // Check create(). CS1->eraseFromParent(); auto *NewCSI = sandboxir::CatchSwitchInst::create( CS0, Cleanup, 2, BB1->begin(), Ctx, "NewCSI"); EXPECT_TRUE(isa(NewCSI)); EXPECT_EQ(NewCSI->getParentPad(), CS0); } TEST_F(SandboxIRTest, ResumeInst) { parseIR(C, R"IR( define void @foo() { entry: invoke void @foo() to label %bb unwind label %unwind bb: ret void unwind: %lpad = landingpad { ptr, i32 } cleanup resume { ptr, i32 } %lpad } )IR"); Function &LLVMF = *M->getFunction("foo"); auto *LLVMUnwindBB = getBasicBlockByName(LLVMF, "unwind"); auto LLVMIt = LLVMUnwindBB->begin(); [[maybe_unused]] auto *LLVMLPad = cast(&*LLVMIt++); auto *LLVMResume = cast(&*LLVMIt++); sandboxir::Context Ctx(C); [[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF); auto *UnwindBB = cast(Ctx.getValue(LLVMUnwindBB)); auto It = UnwindBB->begin(); auto *LPad = cast(&*It++); auto *Resume = cast(&*It++); // Check getValue(). EXPECT_EQ(Resume->getValue(), LPad); EXPECT_EQ(Resume->getValue(), Ctx.getValue(LLVMResume->getValue())); // Check getNumSuccessors(). EXPECT_EQ(Resume->getNumSuccessors(), LLVMResume->getNumSuccessors()); // Check create(). auto *NewResume = sandboxir::ResumeInst::create(LPad, UnwindBB->end(), Ctx); EXPECT_EQ(NewResume->getValue(), LPad); EXPECT_EQ(NewResume->getParent(), UnwindBB); EXPECT_EQ(NewResume->getNextNode(), nullptr); } TEST_F(SandboxIRTest, SwitchInst) { parseIR(C, R"IR( define void @foo(i32 %cond0, i32 %cond1) { entry: switch i32 %cond0, label %default [ i32 0, label %bb0 i32 1, label %bb1 ] bb0: ret void bb1: ret void default: ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); auto *LLVMEntry = getBasicBlockByName(LLVMF, "entry"); auto *LLVMSwitch = cast(&*LLVMEntry->begin()); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *Cond1 = F.getArg(1); auto *Entry = cast(Ctx.getValue(LLVMEntry)); auto *Switch = cast(&*Entry->begin()); auto *BB0 = cast( Ctx.getValue(getBasicBlockByName(LLVMF, "bb0"))); auto *BB1 = cast( Ctx.getValue(getBasicBlockByName(LLVMF, "bb1"))); auto *Default = cast( Ctx.getValue(getBasicBlockByName(LLVMF, "default"))); // Check getCondition(). EXPECT_EQ(Switch->getCondition(), Ctx.getValue(LLVMSwitch->getCondition())); // Check setCondition(). auto *OrigCond = Switch->getCondition(); auto *NewCond = Cond1; EXPECT_NE(NewCond, OrigCond); Switch->setCondition(NewCond); EXPECT_EQ(Switch->getCondition(), NewCond); Switch->setCondition(OrigCond); EXPECT_EQ(Switch->getCondition(), OrigCond); // Check getDefaultDest(). EXPECT_EQ(Switch->getDefaultDest(), Ctx.getValue(LLVMSwitch->getDefaultDest())); EXPECT_EQ(Switch->getDefaultDest(), Default); // Check defaultDestUndefined(). EXPECT_EQ(Switch->defaultDestUndefined(), LLVMSwitch->defaultDestUndefined()); // Check setDefaultDest(). auto *OrigDefaultDest = Switch->getDefaultDest(); auto *NewDefaultDest = Entry; EXPECT_NE(NewDefaultDest, OrigDefaultDest); Switch->setDefaultDest(NewDefaultDest); EXPECT_EQ(Switch->getDefaultDest(), NewDefaultDest); Switch->setDefaultDest(OrigDefaultDest); EXPECT_EQ(Switch->getDefaultDest(), OrigDefaultDest); // Check getNumCases(). EXPECT_EQ(Switch->getNumCases(), LLVMSwitch->getNumCases()); // Check getNumSuccessors(). EXPECT_EQ(Switch->getNumSuccessors(), LLVMSwitch->getNumSuccessors()); // Check getSuccessor(). for (auto SuccIdx : seq(0, Switch->getNumSuccessors())) EXPECT_EQ(Switch->getSuccessor(SuccIdx), Ctx.getValue(LLVMSwitch->getSuccessor(SuccIdx))); // Check setSuccessor(). auto *OrigSucc = Switch->getSuccessor(0); auto *NewSucc = Entry; EXPECT_NE(NewSucc, OrigSucc); Switch->setSuccessor(0, NewSucc); EXPECT_EQ(Switch->getSuccessor(0), NewSucc); Switch->setSuccessor(0, OrigSucc); EXPECT_EQ(Switch->getSuccessor(0), OrigSucc); // Check case_begin(), case_end(), CaseIt. auto *Zero = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 0); auto *One = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 1); auto CaseIt = Switch->case_begin(); { sandboxir::SwitchInst::CaseHandle Case = *CaseIt++; EXPECT_EQ(Case.getCaseValue(), Zero); EXPECT_EQ(Case.getCaseSuccessor(), BB0); EXPECT_EQ(Case.getCaseIndex(), 0u); EXPECT_EQ(Case.getSuccessorIndex(), 1u); } { sandboxir::SwitchInst::CaseHandle Case = *CaseIt++; EXPECT_EQ(Case.getCaseValue(), One); EXPECT_EQ(Case.getCaseSuccessor(), BB1); EXPECT_EQ(Case.getCaseIndex(), 1u); EXPECT_EQ(Case.getSuccessorIndex(), 2u); } EXPECT_EQ(CaseIt, Switch->case_end()); // Check cases(). unsigned CntCase = 0; for (auto &Case : Switch->cases()) { EXPECT_EQ(Case.getCaseIndex(), CntCase); ++CntCase; } EXPECT_EQ(CntCase, 2u); // Check case_default(). auto CaseDefault = *Switch->case_default(); EXPECT_EQ(CaseDefault.getCaseSuccessor(), Default); EXPECT_EQ(CaseDefault.getCaseIndex(), sandboxir::SwitchInst::DefaultPseudoIndex); // Check findCaseValue(). EXPECT_EQ(Switch->findCaseValue(Zero)->getCaseIndex(), 0u); EXPECT_EQ(Switch->findCaseValue(One)->getCaseIndex(), 1u); // Check findCaseDest(). EXPECT_EQ(Switch->findCaseDest(BB0), Zero); EXPECT_EQ(Switch->findCaseDest(BB1), One); EXPECT_EQ(Switch->findCaseDest(Entry), nullptr); // Check addCase(). auto *Two = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 2); Switch->addCase(Two, Entry); auto CaseTwoIt = Switch->findCaseValue(Two); auto CaseTwo = *CaseTwoIt; EXPECT_EQ(CaseTwo.getCaseValue(), Two); EXPECT_EQ(CaseTwo.getCaseSuccessor(), Entry); EXPECT_EQ(Switch->getNumCases(), 3u); // Check removeCase(). auto RemovedIt = Switch->removeCase(CaseTwoIt); EXPECT_EQ(RemovedIt, Switch->case_end()); EXPECT_EQ(Switch->getNumCases(), 2u); // Check create(). auto NewSwitch = sandboxir::SwitchInst::create( Cond1, Default, 1, Default->begin(), Ctx, "NewSwitch"); EXPECT_TRUE(isa(NewSwitch)); EXPECT_EQ(NewSwitch->getCondition(), Cond1); EXPECT_EQ(NewSwitch->getDefaultDest(), Default); } TEST_F(SandboxIRTest, UnaryOperator) { parseIR(C, R"IR( define void @foo(float %arg0) { %fneg = fneg float %arg0 %copyfrom = fadd reassoc float %arg0, 42.0 ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *Arg0 = F.getArg(0); auto *BB = &*F.begin(); auto It = BB->begin(); auto *I = cast(&*It++); auto *CopyFrom = cast(&*It++); auto *Ret = &*It++; EXPECT_EQ(I->getOpcode(), sandboxir::Instruction::Opcode::FNeg); EXPECT_EQ(I->getOperand(0), Arg0); { // Check create() WhereIt, WhereBB. auto *NewI = cast(sandboxir::UnaryOperator::create( sandboxir::Instruction::Opcode::FNeg, Arg0, Ret->getIterator(), Ctx, "New1")); EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::FNeg); EXPECT_EQ(NewI->getOperand(0), Arg0); #ifndef NDEBUG EXPECT_EQ(NewI->getName(), "New1"); #endif // NDEBUG EXPECT_EQ(NewI->getNextNode(), Ret); } { // Check create() InsertBefore. auto *NewI = cast(sandboxir::UnaryOperator::create( sandboxir::Instruction::Opcode::FNeg, Arg0, Ret->getIterator(), Ctx, "New2")); EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::FNeg); EXPECT_EQ(NewI->getOperand(0), Arg0); #ifndef NDEBUG EXPECT_EQ(NewI->getName(), "New2"); #endif // NDEBUG EXPECT_EQ(NewI->getNextNode(), Ret); } { // Check create() InsertAtEnd. auto *NewI = cast(sandboxir::UnaryOperator::create( sandboxir::Instruction::Opcode::FNeg, Arg0, BB, Ctx, "New3")); EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::FNeg); EXPECT_EQ(NewI->getOperand(0), Arg0); #ifndef NDEBUG EXPECT_EQ(NewI->getName(), "New3"); #endif // NDEBUG EXPECT_EQ(NewI->getParent(), BB); EXPECT_EQ(NewI->getNextNode(), nullptr); } { // Check create() when it gets folded. auto *FortyTwo = CopyFrom->getOperand(1); auto *NewV = sandboxir::UnaryOperator::create( sandboxir::Instruction::Opcode::FNeg, FortyTwo, Ret->getIterator(), Ctx, "Folded"); EXPECT_TRUE(isa(NewV)); } { // Check createWithCopiedFlags() WhereIt, WhereBB. auto *NewI = cast( sandboxir::UnaryOperator::createWithCopiedFlags( sandboxir::Instruction::Opcode::FNeg, Arg0, CopyFrom, Ret->getIterator(), Ctx, "NewCopyFrom1")); EXPECT_EQ(NewI->hasAllowReassoc(), CopyFrom->hasAllowReassoc()); EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::FNeg); EXPECT_EQ(NewI->getOperand(0), Arg0); #ifndef NDEBUG EXPECT_EQ(NewI->getName(), "NewCopyFrom1"); #endif // NDEBUG EXPECT_EQ(NewI->getNextNode(), Ret); } { // Check createWithCopiedFlags() InsertBefore, auto *NewI = cast( sandboxir::UnaryOperator::createWithCopiedFlags( sandboxir::Instruction::Opcode::FNeg, Arg0, CopyFrom, Ret->getIterator(), Ctx, "NewCopyFrom2")); EXPECT_EQ(NewI->hasAllowReassoc(), CopyFrom->hasAllowReassoc()); EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::FNeg); EXPECT_EQ(NewI->getOperand(0), Arg0); #ifndef NDEBUG EXPECT_EQ(NewI->getName(), "NewCopyFrom2"); #endif // NDEBUG EXPECT_EQ(NewI->getNextNode(), Ret); } { // Check createWithCopiedFlags() InsertAtEnd, auto *NewI = cast( sandboxir::UnaryOperator::createWithCopiedFlags( sandboxir::Instruction::Opcode::FNeg, Arg0, CopyFrom, BB, Ctx, "NewCopyFrom3")); EXPECT_EQ(NewI->hasAllowReassoc(), CopyFrom->hasAllowReassoc()); EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::FNeg); EXPECT_EQ(NewI->getOperand(0), Arg0); #ifndef NDEBUG EXPECT_EQ(NewI->getName(), "NewCopyFrom3"); #endif // NDEBUG EXPECT_EQ(NewI->getParent(), BB); EXPECT_EQ(NewI->getNextNode(), nullptr); } { // Check createWithCopiedFlags() when it gets folded. auto *FortyTwo = CopyFrom->getOperand(1); auto *NewV = sandboxir::UnaryOperator::createWithCopiedFlags( sandboxir::Instruction::Opcode::FNeg, FortyTwo, CopyFrom, BB, Ctx, "Folded"); EXPECT_TRUE(isa(NewV)); } } TEST_F(SandboxIRTest, BinaryOperator) { parseIR(C, R"IR( define void @foo(i8 %arg0, i8 %arg1, float %farg0, float %farg1) { %add = add i8 %arg0, %arg1 %fadd = fadd float %farg0, %farg1 %sub = sub i8 %arg0, %arg1 %fsub = fsub float %farg0, %farg1 %mul = mul i8 %arg0, %arg1 %fmul = fmul float %farg0, %farg1 %udiv = udiv i8 %arg0, %arg1 %sdiv = sdiv i8 %arg0, %arg1 %fdiv = fdiv float %farg0, %farg1 %urem = urem i8 %arg0, %arg1 %srem = srem i8 %arg0, %arg1 %frem = frem float %farg0, %farg1 %shl = shl i8 %arg0, %arg1 %lshr = lshr i8 %arg0, %arg1 %ashr = ashr i8 %arg0, %arg1 %and = and i8 %arg0, %arg1 %or = or i8 %arg0, %arg1 %xor = xor i8 %arg0, %arg1 %copyfrom = add nsw i8 %arg0, %arg1 ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *Arg0 = F.getArg(0); auto *Arg1 = F.getArg(1); auto *FArg0 = F.getArg(2); auto *FArg1 = F.getArg(3); auto *BB = &*F.begin(); auto It = BB->begin(); #define CHECK_IBINOP(OPCODE) \ { \ auto *I = cast(&*It++); \ EXPECT_EQ(I->getOpcode(), OPCODE); \ EXPECT_EQ(I->getOperand(0), Arg0); \ EXPECT_EQ(I->getOperand(1), Arg1); \ } #define CHECK_FBINOP(OPCODE) \ { \ auto *I = cast(&*It++); \ EXPECT_EQ(I->getOpcode(), OPCODE); \ EXPECT_EQ(I->getOperand(0), FArg0); \ EXPECT_EQ(I->getOperand(1), FArg1); \ } CHECK_IBINOP(sandboxir::Instruction::Opcode::Add); CHECK_FBINOP(sandboxir::Instruction::Opcode::FAdd); CHECK_IBINOP(sandboxir::Instruction::Opcode::Sub); CHECK_FBINOP(sandboxir::Instruction::Opcode::FSub); CHECK_IBINOP(sandboxir::Instruction::Opcode::Mul); CHECK_FBINOP(sandboxir::Instruction::Opcode::FMul); CHECK_IBINOP(sandboxir::Instruction::Opcode::UDiv); CHECK_IBINOP(sandboxir::Instruction::Opcode::SDiv); CHECK_FBINOP(sandboxir::Instruction::Opcode::FDiv); CHECK_IBINOP(sandboxir::Instruction::Opcode::URem); CHECK_IBINOP(sandboxir::Instruction::Opcode::SRem); CHECK_FBINOP(sandboxir::Instruction::Opcode::FRem); CHECK_IBINOP(sandboxir::Instruction::Opcode::Shl); CHECK_IBINOP(sandboxir::Instruction::Opcode::LShr); CHECK_IBINOP(sandboxir::Instruction::Opcode::AShr); CHECK_IBINOP(sandboxir::Instruction::Opcode::And); CHECK_IBINOP(sandboxir::Instruction::Opcode::Or); CHECK_IBINOP(sandboxir::Instruction::Opcode::Xor); auto *CopyFrom = cast(&*It++); auto *Ret = cast(&*It++); { // Check create() WhereIt, WhereBB. auto *NewI = cast(sandboxir::BinaryOperator::create( sandboxir::Instruction::Opcode::Add, Arg0, Arg1, Ret->getIterator(), Ctx, "New1")); EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::Add); EXPECT_EQ(NewI->getOperand(0), Arg0); EXPECT_EQ(NewI->getOperand(1), Arg1); #ifndef NDEBUG EXPECT_EQ(NewI->getName(), "New1"); #endif // NDEBUG EXPECT_EQ(NewI->getNextNode(), Ret); } { // Check create() InsertBefore. auto *NewI = cast(sandboxir::BinaryOperator::create( sandboxir::Instruction::Opcode::Add, Arg0, Arg1, Ret->getIterator(), Ctx, "New2")); EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::Add); EXPECT_EQ(NewI->getOperand(0), Arg0); EXPECT_EQ(NewI->getOperand(1), Arg1); #ifndef NDEBUG EXPECT_EQ(NewI->getName(), "New2"); #endif // NDEBUG EXPECT_EQ(NewI->getNextNode(), Ret); } { // Check create() InsertAtEnd. auto *NewI = cast(sandboxir::BinaryOperator::create( sandboxir::Instruction::Opcode::Add, Arg0, Arg1, /*InsertAtEnd=*/BB, Ctx, "New3")); EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::Add); EXPECT_EQ(NewI->getOperand(0), Arg0); EXPECT_EQ(NewI->getOperand(1), Arg1); #ifndef NDEBUG EXPECT_EQ(NewI->getName(), "New3"); #endif // NDEBUG EXPECT_EQ(NewI->getNextNode(), nullptr); EXPECT_EQ(NewI->getParent(), BB); } { // Check create() when it gets folded. auto *FortyTwo = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 42); auto *NewV = sandboxir::BinaryOperator::create( sandboxir::Instruction::Opcode::Add, FortyTwo, FortyTwo, Ret->getIterator(), Ctx, "Folded"); EXPECT_TRUE(isa(NewV)); } { // Check createWithCopiedFlags() WhereIt, WhereBB. auto *NewI = cast( sandboxir::BinaryOperator::createWithCopiedFlags( sandboxir::Instruction::Opcode::Add, Arg0, Arg1, CopyFrom, Ret->getIterator(), Ctx, "NewNSW1")); EXPECT_EQ(NewI->hasNoSignedWrap(), CopyFrom->hasNoSignedWrap()); EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::Add); EXPECT_EQ(NewI->getOperand(0), Arg0); EXPECT_EQ(NewI->getOperand(1), Arg1); #ifndef NDEBUG EXPECT_EQ(NewI->getName(), "NewNSW1"); #endif // NDEBUG EXPECT_EQ(NewI->getNextNode(), Ret); } { // Check createWithCopiedFlags() InsertBefore. auto *NewI = cast( sandboxir::BinaryOperator::createWithCopiedFlags( sandboxir::Instruction::Opcode::Add, Arg0, Arg1, CopyFrom, Ret->getIterator(), Ctx, "NewNSW2")); EXPECT_EQ(NewI->hasNoSignedWrap(), CopyFrom->hasNoSignedWrap()); EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::Add); EXPECT_EQ(NewI->getOperand(0), Arg0); EXPECT_EQ(NewI->getOperand(1), Arg1); #ifndef NDEBUG EXPECT_EQ(NewI->getName(), "NewNSW2"); #endif // NDEBUG EXPECT_EQ(NewI->getNextNode(), Ret); } { // Check createWithCopiedFlags() InsertAtEnd. auto *NewI = cast( sandboxir::BinaryOperator::createWithCopiedFlags( sandboxir::Instruction::Opcode::Add, Arg0, Arg1, CopyFrom, BB, Ctx, "NewNSW3")); EXPECT_EQ(NewI->hasNoSignedWrap(), CopyFrom->hasNoSignedWrap()); EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::Add); EXPECT_EQ(NewI->getOperand(0), Arg0); EXPECT_EQ(NewI->getOperand(1), Arg1); #ifndef NDEBUG EXPECT_EQ(NewI->getName(), "NewNSW3"); #endif // NDEBUG EXPECT_EQ(NewI->getParent(), BB); EXPECT_EQ(NewI->getNextNode(), nullptr); } { // Check createWithCopiedFlags() when it gets folded. auto *FortyTwo = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 42); auto *NewV = sandboxir::BinaryOperator::createWithCopiedFlags( sandboxir::Instruction::Opcode::Add, FortyTwo, FortyTwo, CopyFrom, Ret->getIterator(), Ctx, "Folded"); EXPECT_TRUE(isa(NewV)); } } TEST_F(SandboxIRTest, PossiblyDisjointInst) { parseIR(C, R"IR( define void @foo(i8 %arg0, i8 %arg1) { %or = or i8 %arg0, %arg1 ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto *BB = &*F.begin(); auto It = BB->begin(); auto *PDI = cast(&*It++); // Check setIsDisjoint(), isDisjoint(). auto OrigIsDisjoint = PDI->isDisjoint(); auto NewIsDisjoint = true; EXPECT_NE(NewIsDisjoint, OrigIsDisjoint); PDI->setIsDisjoint(NewIsDisjoint); EXPECT_EQ(PDI->isDisjoint(), NewIsDisjoint); PDI->setIsDisjoint(OrigIsDisjoint); EXPECT_EQ(PDI->isDisjoint(), OrigIsDisjoint); } TEST_F(SandboxIRTest, AtomicRMWInst) { parseIR(C, R"IR( define void @foo(ptr %ptr, i8 %arg) { %atomicrmw = atomicrmw add ptr %ptr, i8 %arg acquire, align 128 ret void } )IR"); llvm::Function &LLVMF = *M->getFunction("foo"); llvm::BasicBlock *LLVMBB = &*LLVMF.begin(); auto LLVMIt = LLVMBB->begin(); auto *LLVMRMW = cast(&*LLVMIt++); sandboxir::Context Ctx(C); sandboxir::Function *F = Ctx.createFunction(&LLVMF); auto *Ptr = F->getArg(0); auto *Arg = F->getArg(1); auto *BB = &*F->begin(); auto It = BB->begin(); auto *RMW = cast(&*It++); auto *Ret = cast(&*It++); // Check getOperationName(). EXPECT_EQ( sandboxir::AtomicRMWInst::getOperationName( sandboxir::AtomicRMWInst::BinOp::Add), llvm::AtomicRMWInst::getOperationName(llvm::AtomicRMWInst::BinOp::Add)); // Check isFPOperation(). EXPECT_EQ( sandboxir::AtomicRMWInst::isFPOperation( sandboxir::AtomicRMWInst::BinOp::Add), llvm::AtomicRMWInst::isFPOperation(llvm::AtomicRMWInst::BinOp::Add)); EXPECT_FALSE(sandboxir::AtomicRMWInst::isFPOperation( sandboxir::AtomicRMWInst::BinOp::Add)); EXPECT_TRUE(sandboxir::AtomicRMWInst::isFPOperation( sandboxir::AtomicRMWInst::BinOp::FAdd)); // Check setOperation(), getOperation(). EXPECT_EQ(RMW->getOperation(), LLVMRMW->getOperation()); RMW->setOperation(sandboxir::AtomicRMWInst::BinOp::Sub); EXPECT_EQ(RMW->getOperation(), sandboxir::AtomicRMWInst::BinOp::Sub); RMW->setOperation(sandboxir::AtomicRMWInst::BinOp::Add); // Check getAlign(). EXPECT_EQ(RMW->getAlign(), LLVMRMW->getAlign()); auto OrigAlign = RMW->getAlign(); Align NewAlign(256); EXPECT_NE(NewAlign, OrigAlign); RMW->setAlignment(NewAlign); EXPECT_EQ(RMW->getAlign(), NewAlign); RMW->setAlignment(OrigAlign); EXPECT_EQ(RMW->getAlign(), OrigAlign); // Check isVolatile(), setVolatile(). EXPECT_EQ(RMW->isVolatile(), LLVMRMW->isVolatile()); bool OrigV = RMW->isVolatile(); bool NewV = true; EXPECT_NE(NewV, OrigV); RMW->setVolatile(NewV); EXPECT_EQ(RMW->isVolatile(), NewV); RMW->setVolatile(OrigV); EXPECT_EQ(RMW->isVolatile(), OrigV); // Check getOrdering(), setOrdering(). EXPECT_EQ(RMW->getOrdering(), LLVMRMW->getOrdering()); auto OldOrdering = RMW->getOrdering(); auto NewOrdering = AtomicOrdering::Monotonic; EXPECT_NE(NewOrdering, OldOrdering); RMW->setOrdering(NewOrdering); EXPECT_EQ(RMW->getOrdering(), NewOrdering); RMW->setOrdering(OldOrdering); EXPECT_EQ(RMW->getOrdering(), OldOrdering); // Check getSyncScopeID(), setSyncScopeID(). EXPECT_EQ(RMW->getSyncScopeID(), LLVMRMW->getSyncScopeID()); auto OrigSSID = RMW->getSyncScopeID(); SyncScope::ID NewSSID = SyncScope::SingleThread; EXPECT_NE(NewSSID, OrigSSID); RMW->setSyncScopeID(NewSSID); EXPECT_EQ(RMW->getSyncScopeID(), NewSSID); RMW->setSyncScopeID(OrigSSID); EXPECT_EQ(RMW->getSyncScopeID(), OrigSSID); // Check getPointerOperand(). EXPECT_EQ(RMW->getPointerOperand(), Ctx.getValue(LLVMRMW->getPointerOperand())); // Check getValOperand(). EXPECT_EQ(RMW->getValOperand(), Ctx.getValue(LLVMRMW->getValOperand())); // Check getPointerAddressSpace(). EXPECT_EQ(RMW->getPointerAddressSpace(), LLVMRMW->getPointerAddressSpace()); // Check isFloatingPointOperation(). EXPECT_EQ(RMW->isFloatingPointOperation(), LLVMRMW->isFloatingPointOperation()); Align Align(1024); auto Ordering = AtomicOrdering::Acquire; auto SSID = SyncScope::System; { // Check create() WhereIt, WhereBB. auto *NewI = cast(sandboxir::AtomicRMWInst::create( sandboxir::AtomicRMWInst::BinOp::Sub, Ptr, Arg, Align, Ordering, Ret->getIterator(), Ctx, SSID, "NewAtomicRMW1")); // Check getOpcode(). EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::AtomicRMW); // Check getAlign(). EXPECT_EQ(NewI->getAlign(), Align); // Check getSuccessOrdering(). EXPECT_EQ(NewI->getOrdering(), Ordering); // Check instr position. EXPECT_EQ(NewI->getNextNode(), Ret); // Check getPointerOperand(). EXPECT_EQ(NewI->getPointerOperand(), Ptr); // Check getValOperand(). EXPECT_EQ(NewI->getValOperand(), Arg); #ifndef NDEBUG // Check getName(). EXPECT_EQ(NewI->getName(), "NewAtomicRMW1"); #endif // NDEBUG } { // Check create() InsertBefore. auto *NewI = cast(sandboxir::AtomicRMWInst::create( sandboxir::AtomicRMWInst::BinOp::Sub, Ptr, Arg, Align, Ordering, Ret->getIterator(), Ctx, SSID, "NewAtomicRMW2")); // Check getOpcode(). EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::AtomicRMW); // Check getAlign(). EXPECT_EQ(NewI->getAlign(), Align); // Check getSuccessOrdering(). EXPECT_EQ(NewI->getOrdering(), Ordering); // Check instr position. EXPECT_EQ(NewI->getNextNode(), Ret); // Check getPointerOperand(). EXPECT_EQ(NewI->getPointerOperand(), Ptr); // Check getValOperand(). EXPECT_EQ(NewI->getValOperand(), Arg); #ifndef NDEBUG // Check getName(). EXPECT_EQ(NewI->getName(), "NewAtomicRMW2"); #endif // NDEBUG } { // Check create() InsertAtEnd. auto *NewI = cast(sandboxir::AtomicRMWInst::create( sandboxir::AtomicRMWInst::BinOp::Sub, Ptr, Arg, Align, Ordering, BB, Ctx, SSID, "NewAtomicRMW3")); // Check getOpcode(). EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::AtomicRMW); // Check getAlign(). EXPECT_EQ(NewI->getAlign(), Align); // Check getSuccessOrdering(). EXPECT_EQ(NewI->getOrdering(), Ordering); // Check instr position. EXPECT_EQ(NewI->getParent(), BB); EXPECT_EQ(NewI->getNextNode(), nullptr); // Check getPointerOperand(). EXPECT_EQ(NewI->getPointerOperand(), Ptr); // Check getValOperand(). EXPECT_EQ(NewI->getValOperand(), Arg); #ifndef NDEBUG // Check getName(). EXPECT_EQ(NewI->getName(), "NewAtomicRMW3"); #endif // NDEBUG } } TEST_F(SandboxIRTest, AtomicCmpXchgInst) { parseIR(C, R"IR( define void @foo(ptr %ptr, i8 %cmp, i8 %new) { %cmpxchg = cmpxchg ptr %ptr, i8 %cmp, i8 %new monotonic monotonic, align 128 ret void } )IR"); llvm::Function &LLVMF = *M->getFunction("foo"); llvm::BasicBlock *LLVMBB = &*LLVMF.begin(); auto LLVMIt = LLVMBB->begin(); auto *LLVMCmpXchg = cast(&*LLVMIt++); sandboxir::Context Ctx(C); sandboxir::Function *F = Ctx.createFunction(&LLVMF); auto *Ptr = F->getArg(0); auto *Cmp = F->getArg(1); auto *New = F->getArg(2); auto *BB = &*F->begin(); auto It = BB->begin(); auto *CmpXchg = cast(&*It++); auto *Ret = cast(&*It++); // Check getAlign(), setAlignment(). EXPECT_EQ(CmpXchg->getAlign(), LLVMCmpXchg->getAlign()); auto OrigAlign = CmpXchg->getAlign(); Align NewAlign(256); EXPECT_NE(NewAlign, OrigAlign); CmpXchg->setAlignment(NewAlign); EXPECT_EQ(CmpXchg->getAlign(), NewAlign); CmpXchg->setAlignment(OrigAlign); EXPECT_EQ(CmpXchg->getAlign(), OrigAlign); // Check isVolatile(), setVolatile(). EXPECT_EQ(CmpXchg->isVolatile(), LLVMCmpXchg->isVolatile()); bool OrigV = CmpXchg->isVolatile(); bool NewV = true; EXPECT_NE(NewV, OrigV); CmpXchg->setVolatile(NewV); EXPECT_EQ(CmpXchg->isVolatile(), NewV); CmpXchg->setVolatile(OrigV); EXPECT_EQ(CmpXchg->isVolatile(), OrigV); // Check isWeak(), setWeak(). EXPECT_EQ(CmpXchg->isWeak(), LLVMCmpXchg->isWeak()); bool OrigWeak = CmpXchg->isWeak(); bool NewWeak = true; EXPECT_NE(NewWeak, OrigWeak); CmpXchg->setWeak(NewWeak); EXPECT_EQ(CmpXchg->isWeak(), NewWeak); CmpXchg->setWeak(OrigWeak); EXPECT_EQ(CmpXchg->isWeak(), OrigWeak); // Check isValidSuccessOrdering(), isValidFailureOrdering(). SmallVector AllOrderings( {AtomicOrdering::NotAtomic, AtomicOrdering::Unordered, AtomicOrdering::Monotonic, AtomicOrdering::Acquire, AtomicOrdering::Release, AtomicOrdering::AcquireRelease, AtomicOrdering::SequentiallyConsistent}); for (auto Ordering : AllOrderings) { EXPECT_EQ(sandboxir::AtomicCmpXchgInst::isValidSuccessOrdering(Ordering), llvm::AtomicCmpXchgInst::isValidSuccessOrdering(Ordering)); EXPECT_EQ(sandboxir::AtomicCmpXchgInst::isValidFailureOrdering(Ordering), llvm::AtomicCmpXchgInst::isValidFailureOrdering(Ordering)); } // Check getSuccessOrdering(), setSuccessOrdering(). EXPECT_EQ(CmpXchg->getSuccessOrdering(), LLVMCmpXchg->getSuccessOrdering()); auto OldSuccOrdering = CmpXchg->getSuccessOrdering(); auto NewSuccOrdering = AtomicOrdering::Acquire; EXPECT_NE(NewSuccOrdering, OldSuccOrdering); CmpXchg->setSuccessOrdering(NewSuccOrdering); EXPECT_EQ(CmpXchg->getSuccessOrdering(), NewSuccOrdering); CmpXchg->setSuccessOrdering(OldSuccOrdering); EXPECT_EQ(CmpXchg->getSuccessOrdering(), OldSuccOrdering); // Check getFailureOrdering(), setFailureOrdering(). EXPECT_EQ(CmpXchg->getFailureOrdering(), LLVMCmpXchg->getFailureOrdering()); auto OldFailOrdering = CmpXchg->getFailureOrdering(); auto NewFailOrdering = AtomicOrdering::Acquire; EXPECT_NE(NewFailOrdering, OldFailOrdering); CmpXchg->setFailureOrdering(NewFailOrdering); EXPECT_EQ(CmpXchg->getFailureOrdering(), NewFailOrdering); CmpXchg->setFailureOrdering(OldFailOrdering); EXPECT_EQ(CmpXchg->getFailureOrdering(), OldFailOrdering); // Check getMergedOrdering(). EXPECT_EQ(CmpXchg->getMergedOrdering(), LLVMCmpXchg->getMergedOrdering()); // Check getSyncScopeID(), setSyncScopeID(). EXPECT_EQ(CmpXchg->getSyncScopeID(), LLVMCmpXchg->getSyncScopeID()); auto OrigSSID = CmpXchg->getSyncScopeID(); SyncScope::ID NewSSID = SyncScope::SingleThread; EXPECT_NE(NewSSID, OrigSSID); CmpXchg->setSyncScopeID(NewSSID); EXPECT_EQ(CmpXchg->getSyncScopeID(), NewSSID); CmpXchg->setSyncScopeID(OrigSSID); EXPECT_EQ(CmpXchg->getSyncScopeID(), OrigSSID); // Check getPointerOperand(). EXPECT_EQ(CmpXchg->getPointerOperand(), Ctx.getValue(LLVMCmpXchg->getPointerOperand())); // Check getCompareOperand(). EXPECT_EQ(CmpXchg->getCompareOperand(), Ctx.getValue(LLVMCmpXchg->getCompareOperand())); // Check getNewValOperand(). EXPECT_EQ(CmpXchg->getNewValOperand(), Ctx.getValue(LLVMCmpXchg->getNewValOperand())); // Check getPointerAddressSpace(). EXPECT_EQ(CmpXchg->getPointerAddressSpace(), LLVMCmpXchg->getPointerAddressSpace()); Align Align(1024); auto SuccOrdering = AtomicOrdering::Acquire; auto FailOrdering = AtomicOrdering::Monotonic; auto SSID = SyncScope::System; { // Check create() WhereIt, WhereBB. auto *NewI = cast(sandboxir::AtomicCmpXchgInst::create( Ptr, Cmp, New, Align, SuccOrdering, FailOrdering, Ret->getIterator(), Ctx, SSID, "NewAtomicCmpXchg1")); // Check getOpcode(). EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::AtomicCmpXchg); // Check getAlign(). EXPECT_EQ(NewI->getAlign(), Align); // Check getSuccessOrdering(). EXPECT_EQ(NewI->getSuccessOrdering(), SuccOrdering); // Check getFailureOrdering(). EXPECT_EQ(NewI->getFailureOrdering(), FailOrdering); // Check instr position. EXPECT_EQ(NewI->getNextNode(), Ret); // Check getPointerOperand(). EXPECT_EQ(NewI->getPointerOperand(), Ptr); // Check getCompareOperand(). EXPECT_EQ(NewI->getCompareOperand(), Cmp); // Check getNewValOperand(). EXPECT_EQ(NewI->getNewValOperand(), New); #ifndef NDEBUG // Check getName(). EXPECT_EQ(NewI->getName(), "NewAtomicCmpXchg1"); #endif // NDEBUG } { // Check create() InsertBefore. auto *NewI = cast(sandboxir::AtomicCmpXchgInst::create( Ptr, Cmp, New, Align, SuccOrdering, FailOrdering, Ret->getIterator(), Ctx, SSID, "NewAtomicCmpXchg2")); // Check getOpcode(). EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::AtomicCmpXchg); // Check getAlign(). EXPECT_EQ(NewI->getAlign(), Align); // Check getSuccessOrdering(). EXPECT_EQ(NewI->getSuccessOrdering(), SuccOrdering); // Check getFailureOrdering(). EXPECT_EQ(NewI->getFailureOrdering(), FailOrdering); // Check instr position. EXPECT_EQ(NewI->getNextNode(), Ret); // Check getPointerOperand(). EXPECT_EQ(NewI->getPointerOperand(), Ptr); // Check getCompareOperand(). EXPECT_EQ(NewI->getCompareOperand(), Cmp); // Check getNewValOperand(). EXPECT_EQ(NewI->getNewValOperand(), New); #ifndef NDEBUG // Check getName(). EXPECT_EQ(NewI->getName(), "NewAtomicCmpXchg2"); #endif // NDEBUG } { // Check create() InsertAtEnd. auto *NewI = cast(sandboxir::AtomicCmpXchgInst::create( Ptr, Cmp, New, Align, SuccOrdering, FailOrdering, BB, Ctx, SSID, "NewAtomicCmpXchg3")); // Check getOpcode(). EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::AtomicCmpXchg); // Check getAlign(). EXPECT_EQ(NewI->getAlign(), Align); // Check getSuccessOrdering(). EXPECT_EQ(NewI->getSuccessOrdering(), SuccOrdering); // Check getFailureOrdering(). EXPECT_EQ(NewI->getFailureOrdering(), FailOrdering); // Check instr position. EXPECT_EQ(NewI->getParent(), BB); EXPECT_EQ(NewI->getNextNode(), nullptr); // Check getPointerOperand(). EXPECT_EQ(NewI->getPointerOperand(), Ptr); // Check getCompareOperand(). EXPECT_EQ(NewI->getCompareOperand(), Cmp); // Check getNewValOperand(). EXPECT_EQ(NewI->getNewValOperand(), New); #ifndef NDEBUG // Check getName(). EXPECT_EQ(NewI->getName(), "NewAtomicCmpXchg3"); #endif // NDEBUG } } TEST_F(SandboxIRTest, AllocaInst) { parseIR(C, R"IR( define void @foo() { %allocaScalar = alloca i32, align 1024 %allocaArray = alloca i32, i32 42 ret void } )IR"); const DataLayout &DL = M->getDataLayout(); llvm::Function &LLVMF = *M->getFunction("foo"); llvm::BasicBlock *LLVMBB = &*LLVMF.begin(); auto LLVMIt = LLVMBB->begin(); auto *LLVMAllocaScalar = cast(&*LLVMIt++); auto *LLVMAllocaArray = cast(&*LLVMIt++); sandboxir::Context Ctx(C); sandboxir::Function *F = Ctx.createFunction(&LLVMF); auto *BB = &*F->begin(); auto It = BB->begin(); auto *AllocaScalar = cast(&*It++); auto *AllocaArray = cast(&*It++); auto *Ret = cast(&*It++); // Check isArrayAllocation(). EXPECT_EQ(AllocaScalar->isArrayAllocation(), LLVMAllocaScalar->isArrayAllocation()); EXPECT_EQ(AllocaArray->isArrayAllocation(), LLVMAllocaArray->isArrayAllocation()); // Check getArraySize(). EXPECT_EQ(AllocaScalar->getArraySize(), Ctx.getValue(LLVMAllocaScalar->getArraySize())); EXPECT_EQ(AllocaArray->getArraySize(), Ctx.getValue(LLVMAllocaArray->getArraySize())); // Check getType(). EXPECT_EQ(AllocaScalar->getType(), Ctx.getType(LLVMAllocaScalar->getType())); EXPECT_EQ(AllocaArray->getType(), Ctx.getType(LLVMAllocaArray->getType())); // Check getAddressSpace(). EXPECT_EQ(AllocaScalar->getAddressSpace(), LLVMAllocaScalar->getAddressSpace()); EXPECT_EQ(AllocaArray->getAddressSpace(), LLVMAllocaArray->getAddressSpace()); // Check getAllocationSize(). EXPECT_EQ(AllocaScalar->getAllocationSize(DL), LLVMAllocaScalar->getAllocationSize(DL)); EXPECT_EQ(AllocaArray->getAllocationSize(DL), LLVMAllocaArray->getAllocationSize(DL)); // Check getAllocationSizeInBits(). EXPECT_EQ(AllocaScalar->getAllocationSizeInBits(DL), LLVMAllocaScalar->getAllocationSizeInBits(DL)); EXPECT_EQ(AllocaArray->getAllocationSizeInBits(DL), LLVMAllocaArray->getAllocationSizeInBits(DL)); // Check getAllocatedType(). EXPECT_EQ(AllocaScalar->getAllocatedType(), Ctx.getType(LLVMAllocaScalar->getAllocatedType())); EXPECT_EQ(AllocaArray->getAllocatedType(), Ctx.getType(LLVMAllocaArray->getAllocatedType())); // Check setAllocatedType(). auto *OrigType = AllocaScalar->getAllocatedType(); auto *NewType = sandboxir::PointerType::get(Ctx, 0); EXPECT_NE(NewType, OrigType); AllocaScalar->setAllocatedType(NewType); EXPECT_EQ(AllocaScalar->getAllocatedType(), NewType); AllocaScalar->setAllocatedType(OrigType); EXPECT_EQ(AllocaScalar->getAllocatedType(), OrigType); // Check getAlign(). EXPECT_EQ(AllocaScalar->getAlign(), LLVMAllocaScalar->getAlign()); EXPECT_EQ(AllocaArray->getAlign(), LLVMAllocaArray->getAlign()); // Check setAlignment(). Align OrigAlign = AllocaScalar->getAlign(); Align NewAlign(16); EXPECT_NE(NewAlign, OrigAlign); AllocaScalar->setAlignment(NewAlign); EXPECT_EQ(AllocaScalar->getAlign(), NewAlign); AllocaScalar->setAlignment(OrigAlign); EXPECT_EQ(AllocaScalar->getAlign(), OrigAlign); // Check isStaticAlloca(). EXPECT_EQ(AllocaScalar->isStaticAlloca(), LLVMAllocaScalar->isStaticAlloca()); EXPECT_EQ(AllocaArray->isStaticAlloca(), LLVMAllocaArray->isStaticAlloca()); // Check isUsedWithInAlloca(), setUsedWithInAlloca(). EXPECT_EQ(AllocaScalar->isUsedWithInAlloca(), LLVMAllocaScalar->isUsedWithInAlloca()); bool OrigUsedWithInAlloca = AllocaScalar->isUsedWithInAlloca(); bool NewUsedWithInAlloca = true; EXPECT_NE(NewUsedWithInAlloca, OrigUsedWithInAlloca); AllocaScalar->setUsedWithInAlloca(NewUsedWithInAlloca); EXPECT_EQ(AllocaScalar->isUsedWithInAlloca(), NewUsedWithInAlloca); AllocaScalar->setUsedWithInAlloca(OrigUsedWithInAlloca); EXPECT_EQ(AllocaScalar->isUsedWithInAlloca(), OrigUsedWithInAlloca); auto *Ty = sandboxir::Type::getInt32Ty(Ctx); unsigned AddrSpace = 42; auto *PtrTy = sandboxir::PointerType::get(Ctx, AddrSpace); auto *ArraySize = sandboxir::ConstantInt::get(Ty, 43); { // Check create() WhereIt, WhereBB. auto *NewI = cast(sandboxir::AllocaInst::create( Ty, AddrSpace, Ret->getIterator(), Ctx, ArraySize, "NewAlloca1")); // Check getOpcode(). EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::Alloca); // Check getType(). EXPECT_EQ(NewI->getType(), PtrTy); // Check getArraySize(). EXPECT_EQ(NewI->getArraySize(), ArraySize); // Check getAddrSpace(). EXPECT_EQ(NewI->getAddressSpace(), AddrSpace); // Check instr position. EXPECT_EQ(NewI->getNextNode(), Ret); } { // Check create() InsertBefore. auto *NewI = cast(sandboxir::AllocaInst::create( Ty, AddrSpace, Ret->getIterator(), Ctx, ArraySize, "NewAlloca2")); // Check getOpcode(). EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::Alloca); // Check getType(). EXPECT_EQ(NewI->getType(), PtrTy); // Check getArraySize(). EXPECT_EQ(NewI->getArraySize(), ArraySize); // Check getAddrSpace(). EXPECT_EQ(NewI->getAddressSpace(), AddrSpace); // Check instr position. EXPECT_EQ(NewI->getNextNode(), Ret); } { // Check create() InsertAtEnd. auto *NewI = cast(sandboxir::AllocaInst::create( Ty, AddrSpace, BB, Ctx, ArraySize, "NewAlloca3")); // Check getOpcode(). EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::Alloca); // Check getType(). EXPECT_EQ(NewI->getType(), PtrTy); // Check getArraySize(). EXPECT_EQ(NewI->getArraySize(), ArraySize); // Check getAddrSpace(). EXPECT_EQ(NewI->getAddressSpace(), AddrSpace); // Check instr position. EXPECT_EQ(NewI->getParent(), BB); EXPECT_EQ(NewI->getNextNode(), nullptr); } } TEST_F(SandboxIRTest, CastInst) { parseIR(C, R"IR( define void @foo(i32 %arg, float %farg, double %darg, ptr %ptr) { %zext = zext i32 %arg to i64 %sext = sext i32 %arg to i64 %fptoui = fptoui float %farg to i32 %fptosi = fptosi float %farg to i32 %fpext = fpext float %farg to double %ptrtoint = ptrtoint ptr %ptr to i32 %inttoptr = inttoptr i32 %arg to ptr %sitofp = sitofp i32 %arg to float %uitofp = uitofp i32 %arg to float %trunc = trunc i32 %arg to i16 %fptrunc = fptrunc double %darg to float %bitcast = bitcast i32 %arg to float %addrspacecast = addrspacecast ptr %ptr to ptr addrspace(1) ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); sandboxir::Function *F = Ctx.createFunction(&LLVMF); unsigned ArgIdx = 0; auto *Arg = F->getArg(ArgIdx++); auto *BB = &*F->begin(); auto It = BB->begin(); auto *Ti64 = sandboxir::Type::getInt64Ty(Ctx); auto *Ti32 = sandboxir::Type::getInt32Ty(Ctx); auto *Ti16 = sandboxir::Type::getInt16Ty(Ctx); auto *Tdouble = sandboxir::Type::getDoubleTy(Ctx); auto *Tfloat = sandboxir::Type::getFloatTy(Ctx); auto *Tptr = sandboxir::PointerType::get(Ctx, 0); auto *Tptr1 = sandboxir::PointerType::get(Ctx, 1); // Check classof(), getOpcode(), getSrcTy(), getDstTy() auto *ZExt = cast(&*It++); auto *ZExtI = cast(ZExt); EXPECT_TRUE(isa(ZExtI)); EXPECT_TRUE(isa(ZExtI)); EXPECT_EQ(ZExt->getOpcode(), sandboxir::Instruction::Opcode::ZExt); EXPECT_EQ(ZExt->getSrcTy(), Ti32); EXPECT_EQ(ZExt->getDestTy(), Ti64); auto *SExt = cast(&*It++); auto *SExtI = cast(SExt); EXPECT_TRUE(isa(SExt)); EXPECT_TRUE(isa(SExtI)); EXPECT_EQ(SExt->getOpcode(), sandboxir::Instruction::Opcode::SExt); EXPECT_EQ(SExt->getSrcTy(), Ti32); EXPECT_EQ(SExt->getDestTy(), Ti64); auto *FPToUI = cast(&*It++); auto *FPToUII = cast(FPToUI); EXPECT_TRUE(isa(FPToUI)); EXPECT_TRUE(isa(FPToUII)); EXPECT_EQ(FPToUI->getOpcode(), sandboxir::Instruction::Opcode::FPToUI); EXPECT_EQ(FPToUI->getSrcTy(), Tfloat); EXPECT_EQ(FPToUI->getDestTy(), Ti32); auto *FPToSI = cast(&*It++); auto *FPToSII = cast(FPToSI); EXPECT_TRUE(isa(FPToSI)); EXPECT_TRUE(isa(FPToSII)); EXPECT_EQ(FPToSI->getOpcode(), sandboxir::Instruction::Opcode::FPToSI); EXPECT_EQ(FPToSI->getSrcTy(), Tfloat); EXPECT_EQ(FPToSI->getDestTy(), Ti32); auto *FPExt = cast(&*It++); auto *FPExtI = cast(FPExt); EXPECT_TRUE(isa(FPExt)); EXPECT_TRUE(isa(FPExtI)); EXPECT_EQ(FPExt->getOpcode(), sandboxir::Instruction::Opcode::FPExt); EXPECT_EQ(FPExt->getSrcTy(), Tfloat); EXPECT_EQ(FPExt->getDestTy(), Tdouble); auto *PtrToInt = cast(&*It++); auto *PtrToIntI = cast(PtrToInt); EXPECT_TRUE(isa(PtrToInt)); EXPECT_TRUE(isa(PtrToIntI)); EXPECT_EQ(PtrToInt->getOpcode(), sandboxir::Instruction::Opcode::PtrToInt); EXPECT_EQ(PtrToInt->getSrcTy(), Tptr); EXPECT_EQ(PtrToInt->getDestTy(), Ti32); auto *IntToPtr = cast(&*It++); auto *IntToPtrI = cast(IntToPtr); EXPECT_TRUE(isa(IntToPtr)); EXPECT_TRUE(isa(IntToPtrI)); EXPECT_EQ(IntToPtr->getOpcode(), sandboxir::Instruction::Opcode::IntToPtr); EXPECT_EQ(IntToPtr->getSrcTy(), Ti32); EXPECT_EQ(IntToPtr->getDestTy(), Tptr); auto *SIToFP = cast(&*It++); auto *SIToFPI = cast(SIToFP); EXPECT_TRUE(isa(SIToFP)); EXPECT_TRUE(isa(SIToFPI)); EXPECT_EQ(SIToFP->getOpcode(), sandboxir::Instruction::Opcode::SIToFP); EXPECT_EQ(SIToFP->getSrcTy(), Ti32); EXPECT_EQ(SIToFP->getDestTy(), Tfloat); auto *UIToFP = cast(&*It++); auto *UIToFPI = cast(UIToFP); EXPECT_TRUE(isa(UIToFP)); EXPECT_TRUE(isa(UIToFPI)); EXPECT_EQ(UIToFP->getOpcode(), sandboxir::Instruction::Opcode::UIToFP); EXPECT_EQ(UIToFP->getSrcTy(), Ti32); EXPECT_EQ(UIToFP->getDestTy(), Tfloat); auto *Trunc = cast(&*It++); auto *TruncI = cast(Trunc); EXPECT_TRUE(isa(Trunc)); EXPECT_TRUE(isa(TruncI)); EXPECT_EQ(Trunc->getOpcode(), sandboxir::Instruction::Opcode::Trunc); EXPECT_EQ(Trunc->getSrcTy(), Ti32); EXPECT_EQ(Trunc->getDestTy(), Ti16); auto *FPTrunc = cast(&*It++); auto *FPTruncI = cast(FPTrunc); EXPECT_TRUE(isa(FPTrunc)); EXPECT_TRUE(isa(FPTruncI)); EXPECT_EQ(FPTrunc->getOpcode(), sandboxir::Instruction::Opcode::FPTrunc); EXPECT_EQ(FPTrunc->getSrcTy(), Tdouble); EXPECT_EQ(FPTrunc->getDestTy(), Tfloat); auto *BitCast = cast(&*It++); auto *BitCastI = cast(BitCast); EXPECT_TRUE(isa(BitCast)); EXPECT_TRUE(isa(BitCastI)); EXPECT_EQ(BitCast->getOpcode(), sandboxir::Instruction::Opcode::BitCast); EXPECT_EQ(BitCast->getSrcTy(), Ti32); EXPECT_EQ(BitCast->getDestTy(), Tfloat); auto *AddrSpaceCast = cast(&*It++); auto *AddrSpaceCastI = cast(AddrSpaceCast); EXPECT_TRUE(isa(AddrSpaceCast)); EXPECT_TRUE(isa(AddrSpaceCastI)); EXPECT_EQ(AddrSpaceCast->getOpcode(), sandboxir::Instruction::Opcode::AddrSpaceCast); EXPECT_EQ(AddrSpaceCast->getSrcTy(), Tptr); EXPECT_EQ(AddrSpaceCast->getDestTy(), Tptr1); auto *Ret = cast(&*It++); { // Check create() WhereIt, WhereBB auto *NewI = cast( sandboxir::CastInst::create(Ti64, sandboxir::Instruction::Opcode::SExt, Arg, BB->end(), Ctx, "SExt")); // Check getOpcode(). EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::SExt); // Check getSrcTy(). EXPECT_EQ(NewI->getSrcTy(), Arg->getType()); // Check getDestTy(). EXPECT_EQ(NewI->getDestTy(), Ti64); // Check instr position. EXPECT_EQ(NewI->getNextNode(), nullptr); EXPECT_EQ(NewI->getPrevNode(), Ret); } { // Check create() InsertBefore. auto *NewI = cast( sandboxir::CastInst::create(Ti64, sandboxir::Instruction::Opcode::ZExt, Arg, Ret->getIterator(), Ctx, "ZExt")); // Check getOpcode(). EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::ZExt); // Check getSrcTy(). EXPECT_EQ(NewI->getSrcTy(), Arg->getType()); // Check getDestTy(). EXPECT_EQ(NewI->getDestTy(), Ti64); // Check instr position. EXPECT_EQ(NewI->getNextNode(), Ret); } { // Check create() InsertAtEnd. auto *NewI = cast(sandboxir::CastInst::create( Ti64, sandboxir::Instruction::Opcode::ZExt, Arg, BB, Ctx, "ZExt")); // Check getOpcode(). EXPECT_EQ(NewI->getOpcode(), sandboxir::Instruction::Opcode::ZExt); // Check getSrcTy(). EXPECT_EQ(NewI->getSrcTy(), Arg->getType()); // Check getDestTy(). EXPECT_EQ(NewI->getDestTy(), Ti64); // Check instr position. EXPECT_EQ(NewI->getNextNode(), nullptr); EXPECT_EQ(NewI->getParent(), BB); } { #ifndef NDEBUG // Check that passing a non-cast opcode crashes. EXPECT_DEATH( sandboxir::CastInst::create(Ti64, sandboxir::Instruction::Opcode::Store, Arg, Ret->getIterator(), Ctx, "Bad"), ".*Opcode.*"); #endif // NDEBUG } } TEST_F(SandboxIRTest, PossiblyNonNegInst) { parseIR(C, R"IR( define void @foo(i32 %arg, float %farg, double %darg, ptr %ptr) { %zext = zext i32 %arg to i64 %uitofp = uitofp i32 %arg to float %sext = sext i32 %arg to i64 %fptoui = fptoui float %farg to i32 %fptosi = fptosi float %farg to i32 %fpext = fpext float %farg to double %ptrtoint = ptrtoint ptr %ptr to i32 %inttoptr = inttoptr i32 %arg to ptr %sitofp = sitofp i32 %arg to float %trunc = trunc i32 %arg to i16 %fptrunc = fptrunc double %darg to float %bitcast = bitcast i32 %arg to float %addrspacecast = addrspacecast ptr %ptr to ptr addrspace(1) ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); sandboxir::Function *F = Ctx.createFunction(&LLVMF); auto *BB = &*F->begin(); auto It = BB->begin(); auto *PNNI0 = cast(&*It++); auto *PNNI1 = cast(&*It++); for (auto ItE = BB->end(); It != ItE; ++It) EXPECT_FALSE(isa(&*It++)); for (auto *PNNI : {PNNI0, PNNI1}) { // Check setNonNeg(), hasNonNeg(). auto OrigNonNeg = PNNI->hasNonNeg(); auto NewNonNeg = true; EXPECT_NE(NewNonNeg, OrigNonNeg); PNNI->setNonNeg(NewNonNeg); EXPECT_EQ(PNNI->hasNonNeg(), NewNonNeg); PNNI->setNonNeg(OrigNonNeg); EXPECT_EQ(PNNI->hasNonNeg(), OrigNonNeg); } } /// CastInst's subclasses are very similar so we can use a common test function /// for them. template void testCastInst(llvm::Module &M, llvm::Type *LLVMSrcTy, llvm::Type *LLVMDstTy) { Function &LLVMF = *M.getFunction("foo"); sandboxir::Context Ctx(M.getContext()); sandboxir::Function *F = Ctx.createFunction(&LLVMF); sandboxir::Type *SrcTy = Ctx.getType(LLVMSrcTy); sandboxir::Type *DstTy = Ctx.getType(LLVMDstTy); unsigned ArgIdx = 0; auto *Arg = F->getArg(ArgIdx++); auto *BB = &*F->begin(); auto It = BB->begin(); auto *CI = cast(&*It++); EXPECT_EQ(CI->getOpcode(), OpcodeT); EXPECT_EQ(CI->getSrcTy(), SrcTy); EXPECT_EQ(CI->getDestTy(), DstTy); auto *Ret = cast(&*It++); { // Check create() WhereIt, WhereBB auto *NewI = cast(SubclassT::create(Arg, DstTy, BB->end(), Ctx, "NewCI")); // Check getOpcode(). EXPECT_EQ(NewI->getOpcode(), OpcodeT); // Check getSrcTy(). EXPECT_EQ(NewI->getSrcTy(), Arg->getType()); // Check getDestTy(). EXPECT_EQ(NewI->getDestTy(), DstTy); // Check instr position. EXPECT_EQ(NewI->getNextNode(), nullptr); EXPECT_EQ(NewI->getPrevNode(), Ret); // Check instr name. EXPECT_EQ(NewI->getName(), "NewCI"); } { // Check create() InsertBefore. auto *NewI = cast( SubclassT::create(Arg, DstTy, Ret->getIterator(), Ctx, "NewCI")); // Check getOpcode(). EXPECT_EQ(NewI->getOpcode(), OpcodeT); // Check getSrcTy(). EXPECT_EQ(NewI->getSrcTy(), Arg->getType()); // Check getDestTy(). EXPECT_EQ(NewI->getDestTy(), DstTy); // Check instr position. EXPECT_EQ(NewI->getNextNode(), Ret); } { // Check create() InsertAtEnd. auto *NewI = cast(SubclassT::create(Arg, DstTy, /*InsertAtEnd=*/BB, Ctx, "NewCI")); // Check getOpcode(). EXPECT_EQ(NewI->getOpcode(), OpcodeT); // Check getSrcTy(). EXPECT_EQ(NewI->getSrcTy(), Arg->getType()); // Check getDestTy(). EXPECT_EQ(NewI->getDestTy(), DstTy); // Check instr position. EXPECT_EQ(NewI->getNextNode(), nullptr); EXPECT_EQ(NewI->getParent(), BB); } } TEST_F(SandboxIRTest, TruncInst) { parseIR(C, R"IR( define void @foo(i64 %arg) { %trunc = trunc i64 %arg to i32 ret void } )IR"); testCastInst( *M, /*SrcTy=*/Type::getInt64Ty(C), /*DstTy=*/Type::getInt32Ty(C)); } TEST_F(SandboxIRTest, ZExtInst) { parseIR(C, R"IR( define void @foo(i32 %arg) { %zext = zext i32 %arg to i64 ret void } )IR"); testCastInst( *M, /*SrcTy=*/Type::getInt32Ty(C), /*DstTy=*/Type::getInt64Ty(C)); } TEST_F(SandboxIRTest, SExtInst) { parseIR(C, R"IR( define void @foo(i32 %arg) { %sext = sext i32 %arg to i64 ret void } )IR"); testCastInst( *M, /*SrcTy=*/Type::getInt32Ty(C), /*DstTy=*/Type::getInt64Ty(C)); } TEST_F(SandboxIRTest, FPTruncInst) { parseIR(C, R"IR( define void @foo(double %arg) { %fptrunc = fptrunc double %arg to float ret void } )IR"); testCastInst( *M, /*SrcTy=*/Type::getDoubleTy(C), /*DstTy=*/Type::getFloatTy(C)); } TEST_F(SandboxIRTest, FPExtInst) { parseIR(C, R"IR( define void @foo(float %arg) { %fpext = fpext float %arg to double ret void } )IR"); testCastInst( *M, /*SrcTy=*/Type::getFloatTy(C), /*DstTy=*/Type::getDoubleTy(C)); } TEST_F(SandboxIRTest, UIToFPInst) { parseIR(C, R"IR( define void @foo(i32 %arg) { %uitofp = uitofp i32 %arg to float ret void } )IR"); testCastInst( *M, /*SrcTy=*/Type::getInt32Ty(C), /*DstTy=*/Type::getFloatTy(C)); } TEST_F(SandboxIRTest, SIToFPInst) { parseIR(C, R"IR( define void @foo(i32 %arg) { %sitofp = sitofp i32 %arg to float ret void } )IR"); testCastInst( *M, /*SrcTy=*/Type::getInt32Ty(C), /*DstTy=*/Type::getFloatTy(C)); } TEST_F(SandboxIRTest, FPToUIInst) { parseIR(C, R"IR( define void @foo(float %arg) { %fptoui = fptoui float %arg to i32 ret void } )IR"); testCastInst( *M, /*SrcTy=*/Type::getFloatTy(C), /*DstTy=*/Type::getInt32Ty(C)); } TEST_F(SandboxIRTest, FPToSIInst) { parseIR(C, R"IR( define void @foo(float %arg) { %fptosi = fptosi float %arg to i32 ret void } )IR"); testCastInst( *M, /*SrcTy=*/Type::getFloatTy(C), /*DstTy=*/Type::getInt32Ty(C)); } TEST_F(SandboxIRTest, IntToPtrInst) { parseIR(C, R"IR( define void @foo(i32 %arg) { %inttoptr = inttoptr i32 %arg to ptr ret void } )IR"); testCastInst( *M, /*SrcTy=*/Type::getInt32Ty(C), /*DstTy=*/PointerType::get(C, 0)); } TEST_F(SandboxIRTest, PtrToIntInst) { parseIR(C, R"IR( define void @foo(ptr %ptr) { %ptrtoint = ptrtoint ptr %ptr to i32 ret void } )IR"); testCastInst( *M, /*SrcTy=*/PointerType::get(C, 0), /*DstTy=*/Type::getInt32Ty(C)); } TEST_F(SandboxIRTest, BitCastInst) { parseIR(C, R"IR( define void @foo(i32 %arg) { %bitcast = bitcast i32 %arg to float ret void } )IR"); testCastInst( *M, /*SrcTy=*/Type::getInt32Ty(C), /*DstTy=*/Type::getFloatTy(C)); } TEST_F(SandboxIRTest, AddrSpaceCastInst) { parseIR(C, R"IR( define void @foo(ptr %ptr) { %addrspacecast = addrspacecast ptr %ptr to ptr addrspace(1) ret void } )IR"); Type *Tptr0 = PointerType::get(C, 0); Type *Tptr1 = PointerType::get(C, 1); testCastInst(*M, /*SrcTy=*/Tptr0, /*DstTy=*/Tptr1); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); sandboxir::Function *F = Ctx.createFunction(&LLVMF); unsigned ArgIdx = 0; auto *Arg = F->getArg(ArgIdx++); auto *BB = &*F->begin(); auto It = BB->begin(); auto *AddrSpaceCast = cast(&*It++); EXPECT_EQ(AddrSpaceCast->getOpcode(), sandboxir::Instruction::Opcode::AddrSpaceCast); EXPECT_EQ(AddrSpaceCast->getPointerOperand(), Arg); EXPECT_EQ(sandboxir::AddrSpaceCastInst::getPointerOperandIndex(), 0u); EXPECT_EQ(AddrSpaceCast->getSrcAddressSpace(), cast(Tptr0)->getPointerAddressSpace()); EXPECT_EQ(AddrSpaceCast->getDestAddressSpace(), cast(Tptr1)->getPointerAddressSpace()); } TEST_F(SandboxIRTest, PHINode) { parseIR(C, R"IR( define void @foo(i32 %arg) { bb1: br label %bb2 bb2: %phi = phi i32 [ %arg, %bb1 ], [ 0, %bb2 ], [ 1, %bb3 ], [ 2, %bb4 ], [ 3, %bb5 ] br label %bb2 bb3: br label %bb2 bb4: br label %bb2 bb5: br label %bb2 ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); auto *LLVMBB1 = getBasicBlockByName(LLVMF, "bb1"); auto *LLVMBB2 = getBasicBlockByName(LLVMF, "bb2"); auto *LLVMBB3 = getBasicBlockByName(LLVMF, "bb3"); auto LLVMIt = LLVMBB2->begin(); auto *LLVMPHI = cast(&*LLVMIt++); sandboxir::Context Ctx(C); sandboxir::Function *F = Ctx.createFunction(&LLVMF); auto *Arg = F->getArg(0); auto *BB1 = cast(Ctx.getValue(LLVMBB1)); auto *BB2 = cast(Ctx.getValue(LLVMBB2)); auto *BB3 = cast(Ctx.getValue(LLVMBB3)); auto It = BB2->begin(); // Check classof(). auto *PHI = cast(&*It++); auto *Br = cast(&*It++); // Check blocks(). EXPECT_EQ(range_size(PHI->blocks()), range_size(LLVMPHI->blocks())); auto BlockIt = PHI->block_begin(); for (llvm::BasicBlock *LLVMBB : LLVMPHI->blocks()) { sandboxir::BasicBlock *BB = *BlockIt++; EXPECT_EQ(BB, Ctx.getValue(LLVMBB)); } // Check incoming_values(). EXPECT_EQ(range_size(PHI->incoming_values()), range_size(LLVMPHI->incoming_values())); auto IncIt = PHI->incoming_values().begin(); for (llvm::Value *LLVMV : LLVMPHI->incoming_values()) { sandboxir::Value *IncV = *IncIt++; EXPECT_EQ(IncV, Ctx.getValue(LLVMV)); } // Check getNumIncomingValues(). EXPECT_EQ(PHI->getNumIncomingValues(), LLVMPHI->getNumIncomingValues()); // Check getIncomingValue(). EXPECT_EQ(PHI->getIncomingValue(0), Ctx.getValue(LLVMPHI->getIncomingValue(0))); EXPECT_EQ(PHI->getIncomingValue(1), Ctx.getValue(LLVMPHI->getIncomingValue(1))); // Check setIncomingValue(). auto *OrigV = PHI->getIncomingValue(0); PHI->setIncomingValue(0, PHI); EXPECT_EQ(PHI->getIncomingValue(0), PHI); PHI->setIncomingValue(0, OrigV); // Check getOperandNumForIncomingValue(). EXPECT_EQ(sandboxir::PHINode::getOperandNumForIncomingValue(0), llvm::PHINode::getOperandNumForIncomingValue(0)); // Check getIncomingValueNumForOperand(). EXPECT_EQ(sandboxir::PHINode::getIncomingValueNumForOperand(0), llvm::PHINode::getIncomingValueNumForOperand(0)); // Check getIncomingBlock(unsigned). EXPECT_EQ(PHI->getIncomingBlock(0), Ctx.getValue(LLVMPHI->getIncomingBlock(0))); // Check getIncomingBlock(Use). llvm::Use &LLVMUse = LLVMPHI->getOperandUse(0); sandboxir::Use Use = PHI->getOperandUse(0); EXPECT_EQ(PHI->getIncomingBlock(Use), Ctx.getValue(LLVMPHI->getIncomingBlock(LLVMUse))); // Check setIncomingBlock(). sandboxir::BasicBlock *OrigBB = PHI->getIncomingBlock(0); EXPECT_NE(OrigBB, BB2); PHI->setIncomingBlock(0, BB2); EXPECT_EQ(PHI->getIncomingBlock(0), BB2); PHI->setIncomingBlock(0, OrigBB); EXPECT_EQ(PHI->getIncomingBlock(0), OrigBB); // Check addIncoming(). unsigned OrigNumIncoming = PHI->getNumIncomingValues(); PHI->addIncoming(Arg, BB3); EXPECT_EQ(PHI->getNumIncomingValues(), LLVMPHI->getNumIncomingValues()); EXPECT_EQ(PHI->getNumIncomingValues(), OrigNumIncoming + 1); EXPECT_EQ(PHI->getIncomingValue(OrigNumIncoming), Arg); EXPECT_EQ(PHI->getIncomingBlock(OrigNumIncoming), BB3); // Check removeIncomingValue(unsigned). PHI->removeIncomingValue(OrigNumIncoming); EXPECT_EQ(PHI->getNumIncomingValues(), OrigNumIncoming); // Check removeIncomingValue(BasicBlock *). PHI->addIncoming(Arg, BB3); PHI->removeIncomingValue(BB3); EXPECT_EQ(PHI->getNumIncomingValues(), OrigNumIncoming); // Check getBasicBlockIndex(). EXPECT_EQ(PHI->getBasicBlockIndex(BB1), LLVMPHI->getBasicBlockIndex(LLVMBB1)); // Check getIncomingValueForBlock(). EXPECT_EQ(PHI->getIncomingValueForBlock(BB1), Ctx.getValue(LLVMPHI->getIncomingValueForBlock(LLVMBB1))); // Check hasConstantValue(). llvm::Value *ConstV = LLVMPHI->hasConstantValue(); EXPECT_EQ(PHI->hasConstantValue(), ConstV != nullptr ? Ctx.getValue(ConstV) : nullptr); // Check hasConstantOrUndefValue(). EXPECT_EQ(PHI->hasConstantOrUndefValue(), LLVMPHI->hasConstantOrUndefValue()); // Check isComplete(). EXPECT_EQ(PHI->isComplete(), LLVMPHI->isComplete()); // Check replaceIncomingValueIf EXPECT_EQ(PHI->getNumIncomingValues(), 5u); auto *RemainBB0 = PHI->getIncomingBlock(0); auto *RemoveBB0 = PHI->getIncomingBlock(1); auto *RemainBB1 = PHI->getIncomingBlock(2); auto *RemoveBB1 = PHI->getIncomingBlock(3); auto *RemainBB2 = PHI->getIncomingBlock(4); PHI->removeIncomingValueIf([&](unsigned Idx) { return PHI->getIncomingBlock(Idx) == RemoveBB0 || PHI->getIncomingBlock(Idx) == RemoveBB1; }); EXPECT_EQ(PHI->getNumIncomingValues(), 3u); EXPECT_EQ(PHI->getIncomingBlock(0), RemainBB0); EXPECT_EQ(PHI->getIncomingBlock(1), RemainBB1); EXPECT_EQ(PHI->getIncomingBlock(2), RemainBB2); // Check replaceIncomingBlockWith OrigBB = RemainBB0; auto *NewBB = RemainBB1; EXPECT_NE(NewBB, OrigBB); PHI->replaceIncomingBlockWith(OrigBB, NewBB); EXPECT_EQ(PHI->getIncomingBlock(0), NewBB); EXPECT_EQ(PHI->getIncomingBlock(1), RemainBB1); EXPECT_EQ(PHI->getIncomingBlock(2), RemainBB2); // Check create(). auto *NewPHI = cast(sandboxir::PHINode::create( PHI->getType(), 0, Br->getIterator(), Ctx, "NewPHI")); EXPECT_EQ(NewPHI->getType(), PHI->getType()); EXPECT_EQ(NewPHI->getNextNode(), Br); EXPECT_EQ(NewPHI->getName(), "NewPHI"); EXPECT_EQ(NewPHI->getNumIncomingValues(), 0u); for (auto [Idx, V] : enumerate(PHI->incoming_values())) { sandboxir::BasicBlock *IncBB = PHI->getIncomingBlock(Idx); NewPHI->addIncoming(V, IncBB); } EXPECT_EQ(NewPHI->getNumIncomingValues(), PHI->getNumIncomingValues()); } static void checkSwapOperands(sandboxir::Context &Ctx, llvm::sandboxir::CmpInst *Cmp, llvm::CmpInst *LLVMCmp) { auto OrigOp0 = Cmp->getOperand(0); auto OrigOp1 = Cmp->getOperand(1); EXPECT_EQ(Ctx.getValue(LLVMCmp->getOperand(0)), OrigOp0); EXPECT_EQ(Ctx.getValue(LLVMCmp->getOperand(1)), OrigOp1); // This checks the dispatch mechanism in CmpInst, as well as // the specific implementations. Cmp->swapOperands(); EXPECT_EQ(Ctx.getValue(LLVMCmp->getOperand(1)), OrigOp0); EXPECT_EQ(Ctx.getValue(LLVMCmp->getOperand(0)), OrigOp1); EXPECT_EQ(Cmp->getOperand(0), OrigOp1); EXPECT_EQ(Cmp->getOperand(1), OrigOp0); // Undo it to keep the rest of the test consistent Cmp->swapOperands(); } static void checkCommonPredicates(sandboxir::CmpInst *Cmp, llvm::CmpInst *LLVMCmp) { // Check proper creation auto Pred = Cmp->getPredicate(); auto LLVMPred = LLVMCmp->getPredicate(); EXPECT_EQ(Pred, LLVMPred); // Check setPredicate Cmp->setPredicate(llvm::CmpInst::FCMP_FALSE); EXPECT_EQ(Cmp->getPredicate(), llvm::CmpInst::FCMP_FALSE); EXPECT_EQ(LLVMCmp->getPredicate(), llvm::CmpInst::FCMP_FALSE); Cmp->setPredicate(Pred); EXPECT_EQ(LLVMCmp->getPredicate(), Pred); // Ensure the accessors properly forward to the underlying implementation EXPECT_STREQ(sandboxir::CmpInst::getPredicateName(Pred).data(), llvm::CmpInst::getPredicateName(LLVMPred).data()); EXPECT_EQ(Cmp->isFPPredicate(), LLVMCmp->isFPPredicate()); EXPECT_EQ(Cmp->isIntPredicate(), LLVMCmp->isIntPredicate()); EXPECT_EQ(Cmp->getInversePredicate(), LLVMCmp->getInversePredicate()); EXPECT_EQ(Cmp->getOrderedPredicate(), LLVMCmp->getOrderedPredicate()); EXPECT_EQ(Cmp->getUnorderedPredicate(), LLVMCmp->getUnorderedPredicate()); EXPECT_EQ(Cmp->getSwappedPredicate(), LLVMCmp->getSwappedPredicate()); EXPECT_EQ(Cmp->isStrictPredicate(), LLVMCmp->isStrictPredicate()); EXPECT_EQ(Cmp->isNonStrictPredicate(), LLVMCmp->isNonStrictPredicate()); EXPECT_EQ(Cmp->isRelational(), LLVMCmp->isRelational()); if (Cmp->isRelational()) { EXPECT_EQ(Cmp->getFlippedStrictnessPredicate(), LLVMCmp->getFlippedStrictnessPredicate()); } EXPECT_EQ(Cmp->isCommutative(), LLVMCmp->isCommutative()); EXPECT_EQ(Cmp->isTrueWhenEqual(), LLVMCmp->isTrueWhenEqual()); EXPECT_EQ(Cmp->isFalseWhenEqual(), LLVMCmp->isFalseWhenEqual()); EXPECT_EQ(sandboxir::CmpInst::isOrdered(Pred), llvm::CmpInst::isOrdered(LLVMPred)); EXPECT_EQ(sandboxir::CmpInst::isUnordered(Pred), llvm::CmpInst::isUnordered(LLVMPred)); } TEST_F(SandboxIRTest, ICmpInst) { SCOPED_TRACE("SandboxIRTest sandboxir::ICmpInst tests"); parseIR(C, R"IR( define void @foo(i32 %i0, i32 %i1) { bb: %ine = icmp ne i32 %i0, %i1 %iugt = icmp ugt i32 %i0, %i1 %iuge = icmp uge i32 %i0, %i1 %iult = icmp ult i32 %i0, %i1 %iule = icmp ule i32 %i0, %i1 %isgt = icmp sgt i32 %i0, %i1 %isle = icmp sle i32 %i0, %i1 %ieg = icmp eq i32 %i0, %i1 ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); [[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF); auto *LLVMBB = getBasicBlockByName(LLVMF, "bb"); auto LLVMIt = LLVMBB->begin(); auto *BB = cast(Ctx.getValue(LLVMBB)); auto It = BB->begin(); // Check classof() while (auto *ICmp = dyn_cast(&*It++)) { auto *LLVMICmp = cast(&*LLVMIt++); checkSwapOperands(Ctx, ICmp, LLVMICmp); checkCommonPredicates(ICmp, LLVMICmp); EXPECT_EQ(ICmp->isSigned(), LLVMICmp->isSigned()); EXPECT_EQ(ICmp->isUnsigned(), LLVMICmp->isUnsigned()); EXPECT_EQ(ICmp->getSignedPredicate(), LLVMICmp->getSignedPredicate()); EXPECT_EQ(ICmp->getUnsignedPredicate(), LLVMICmp->getUnsignedPredicate()); } auto *NewCmp = cast( sandboxir::CmpInst::create(llvm::CmpInst::ICMP_ULE, F.getArg(0), F.getArg(1), BB->begin(), Ctx, "NewCmp")); EXPECT_EQ(NewCmp, &*BB->begin()); EXPECT_EQ(NewCmp->getPredicate(), llvm::CmpInst::ICMP_ULE); EXPECT_EQ(NewCmp->getOperand(0), F.getArg(0)); EXPECT_EQ(NewCmp->getOperand(1), F.getArg(1)); #ifndef NDEBUG EXPECT_EQ(NewCmp->getName(), "NewCmp"); #endif // NDEBUG // TODO: Improve this test when sandboxir::VectorType is more completely // implemented. sandboxir::Type *RT = sandboxir::CmpInst::makeCmpResultType(F.getArg(0)->getType()); EXPECT_TRUE(RT->isIntegerTy(1)); // Only one bit in a single comparison { // Check create() when operands are constant. auto *Const42 = sandboxir::ConstantInt::get(sandboxir::Type::getInt32Ty(Ctx), 42); auto *NewConstCmp = sandboxir::CmpInst::create(llvm::CmpInst::ICMP_ULE, Const42, Const42, BB->begin(), Ctx, "NewConstCmp"); EXPECT_TRUE(isa(NewConstCmp)); } } TEST_F(SandboxIRTest, FCmpInst) { SCOPED_TRACE("SandboxIRTest sandboxir::FCmpInst tests"); parseIR(C, R"IR( define void @foo(float %f0, float %f1) { bb: %ffalse = fcmp false float %f0, %f1 %foeq = fcmp oeq float %f0, %f1 %fogt = fcmp ogt float %f0, %f1 %folt = fcmp olt float %f0, %f1 %fole = fcmp ole float %f0, %f1 %fone = fcmp one float %f0, %f1 %ford = fcmp ord float %f0, %f1 %funo = fcmp uno float %f0, %f1 %fueq = fcmp ueq float %f0, %f1 %fugt = fcmp ugt float %f0, %f1 %fuge = fcmp uge float %f0, %f1 %fult = fcmp ult float %f0, %f1 %fule = fcmp ule float %f0, %f1 %fune = fcmp une float %f0, %f1 %ftrue = fcmp true float %f0, %f1 ret void bb1: %copyfrom = fadd reassoc float %f0, 42.0 ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); [[maybe_unused]] auto &F = *Ctx.createFunction(&LLVMF); auto *LLVMBB = getBasicBlockByName(LLVMF, "bb"); auto LLVMIt = LLVMBB->begin(); auto *BB = cast(Ctx.getValue(LLVMBB)); auto It = BB->begin(); // Check classof() while (auto *FCmp = dyn_cast(&*It++)) { auto *LLVMFCmp = cast(&*LLVMIt++); checkSwapOperands(Ctx, FCmp, LLVMFCmp); checkCommonPredicates(FCmp, LLVMFCmp); } auto *LLVMBB1 = getBasicBlockByName(LLVMF, "bb1"); auto *BB1 = cast(Ctx.getValue(LLVMBB1)); auto It1 = BB1->begin(); auto *CopyFrom = &*It1++; CopyFrom->setFastMathFlags(FastMathFlags::getFast()); // create with default flags auto *NewFCmp = cast(sandboxir::CmpInst::create( llvm::CmpInst::FCMP_ONE, F.getArg(0), F.getArg(1), It1, Ctx, "NewFCmp")); EXPECT_EQ(NewFCmp->getPredicate(), llvm::CmpInst::FCMP_ONE); EXPECT_EQ(NewFCmp->getOperand(0), F.getArg(0)); EXPECT_EQ(NewFCmp->getOperand(1), F.getArg(1)); #ifndef NDEBUG EXPECT_EQ(NewFCmp->getName(), "NewFCmp"); #endif // NDEBUG FastMathFlags DefaultFMF = NewFCmp->getFastMathFlags(); EXPECT_TRUE(CopyFrom->getFastMathFlags() != DefaultFMF); // create with copied flags auto *NewFCmpFlags = cast(sandboxir::CmpInst::createWithCopiedFlags( llvm::CmpInst::FCMP_ONE, F.getArg(0), F.getArg(1), CopyFrom, It1, Ctx, "NewFCmpFlags")); EXPECT_FALSE(NewFCmpFlags->getFastMathFlags() != CopyFrom->getFastMathFlags()); EXPECT_EQ(NewFCmpFlags->getPredicate(), llvm::CmpInst::FCMP_ONE); EXPECT_EQ(NewFCmpFlags->getOperand(0), F.getArg(0)); EXPECT_EQ(NewFCmpFlags->getOperand(1), F.getArg(1)); #ifndef NDEBUG EXPECT_EQ(NewFCmpFlags->getName(), "NewFCmpFlags"); #endif // NDEBUG { // Check create() when operands are constant. auto *Const42 = sandboxir::ConstantFP::get(sandboxir::Type::getFloatTy(Ctx), 42.0); auto *NewConstCmp = sandboxir::CmpInst::create(llvm::CmpInst::FCMP_ULE, Const42, Const42, BB->begin(), Ctx, "NewConstCmp"); EXPECT_TRUE(isa(NewConstCmp)); } } TEST_F(SandboxIRTest, UnreachableInst) { parseIR(C, R"IR( define void @foo() { unreachable } )IR"); llvm::Function *LLVMF = &*M->getFunction("foo"); sandboxir::Context Ctx(C); sandboxir::Function *F = Ctx.createFunction(LLVMF); auto *BB = &*F->begin(); auto It = BB->begin(); auto *UI = cast(&*It++); EXPECT_EQ(UI->getNumSuccessors(), 0u); EXPECT_EQ(UI->getNumOfIRInstrs(), 1u); // Check create(InsertBefore) sandboxir::UnreachableInst *NewUI = sandboxir::UnreachableInst::create(UI->getIterator(), Ctx); EXPECT_EQ(NewUI->getNextNode(), UI); // Check create(InsertAtEnd) sandboxir::UnreachableInst *NewUIEnd = sandboxir::UnreachableInst::create(/*InsertAtEnd=*/BB, Ctx); EXPECT_EQ(NewUIEnd->getParent(), BB); EXPECT_EQ(NewUIEnd->getNextNode(), nullptr); } /// Makes sure that all Instruction sub-classes have a classof(). TEST_F(SandboxIRTest, CheckClassof) { #define DEF_INSTR(ID, OPC, CLASS) \ EXPECT_NE(&sandboxir::CLASS::classof, &sandboxir::Instruction::classof); #include "llvm/SandboxIR/Values.def" } TEST_F(SandboxIRTest, InstructionCallbacks) { parseIR(C, R"IR( define void @foo(ptr %ptr, i8 %val) { ret void } )IR"); Function &LLVMF = *M->getFunction("foo"); sandboxir::Context Ctx(C); auto &F = *Ctx.createFunction(&LLVMF); auto &BB = *F.begin(); sandboxir::Argument *Ptr = F.getArg(0); sandboxir::Argument *Val = F.getArg(1); sandboxir::Instruction *Ret = &BB.front(); SmallVector Inserted; auto InsertCbId = Ctx.registerCreateInstrCallback( [&Inserted](sandboxir::Instruction *I) { Inserted.push_back(I); }); SmallVector Removed; auto RemoveCbId = Ctx.registerEraseInstrCallback( [&Removed](sandboxir::Instruction *I) { Removed.push_back(I); }); // Keep the moved instruction and the instruction pointed by the Where // iterator so we can check both callback arguments work as expected. SmallVector> Moved; auto MoveCbId = Ctx.registerMoveInstrCallback( [&Moved](sandboxir::Instruction *I, const sandboxir::BBIterator &Where) { // Use a nullptr to signal "move to end" to keep it single. We only // have a basic block in this test case anyway. if (Where == Where.getNodeParent()->end()) Moved.push_back(std::make_pair(I, nullptr)); else Moved.push_back(std::make_pair(I, &*Where)); }); // Two more insertion callbacks, to check that they're called in registration // order. SmallVector Order; auto CheckOrderInsertCbId1 = Ctx.registerCreateInstrCallback( [&Order](sandboxir::Instruction *I) { Order.push_back(1); }); auto CheckOrderInsertCbId2 = Ctx.registerCreateInstrCallback( [&Order](sandboxir::Instruction *I) { Order.push_back(2); }); Ctx.save(); auto *NewI = sandboxir::StoreInst::create(Val, Ptr, /*Align=*/std::nullopt, Ret->getIterator(), Ctx); EXPECT_THAT(Inserted, testing::ElementsAre(NewI)); EXPECT_THAT(Removed, testing::IsEmpty()); EXPECT_THAT(Moved, testing::IsEmpty()); EXPECT_THAT(Order, testing::ElementsAre(1, 2)); Ret->moveBefore(NewI); EXPECT_THAT(Inserted, testing::ElementsAre(NewI)); EXPECT_THAT(Removed, testing::IsEmpty()); EXPECT_THAT(Moved, testing::ElementsAre(std::make_pair(Ret, NewI))); Ret->eraseFromParent(); EXPECT_THAT(Inserted, testing::ElementsAre(NewI)); EXPECT_THAT(Removed, testing::ElementsAre(Ret)); EXPECT_THAT(Moved, testing::ElementsAre(std::make_pair(Ret, NewI))); NewI->eraseFromParent(); EXPECT_THAT(Inserted, testing::ElementsAre(NewI)); EXPECT_THAT(Removed, testing::ElementsAre(Ret, NewI)); EXPECT_THAT(Moved, testing::ElementsAre(std::make_pair(Ret, NewI))); // Check that after revert the callbacks have been called for the inverse // operations of the changes made so far. Ctx.revert(); EXPECT_THAT(Inserted, testing::ElementsAre(NewI, NewI, Ret)); EXPECT_THAT(Removed, testing::ElementsAre(Ret, NewI, NewI)); EXPECT_THAT(Moved, testing::ElementsAre(std::make_pair(Ret, NewI), std::make_pair(Ret, nullptr))); EXPECT_THAT(Order, testing::ElementsAre(1, 2, 1, 2, 1, 2)); // Check that deregistration works. Do an operation of each type after // deregistering callbacks and check. Inserted.clear(); Removed.clear(); Moved.clear(); Ctx.unregisterCreateInstrCallback(InsertCbId); Ctx.unregisterEraseInstrCallback(RemoveCbId); Ctx.unregisterMoveInstrCallback(MoveCbId); Ctx.unregisterCreateInstrCallback(CheckOrderInsertCbId1); Ctx.unregisterCreateInstrCallback(CheckOrderInsertCbId2); auto *NewI2 = sandboxir::StoreInst::create(Val, Ptr, /*Align=*/std::nullopt, Ret->getIterator(), Ctx); Ret->moveBefore(NewI2); Ret->eraseFromParent(); EXPECT_THAT(Inserted, testing::IsEmpty()); EXPECT_THAT(Removed, testing::IsEmpty()); EXPECT_THAT(Moved, testing::IsEmpty()); } TEST_F(SandboxIRTest, FunctionObjectAlreadyExists) { parseIR(C, R"IR( define void @foo() { call void @bar() ret void } define void @bar() { ret void } )IR"); Function &LLVMFoo = *M->getFunction("foo"); Function &LLVMBar = *M->getFunction("bar"); sandboxir::Context Ctx(C); // This will create a Function object for @bar(). Ctx.createFunction(&LLVMFoo); EXPECT_NE(Ctx.getValue(&LLVMBar), nullptr); // This should not crash, even though there is already a value for LLVMBar. Ctx.createFunction(&LLVMBar); }