2016-01-26 18:48:36 +00:00
|
|
|
//===- llvm/unittest/IR/VerifierTest.cpp - Verifier unit tests --*- C++ -*-===//
|
2010-02-15 22:09:09 +00:00
|
|
|
//
|
2019-01-19 08:50:56 +00:00
|
|
|
// 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
|
2010-02-15 22:09:09 +00:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2017-06-06 11:06:56 +00:00
|
|
|
#include "llvm/IR/Verifier.h"
|
2013-01-02 11:36:10 +00:00
|
|
|
#include "llvm/IR/Constants.h"
|
2016-01-14 22:42:02 +00:00
|
|
|
#include "llvm/IR/DIBuilder.h"
|
2013-01-02 11:36:10 +00:00
|
|
|
#include "llvm/IR/DerivedTypes.h"
|
|
|
|
#include "llvm/IR/Function.h"
|
|
|
|
#include "llvm/IR/GlobalAlias.h"
|
|
|
|
#include "llvm/IR/GlobalVariable.h"
|
2016-09-14 17:30:37 +00:00
|
|
|
#include "llvm/IR/IRBuilder.h"
|
2017-06-06 11:06:56 +00:00
|
|
|
#include "llvm/IR/Instructions.h"
|
2013-01-02 11:36:10 +00:00
|
|
|
#include "llvm/IR/LLVMContext.h"
|
|
|
|
#include "llvm/IR/Module.h"
|
2010-02-15 22:09:09 +00:00
|
|
|
#include "gtest/gtest.h"
|
|
|
|
|
|
|
|
namespace llvm {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
TEST(VerifierTest, Branch_i1) {
|
2016-04-14 21:59:01 +00:00
|
|
|
LLVMContext C;
|
2013-10-30 22:37:51 +00:00
|
|
|
Module M("M", C);
|
2010-02-15 22:09:09 +00:00
|
|
|
FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg=*/false);
|
[opaque pointer types] Add a FunctionCallee wrapper type, and use it.
Recommit r352791 after tweaking DerivedTypes.h slightly, so that gcc
doesn't choke on it, hopefully.
Original Message:
The FunctionCallee type is effectively a {FunctionType*,Value*} pair,
and is a useful convenience to enable code to continue passing the
result of getOrInsertFunction() through to EmitCall, even once pointer
types lose their pointee-type.
Then:
- update the CallInst/InvokeInst instruction creation functions to
take a Callee,
- modify getOrInsertFunction to return FunctionCallee, and
- update all callers appropriately.
One area of particular note is the change to the sanitizer
code. Previously, they had been casting the result of
`getOrInsertFunction` to a `Function*` via
`checkSanitizerInterfaceFunction`, and storing that. That would report
an error if someone had already inserted a function declaraction with
a mismatching signature.
However, in general, LLVM allows for such mismatches, as
`getOrInsertFunction` will automatically insert a bitcast if
needed. As part of this cleanup, cause the sanitizer code to do the
same. (It will call its functions using the expected signature,
however they may have been declared.)
Finally, in a small number of locations, callers of
`getOrInsertFunction` actually were expecting/requiring that a brand
new function was being created. In such cases, I've switched them to
Function::Create instead.
Differential Revision: https://reviews.llvm.org/D57315
llvm-svn: 352827
2019-02-01 02:28:03 +00:00
|
|
|
Function *F = Function::Create(FTy, Function::ExternalLinkage, "foo", M);
|
2013-10-30 22:37:51 +00:00
|
|
|
BasicBlock *Entry = BasicBlock::Create(C, "entry", F);
|
|
|
|
BasicBlock *Exit = BasicBlock::Create(C, "exit", F);
|
2010-02-15 22:09:09 +00:00
|
|
|
ReturnInst::Create(C, Exit);
|
|
|
|
|
|
|
|
// To avoid triggering an assertion in BranchInst::Create, we first create
|
|
|
|
// a branch with an 'i1' condition ...
|
|
|
|
|
|
|
|
Constant *False = ConstantInt::getFalse(C);
|
|
|
|
BranchInst *BI = BranchInst::Create(Exit, Exit, False, Entry);
|
|
|
|
|
|
|
|
// ... then use setOperand to redirect it to a value of different type.
|
|
|
|
|
|
|
|
Constant *Zero32 = ConstantInt::get(IntegerType::get(C, 32), 0);
|
|
|
|
BI->setOperand(0, Zero32);
|
|
|
|
|
2014-01-19 02:22:18 +00:00
|
|
|
EXPECT_TRUE(verifyFunction(*F));
|
2010-02-15 22:09:09 +00:00
|
|
|
}
|
|
|
|
|
[IR] Redefine Freeze instruction
Summary:
This patch redefines freeze instruction from being UnaryOperator to a subclass of UnaryInstruction.
ConstantExpr freeze is removed, as discussed in the previous review.
FreezeOperator is not added because there's no ConstantExpr freeze.
`freeze i8* null` test is added to `test/Bindings/llvm-c/freeze.ll` as well, because the null pointer-related bug in `tools/llvm-c/echo.cpp` is now fixed.
InstVisitor has visitFreeze now because freeze is not unaryop anymore.
Reviewers: whitequark, deadalnix, craig.topper, jdoerfert, lebedev.ri
Reviewed By: craig.topper, lebedev.ri
Subscribers: regehr, nlopes, mehdi_amini, hiraditya, steven_wu, dexonsmith, jfb, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D69932
2019-11-07 01:17:49 +09:00
|
|
|
TEST(VerifierTest, Freeze) {
|
|
|
|
LLVMContext C;
|
|
|
|
Module M("M", C);
|
|
|
|
FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg=*/false);
|
|
|
|
Function *F = Function::Create(FTy, Function::ExternalLinkage, "foo", M);
|
|
|
|
BasicBlock *Entry = BasicBlock::Create(C, "entry", F);
|
|
|
|
ReturnInst *RI = ReturnInst::Create(C, Entry);
|
|
|
|
|
|
|
|
IntegerType *ITy = IntegerType::get(C, 32);
|
|
|
|
ConstantInt *CI = ConstantInt::get(ITy, 0);
|
|
|
|
|
|
|
|
// Valid type : freeze(<2 x i32>)
|
2020-08-19 17:26:36 +00:00
|
|
|
Constant *CV = ConstantVector::getSplat(ElementCount::getFixed(2), CI);
|
[IR] Redefine Freeze instruction
Summary:
This patch redefines freeze instruction from being UnaryOperator to a subclass of UnaryInstruction.
ConstantExpr freeze is removed, as discussed in the previous review.
FreezeOperator is not added because there's no ConstantExpr freeze.
`freeze i8* null` test is added to `test/Bindings/llvm-c/freeze.ll` as well, because the null pointer-related bug in `tools/llvm-c/echo.cpp` is now fixed.
InstVisitor has visitFreeze now because freeze is not unaryop anymore.
Reviewers: whitequark, deadalnix, craig.topper, jdoerfert, lebedev.ri
Reviewed By: craig.topper, lebedev.ri
Subscribers: regehr, nlopes, mehdi_amini, hiraditya, steven_wu, dexonsmith, jfb, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D69932
2019-11-07 01:17:49 +09:00
|
|
|
FreezeInst *FI_vec = new FreezeInst(CV);
|
|
|
|
FI_vec->insertBefore(RI);
|
|
|
|
|
|
|
|
EXPECT_FALSE(verifyFunction(*F));
|
|
|
|
|
|
|
|
FI_vec->eraseFromParent();
|
|
|
|
|
|
|
|
// Valid type : freeze(float)
|
|
|
|
Constant *CFP = ConstantFP::get(Type::getDoubleTy(C), 0.0);
|
|
|
|
FreezeInst *FI_dbl = new FreezeInst(CFP);
|
|
|
|
FI_dbl->insertBefore(RI);
|
|
|
|
|
|
|
|
EXPECT_FALSE(verifyFunction(*F));
|
|
|
|
|
|
|
|
FI_dbl->eraseFromParent();
|
|
|
|
|
|
|
|
// Valid type : freeze(i32*)
|
|
|
|
PointerType *PT = PointerType::get(ITy, 0);
|
|
|
|
ConstantPointerNull *CPN = ConstantPointerNull::get(PT);
|
|
|
|
FreezeInst *FI_ptr = new FreezeInst(CPN);
|
|
|
|
FI_ptr->insertBefore(RI);
|
|
|
|
|
|
|
|
EXPECT_FALSE(verifyFunction(*F));
|
|
|
|
|
|
|
|
FI_ptr->eraseFromParent();
|
|
|
|
|
|
|
|
// Valid type : freeze(int)
|
|
|
|
FreezeInst *FI = new FreezeInst(CI);
|
|
|
|
FI->insertBefore(RI);
|
|
|
|
|
|
|
|
EXPECT_FALSE(verifyFunction(*F));
|
|
|
|
|
2019-11-12 11:23:19 +09:00
|
|
|
FI->eraseFromParent();
|
[IR] Redefine Freeze instruction
Summary:
This patch redefines freeze instruction from being UnaryOperator to a subclass of UnaryInstruction.
ConstantExpr freeze is removed, as discussed in the previous review.
FreezeOperator is not added because there's no ConstantExpr freeze.
`freeze i8* null` test is added to `test/Bindings/llvm-c/freeze.ll` as well, because the null pointer-related bug in `tools/llvm-c/echo.cpp` is now fixed.
InstVisitor has visitFreeze now because freeze is not unaryop anymore.
Reviewers: whitequark, deadalnix, craig.topper, jdoerfert, lebedev.ri
Reviewed By: craig.topper, lebedev.ri
Subscribers: regehr, nlopes, mehdi_amini, hiraditya, steven_wu, dexonsmith, jfb, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D69932
2019-11-07 01:17:49 +09:00
|
|
|
}
|
|
|
|
|
2013-06-19 19:26:44 +00:00
|
|
|
TEST(VerifierTest, InvalidRetAttribute) {
|
2016-04-14 21:59:01 +00:00
|
|
|
LLVMContext C;
|
2013-06-19 19:26:44 +00:00
|
|
|
Module M("M", C);
|
|
|
|
FunctionType *FTy = FunctionType::get(Type::getInt32Ty(C), /*isVarArg=*/false);
|
[opaque pointer types] Add a FunctionCallee wrapper type, and use it.
Recommit r352791 after tweaking DerivedTypes.h slightly, so that gcc
doesn't choke on it, hopefully.
Original Message:
The FunctionCallee type is effectively a {FunctionType*,Value*} pair,
and is a useful convenience to enable code to continue passing the
result of getOrInsertFunction() through to EmitCall, even once pointer
types lose their pointee-type.
Then:
- update the CallInst/InvokeInst instruction creation functions to
take a Callee,
- modify getOrInsertFunction to return FunctionCallee, and
- update all callers appropriately.
One area of particular note is the change to the sanitizer
code. Previously, they had been casting the result of
`getOrInsertFunction` to a `Function*` via
`checkSanitizerInterfaceFunction`, and storing that. That would report
an error if someone had already inserted a function declaraction with
a mismatching signature.
However, in general, LLVM allows for such mismatches, as
`getOrInsertFunction` will automatically insert a bitcast if
needed. As part of this cleanup, cause the sanitizer code to do the
same. (It will call its functions using the expected signature,
however they may have been declared.)
Finally, in a small number of locations, callers of
`getOrInsertFunction` actually were expecting/requiring that a brand
new function was being created. In such cases, I've switched them to
Function::Create instead.
Differential Revision: https://reviews.llvm.org/D57315
llvm-svn: 352827
2019-02-01 02:28:03 +00:00
|
|
|
Function *F = Function::Create(FTy, Function::ExternalLinkage, "foo", M);
|
Rename AttributeSet to AttributeList
Summary:
This class is a list of AttributeSetNodes corresponding the function
prototype of a call or function declaration. This class used to be
called ParamAttrListPtr, then AttrListPtr, then AttributeSet. It is
typically accessed by parameter and return value index, so
"AttributeList" seems like a more intuitive name.
Rename AttributeSetImpl to AttributeListImpl to follow suit.
It's useful to rename this class so that we can rename AttributeSetNode
to AttributeSet later. AttributeSet is the set of attributes that apply
to a single function, argument, or return value.
Reviewers: sanjoy, javed.absar, chandlerc, pete
Reviewed By: pete
Subscribers: pete, jholewinski, arsenm, dschuff, mehdi_amini, jfb, nhaehnle, sbc100, void, llvm-commits
Differential Revision: https://reviews.llvm.org/D31102
llvm-svn: 298393
2017-03-21 16:57:19 +00:00
|
|
|
AttributeList AS = F->getAttributes();
|
Extend the `uwtable` attribute with unwind table kind
We have the `clang -cc1` command-line option `-funwind-tables=1|2` and
the codegen option `VALUE_CODEGENOPT(UnwindTables, 2, 0) ///< Unwind
tables (1) or asynchronous unwind tables (2)`. However, this is
encoded in LLVM IR by the presence or the absence of the `uwtable`
attribute, i.e. we lose the information whether to generate want just
some unwind tables or asynchronous unwind tables.
Asynchronous unwind tables take more space in the runtime image, I'd
estimate something like 80-90% more, as the difference is adding
roughly the same number of CFI directives as for prologues, only a bit
simpler (e.g. `.cfi_offset reg, off` vs. `.cfi_restore reg`). Or even
more, if you consider tail duplication of epilogue blocks.
Asynchronous unwind tables could also restrict code generation to
having only a finite number of frame pointer adjustments (an example
of *not* having a finite number of `SP` adjustments is on AArch64 when
untagging the stack (MTE) in some cases the compiler can modify `SP`
in a loop).
Having the CFI precise up to an instruction generally also means one
cannot bundle together CFI instructions once the prologue is done,
they need to be interspersed with ordinary instructions, which means
extra `DW_CFA_advance_loc` commands, further increasing the unwind
tables size.
That is to say, async unwind tables impose a non-negligible overhead,
yet for the most common use cases (like C++ exceptions), they are not
even needed.
This patch extends the `uwtable` attribute with an optional
value:
- `uwtable` (default to `async`)
- `uwtable(sync)`, synchronous unwind tables
- `uwtable(async)`, asynchronous (instruction precise) unwind tables
Reviewed By: MaskRay
Differential Revision: https://reviews.llvm.org/D114543
2022-02-14 13:41:34 +00:00
|
|
|
F->setAttributes(AS.addRetAttribute(
|
|
|
|
C, Attribute::getWithUWTableKind(C, UWTableKind::Default)));
|
2013-06-19 19:26:44 +00:00
|
|
|
|
|
|
|
std::string Error;
|
2014-01-19 02:22:18 +00:00
|
|
|
raw_string_ostream ErrorOS(Error);
|
|
|
|
EXPECT_TRUE(verifyModule(M, &ErrorOS));
|
2024-09-13 10:01:05 -04:00
|
|
|
EXPECT_TRUE(StringRef(Error).starts_with(
|
|
|
|
"Attribute 'uwtable' does not apply to function return values"));
|
2013-06-19 19:26:44 +00:00
|
|
|
}
|
|
|
|
|
2022-12-06 13:04:15 -05:00
|
|
|
/// Test the verifier rejects invalid nofpclass values that the assembler may
|
|
|
|
/// also choose to reject.
|
|
|
|
TEST(VerifierTest, InvalidNoFPClassAttribute) {
|
|
|
|
LLVMContext C;
|
|
|
|
|
|
|
|
const unsigned InvalidMasks[] = {0, fcAllFlags + 1};
|
|
|
|
|
|
|
|
for (unsigned InvalidMask : InvalidMasks) {
|
|
|
|
Module M("M", C);
|
|
|
|
FunctionType *FTy =
|
|
|
|
FunctionType::get(Type::getFloatTy(C), /*isVarArg=*/false);
|
|
|
|
Function *F = Function::Create(FTy, Function::ExternalLinkage, "foo", M);
|
|
|
|
AttributeList AS = F->getAttributes();
|
|
|
|
|
|
|
|
// Don't use getWithNoFPClass to avoid using out of bounds enum values here.
|
|
|
|
F->setAttributes(AS.addRetAttribute(
|
|
|
|
C, Attribute::get(C, Attribute::NoFPClass, InvalidMask)));
|
|
|
|
|
|
|
|
std::string Error;
|
|
|
|
raw_string_ostream ErrorOS(Error);
|
|
|
|
EXPECT_TRUE(verifyModule(M, &ErrorOS));
|
|
|
|
|
2024-09-13 10:01:05 -04:00
|
|
|
StringRef ErrMsg(Error);
|
2022-12-06 13:04:15 -05:00
|
|
|
|
|
|
|
if (InvalidMask == 0) {
|
2023-12-13 22:46:02 -08:00
|
|
|
EXPECT_TRUE(ErrMsg.starts_with(
|
2022-12-06 13:04:15 -05:00
|
|
|
"Attribute 'nofpclass' must have at least one test bit set"))
|
|
|
|
<< ErrMsg;
|
|
|
|
} else {
|
2023-12-13 22:46:02 -08:00
|
|
|
EXPECT_TRUE(ErrMsg.starts_with("Invalid value for 'nofpclass' test mask"))
|
2022-12-06 13:04:15 -05:00
|
|
|
<< ErrMsg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-01 19:06:36 +00:00
|
|
|
TEST(VerifierTest, CrossModuleRef) {
|
2016-04-14 21:59:01 +00:00
|
|
|
LLVMContext C;
|
2015-12-01 19:06:36 +00:00
|
|
|
Module M1("M1", C);
|
|
|
|
Module M2("M2", C);
|
2016-01-14 22:20:56 +00:00
|
|
|
Module M3("M3", C);
|
2015-12-01 19:06:36 +00:00
|
|
|
FunctionType *FTy = FunctionType::get(Type::getInt32Ty(C), /*isVarArg=*/false);
|
[opaque pointer types] Add a FunctionCallee wrapper type, and use it.
Recommit r352791 after tweaking DerivedTypes.h slightly, so that gcc
doesn't choke on it, hopefully.
Original Message:
The FunctionCallee type is effectively a {FunctionType*,Value*} pair,
and is a useful convenience to enable code to continue passing the
result of getOrInsertFunction() through to EmitCall, even once pointer
types lose their pointee-type.
Then:
- update the CallInst/InvokeInst instruction creation functions to
take a Callee,
- modify getOrInsertFunction to return FunctionCallee, and
- update all callers appropriately.
One area of particular note is the change to the sanitizer
code. Previously, they had been casting the result of
`getOrInsertFunction` to a `Function*` via
`checkSanitizerInterfaceFunction`, and storing that. That would report
an error if someone had already inserted a function declaraction with
a mismatching signature.
However, in general, LLVM allows for such mismatches, as
`getOrInsertFunction` will automatically insert a bitcast if
needed. As part of this cleanup, cause the sanitizer code to do the
same. (It will call its functions using the expected signature,
however they may have been declared.)
Finally, in a small number of locations, callers of
`getOrInsertFunction` actually were expecting/requiring that a brand
new function was being created. In such cases, I've switched them to
Function::Create instead.
Differential Revision: https://reviews.llvm.org/D57315
llvm-svn: 352827
2019-02-01 02:28:03 +00:00
|
|
|
Function *F1 = Function::Create(FTy, Function::ExternalLinkage, "foo1", M1);
|
|
|
|
Function *F2 = Function::Create(FTy, Function::ExternalLinkage, "foo2", M2);
|
|
|
|
Function *F3 = Function::Create(FTy, Function::ExternalLinkage, "foo3", M3);
|
2015-12-01 19:06:36 +00:00
|
|
|
|
|
|
|
BasicBlock *Entry1 = BasicBlock::Create(C, "entry", F1);
|
|
|
|
BasicBlock *Entry3 = BasicBlock::Create(C, "entry", F3);
|
|
|
|
|
|
|
|
// BAD: Referencing function in another module
|
|
|
|
CallInst::Create(F2,"call",Entry1);
|
|
|
|
|
|
|
|
// BAD: Referencing personality routine in another module
|
|
|
|
F3->setPersonalityFn(F2);
|
|
|
|
|
|
|
|
// Fill in the body
|
|
|
|
Constant *ConstZero = ConstantInt::get(Type::getInt32Ty(C), 0);
|
|
|
|
ReturnInst::Create(C, ConstZero, Entry1);
|
|
|
|
ReturnInst::Create(C, ConstZero, Entry3);
|
|
|
|
|
|
|
|
std::string Error;
|
|
|
|
raw_string_ostream ErrorOS(Error);
|
2016-01-14 22:20:56 +00:00
|
|
|
EXPECT_TRUE(verifyModule(M2, &ErrorOS));
|
2024-09-13 10:01:05 -04:00
|
|
|
EXPECT_TRUE(Error == "Global is referenced in a different module!\n"
|
|
|
|
"ptr @foo2\n"
|
|
|
|
"; ModuleID = 'M2'\n"
|
|
|
|
" %call = call i32 @foo2()\n"
|
|
|
|
"ptr @foo1\n"
|
|
|
|
"; ModuleID = 'M1'\n"
|
|
|
|
"Global is used by function in a different module\n"
|
|
|
|
"ptr @foo2\n"
|
|
|
|
"; ModuleID = 'M2'\n"
|
|
|
|
"ptr @foo3\n"
|
|
|
|
"; ModuleID = 'M3'\n");
|
2016-01-14 22:20:56 +00:00
|
|
|
|
|
|
|
Error.clear();
|
2015-12-01 19:06:36 +00:00
|
|
|
EXPECT_TRUE(verifyModule(M1, &ErrorOS));
|
2024-09-13 10:01:05 -04:00
|
|
|
EXPECT_TRUE(StringRef(Error) == "Referencing function in another module!\n"
|
|
|
|
" %call = call i32 @foo2()\n"
|
|
|
|
"; ModuleID = 'M1'\n"
|
|
|
|
"ptr @foo2\n"
|
|
|
|
"; ModuleID = 'M2'\n");
|
2015-12-01 19:06:36 +00:00
|
|
|
|
|
|
|
Error.clear();
|
|
|
|
EXPECT_TRUE(verifyModule(M3, &ErrorOS));
|
2024-09-13 10:01:05 -04:00
|
|
|
EXPECT_TRUE(StringRef(Error).starts_with(
|
|
|
|
"Referencing personality function in another module!"));
|
2015-12-01 19:06:36 +00:00
|
|
|
|
|
|
|
// Erase bad methods to avoid triggering an assertion failure on destruction
|
|
|
|
F1->eraseFromParent();
|
|
|
|
F3->eraseFromParent();
|
|
|
|
}
|
|
|
|
|
2016-05-11 13:23:52 +00:00
|
|
|
TEST(VerifierTest, InvalidVariableLinkage) {
|
|
|
|
LLVMContext C;
|
|
|
|
Module M("M", C);
|
|
|
|
new GlobalVariable(M, Type::getInt8Ty(C), false,
|
|
|
|
GlobalValue::LinkOnceODRLinkage, nullptr, "Some Global");
|
|
|
|
std::string Error;
|
|
|
|
raw_string_ostream ErrorOS(Error);
|
|
|
|
EXPECT_TRUE(verifyModule(M, &ErrorOS));
|
2024-09-13 10:01:05 -04:00
|
|
|
EXPECT_TRUE(StringRef(Error).starts_with("Global is external, but doesn't "
|
|
|
|
"have external or weak linkage!"));
|
2016-05-11 13:23:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(VerifierTest, InvalidFunctionLinkage) {
|
|
|
|
LLVMContext C;
|
|
|
|
Module M("M", C);
|
|
|
|
|
|
|
|
FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg=*/false);
|
|
|
|
Function::Create(FTy, GlobalValue::LinkOnceODRLinkage, "foo", &M);
|
|
|
|
std::string Error;
|
|
|
|
raw_string_ostream ErrorOS(Error);
|
|
|
|
EXPECT_TRUE(verifyModule(M, &ErrorOS));
|
2024-09-13 10:01:05 -04:00
|
|
|
EXPECT_TRUE(StringRef(Error).starts_with("Global is external, but doesn't "
|
|
|
|
"have external or weak linkage!"));
|
2016-05-11 13:23:52 +00:00
|
|
|
}
|
|
|
|
|
2017-10-02 18:31:29 +00:00
|
|
|
TEST(VerifierTest, DetectInvalidDebugInfo) {
|
2016-09-14 17:30:37 +00:00
|
|
|
{
|
|
|
|
LLVMContext C;
|
|
|
|
Module M("M", C);
|
|
|
|
DIBuilder DIB(M);
|
2016-12-14 20:24:54 +00:00
|
|
|
DIB.createCompileUnit(dwarf::DW_LANG_C89, DIB.createFile("broken.c", "/"),
|
|
|
|
"unittest", false, "", 0);
|
2016-09-14 17:30:37 +00:00
|
|
|
DIB.finalize();
|
|
|
|
EXPECT_FALSE(verifyModule(M));
|
|
|
|
|
|
|
|
// Now break it by inserting non-CU node to the list of CUs.
|
|
|
|
auto *File = DIB.createFile("not-a-CU.f", ".");
|
|
|
|
NamedMDNode *NMD = M.getOrInsertNamedMetadata("llvm.dbg.cu");
|
|
|
|
NMD->addOperand(File);
|
|
|
|
EXPECT_TRUE(verifyModule(M));
|
|
|
|
}
|
|
|
|
{
|
|
|
|
LLVMContext C;
|
|
|
|
Module M("M", C);
|
|
|
|
DIBuilder DIB(M);
|
2016-12-14 20:24:54 +00:00
|
|
|
auto *CU = DIB.createCompileUnit(dwarf::DW_LANG_C89,
|
|
|
|
DIB.createFile("broken.c", "/"),
|
2016-09-14 17:30:37 +00:00
|
|
|
"unittest", false, "", 0);
|
|
|
|
new GlobalVariable(M, Type::getInt8Ty(C), false,
|
|
|
|
GlobalValue::ExternalLinkage, nullptr, "g");
|
|
|
|
|
[opaque pointer types] Add a FunctionCallee wrapper type, and use it.
Recommit r352791 after tweaking DerivedTypes.h slightly, so that gcc
doesn't choke on it, hopefully.
Original Message:
The FunctionCallee type is effectively a {FunctionType*,Value*} pair,
and is a useful convenience to enable code to continue passing the
result of getOrInsertFunction() through to EmitCall, even once pointer
types lose their pointee-type.
Then:
- update the CallInst/InvokeInst instruction creation functions to
take a Callee,
- modify getOrInsertFunction to return FunctionCallee, and
- update all callers appropriately.
One area of particular note is the change to the sanitizer
code. Previously, they had been casting the result of
`getOrInsertFunction` to a `Function*` via
`checkSanitizerInterfaceFunction`, and storing that. That would report
an error if someone had already inserted a function declaraction with
a mismatching signature.
However, in general, LLVM allows for such mismatches, as
`getOrInsertFunction` will automatically insert a bitcast if
needed. As part of this cleanup, cause the sanitizer code to do the
same. (It will call its functions using the expected signature,
however they may have been declared.)
Finally, in a small number of locations, callers of
`getOrInsertFunction` actually were expecting/requiring that a brand
new function was being created. In such cases, I've switched them to
Function::Create instead.
Differential Revision: https://reviews.llvm.org/D57315
llvm-svn: 352827
2019-02-01 02:28:03 +00:00
|
|
|
auto *F = Function::Create(FunctionType::get(Type::getVoidTy(C), false),
|
|
|
|
Function::ExternalLinkage, "f", M);
|
2016-09-14 17:30:37 +00:00
|
|
|
IRBuilder<> Builder(BasicBlock::Create(C, "", F));
|
|
|
|
Builder.CreateUnreachable();
|
2018-11-19 18:29:28 +00:00
|
|
|
F->setSubprogram(DIB.createFunction(
|
|
|
|
CU, "f", "f", DIB.createFile("broken.c", "/"), 1, nullptr, 1,
|
|
|
|
DINode::FlagZero,
|
|
|
|
DISubprogram::SPFlagLocalToUnit | DISubprogram::SPFlagDefinition));
|
2016-09-14 17:30:37 +00:00
|
|
|
DIB.finalize();
|
|
|
|
EXPECT_FALSE(verifyModule(M));
|
|
|
|
|
|
|
|
// Now break it by not listing the CU at all.
|
|
|
|
M.eraseNamedMetadata(M.getOrInsertNamedMetadata("llvm.dbg.cu"));
|
|
|
|
EXPECT_TRUE(verifyModule(M));
|
|
|
|
}
|
2016-05-09 19:57:29 +00:00
|
|
|
}
|
|
|
|
|
2021-03-24 11:52:02 -07:00
|
|
|
TEST(VerifierTest, MDNodeWrongContext) {
|
|
|
|
LLVMContext C1, C2;
|
2024-09-21 10:59:50 +01:00
|
|
|
auto *Node = MDNode::get(C1, {});
|
2021-03-24 11:52:02 -07:00
|
|
|
|
|
|
|
Module M("M", C2);
|
|
|
|
auto *NamedNode = M.getOrInsertNamedMetadata("test");
|
|
|
|
NamedNode->addOperand(Node);
|
|
|
|
|
|
|
|
std::string Error;
|
|
|
|
raw_string_ostream ErrorOS(Error);
|
|
|
|
EXPECT_TRUE(verifyModule(M, &ErrorOS));
|
2024-09-13 10:01:05 -04:00
|
|
|
EXPECT_TRUE(StringRef(Error).starts_with(
|
|
|
|
"MDNode context does not match Module context!"));
|
2021-03-24 11:52:02 -07:00
|
|
|
}
|
|
|
|
|
2021-03-24 16:13:29 -07:00
|
|
|
TEST(VerifierTest, AttributesWrongContext) {
|
|
|
|
LLVMContext C1, C2;
|
|
|
|
Module M1("M", C1);
|
|
|
|
FunctionType *FTy1 =
|
|
|
|
FunctionType::get(Type::getVoidTy(C1), /*isVarArg=*/false);
|
|
|
|
Function *F1 = Function::Create(FTy1, Function::ExternalLinkage, "foo", M1);
|
|
|
|
F1->setDoesNotReturn();
|
|
|
|
|
|
|
|
Module M2("M", C2);
|
|
|
|
FunctionType *FTy2 =
|
|
|
|
FunctionType::get(Type::getVoidTy(C2), /*isVarArg=*/false);
|
|
|
|
Function *F2 = Function::Create(FTy2, Function::ExternalLinkage, "foo", M2);
|
|
|
|
F2->copyAttributesFrom(F1);
|
|
|
|
|
|
|
|
EXPECT_TRUE(verifyFunction(*F2));
|
|
|
|
}
|
|
|
|
|
2022-10-24 19:10:47 -07:00
|
|
|
TEST(VerifierTest, SwitchInst) {
|
|
|
|
LLVMContext C;
|
|
|
|
Module M("M", C);
|
|
|
|
IntegerType *Int32Ty = Type::getInt32Ty(C);
|
|
|
|
FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), {Int32Ty, Int32Ty},
|
|
|
|
/*isVarArg=*/false);
|
|
|
|
Function *F = Function::Create(FTy, Function::ExternalLinkage, "foo", M);
|
|
|
|
BasicBlock *Entry = BasicBlock::Create(C, "entry", F);
|
|
|
|
BasicBlock *Default = BasicBlock::Create(C, "default", F);
|
|
|
|
BasicBlock *OnOne = BasicBlock::Create(C, "on_one", F);
|
|
|
|
BasicBlock *OnTwo = BasicBlock::Create(C, "on_two", F);
|
|
|
|
|
|
|
|
BasicBlock *Exit = BasicBlock::Create(C, "exit", F);
|
|
|
|
|
|
|
|
BranchInst::Create(Exit, Default);
|
|
|
|
BranchInst::Create(Exit, OnTwo);
|
|
|
|
BranchInst::Create(Exit, OnOne);
|
|
|
|
ReturnInst::Create(C, Exit);
|
|
|
|
|
|
|
|
Value *Cond = F->getArg(0);
|
|
|
|
SwitchInst *Switch = SwitchInst::Create(Cond, Default, 2, Entry);
|
|
|
|
Switch->addCase(ConstantInt::get(Int32Ty, 1), OnOne);
|
|
|
|
Switch->addCase(ConstantInt::get(Int32Ty, 2), OnTwo);
|
|
|
|
|
|
|
|
EXPECT_FALSE(verifyFunction(*F));
|
|
|
|
// set one case value to function argument.
|
|
|
|
Switch->setOperand(2, F->getArg(1));
|
|
|
|
EXPECT_TRUE(verifyFunction(*F));
|
|
|
|
}
|
|
|
|
|
2024-02-29 13:15:41 +01:00
|
|
|
TEST(VerifierTest, CrossFunctionRef) {
|
|
|
|
LLVMContext C;
|
|
|
|
Module M("M", C);
|
|
|
|
FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg=*/false);
|
|
|
|
Function *F1 = Function::Create(FTy, Function::ExternalLinkage, "foo1", M);
|
|
|
|
Function *F2 = Function::Create(FTy, Function::ExternalLinkage, "foo2", M);
|
|
|
|
BasicBlock *Entry1 = BasicBlock::Create(C, "entry", F1);
|
|
|
|
BasicBlock *Entry2 = BasicBlock::Create(C, "entry", F2);
|
|
|
|
Type *I32 = Type::getInt32Ty(C);
|
|
|
|
|
|
|
|
Value *Alloca = new AllocaInst(I32, 0, "alloca", Entry1);
|
|
|
|
ReturnInst::Create(C, Entry1);
|
|
|
|
|
|
|
|
Instruction *Store = new StoreInst(ConstantInt::get(I32, 0), Alloca, Entry2);
|
|
|
|
ReturnInst::Create(C, Entry2);
|
|
|
|
|
|
|
|
std::string Error;
|
|
|
|
raw_string_ostream ErrorOS(Error);
|
|
|
|
EXPECT_TRUE(verifyModule(M, &ErrorOS));
|
2024-09-13 10:01:05 -04:00
|
|
|
EXPECT_TRUE(StringRef(Error).starts_with(
|
|
|
|
"Referring to an instruction in another function!"));
|
2024-02-29 13:15:41 +01:00
|
|
|
|
|
|
|
// Explicitly erase the store to avoid a use-after-free when the module is
|
|
|
|
// destroyed.
|
|
|
|
Store->eraseFromParent();
|
|
|
|
}
|
|
|
|
|
2024-04-06 15:27:45 -04:00
|
|
|
TEST(VerifierTest, AtomicRMW) {
|
|
|
|
LLVMContext C;
|
|
|
|
Module M("M", C);
|
|
|
|
FunctionType *FTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg=*/false);
|
|
|
|
Function *F = Function::Create(FTy, Function::ExternalLinkage, "foo", M);
|
|
|
|
BasicBlock *Entry = BasicBlock::Create(C, "entry", F);
|
|
|
|
Value *Ptr = PoisonValue::get(PointerType::get(C, 0));
|
|
|
|
|
|
|
|
Type *FPTy = Type::getFloatTy(C);
|
|
|
|
Constant *CF = ConstantFP::getZero(FPTy);
|
|
|
|
|
|
|
|
// Invalid scalable type : atomicrmw (<vscale x 2 x float>)
|
|
|
|
Constant *CV = ConstantVector::getSplat(ElementCount::getScalable(2), CF);
|
|
|
|
new AtomicRMWInst(AtomicRMWInst::FAdd, Ptr, CV, Align(8),
|
|
|
|
AtomicOrdering::SequentiallyConsistent, SyncScope::System,
|
|
|
|
Entry);
|
|
|
|
ReturnInst::Create(C, Entry);
|
|
|
|
|
|
|
|
std::string Error;
|
|
|
|
raw_string_ostream ErrorOS(Error);
|
|
|
|
EXPECT_TRUE(verifyFunction(*F, &ErrorOS));
|
2024-09-13 10:01:05 -04:00
|
|
|
EXPECT_TRUE(StringRef(Error).starts_with(
|
|
|
|
"atomicrmw fadd operand must have floating-point or "
|
|
|
|
"fixed vector of floating-point type!"))
|
|
|
|
<< Error;
|
2024-04-06 15:27:45 -04:00
|
|
|
}
|
|
|
|
|
2016-01-26 18:48:36 +00:00
|
|
|
} // end anonymous namespace
|
|
|
|
} // end namespace llvm
|