Skip to content

Commit 4831e0a

Browse files
authored
[IR] Disallow recursive types (#114799)
StructType::setBody is the only mechanism that can potentially create recursion in the type system. Add a runtime check that it is not actually used to create recursion. If the check fails, report an error from LLParser, BitcodeReader and IRLinker. In all other cases assert that the check succeeds. In future StructType::setBody will be removed in favor of specifying the body when the type is created, so any performance hit from this runtime check will be temporary.
1 parent 72f3621 commit 4831e0a

File tree

16 files changed

+65
-95
lines changed

16 files changed

+65
-95
lines changed

llvm/docs/LangRef.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4386,7 +4386,7 @@ is defined inline with other types (e.g. ``[2 x {i32, i32}]``) whereas
43864386
identified types are always defined at the top level with a name.
43874387
Literal types are uniqued by their contents and can never be recursive
43884388
or opaque since there is no way to write one. Identified types can be
4389-
recursive, can be opaqued, and are never uniqued.
4389+
opaqued and are never uniqued. Identified types must not be recursive.
43904390

43914391
:Syntax:
43924392

llvm/docs/ReleaseNotes.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ Makes programs 10x faster by doing Special New Thing.
5656
Changes to the LLVM IR
5757
----------------------
5858

59+
* Types are no longer allowed to be recursive.
60+
5961
* The `x86_mmx` IR type has been removed. It will be translated to
6062
the standard vector type `<1 x i64>` in bitcode upgrade.
6163
* Renamed `llvm.experimental.stepvector` intrinsic to `llvm.stepvector`.

llvm/include/llvm/IR/DerivedTypes.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class Value;
3333
class APInt;
3434
class LLVMContext;
3535
template <typename T> class Expected;
36+
class Error;
3637

3738
/// Class to represent integer types. Note that this class is also used to
3839
/// represent the built-in integer types: Int1Ty, Int8Ty, Int16Ty, Int32Ty and
@@ -317,9 +318,18 @@ class StructType : public Type {
317318
/// suffix if there is a collision. Do not call this on an literal type.
318319
void setName(StringRef Name);
319320

320-
/// Specify a body for an opaque identified type.
321+
/// Specify a body for an opaque identified type, which must not make the type
322+
/// recursive.
321323
void setBody(ArrayRef<Type*> Elements, bool isPacked = false);
322324

325+
/// Specify a body for an opaque identified type or return an error if it
326+
/// would make the type recursive.
327+
Error setBodyOrError(ArrayRef<Type *> Elements, bool isPacked = false);
328+
329+
/// Return an error if the body for an opaque identified type would make it
330+
/// recursive.
331+
Error checkBody(ArrayRef<Type *> Elements);
332+
323333
template <typename... Tys>
324334
std::enable_if_t<are_base_of<Type, Tys...>::value, void>
325335
setBody(Type *elt1, Tys *... elts) {

llvm/lib/AsmParser/LLParser.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3391,7 +3391,9 @@ bool LLParser::parseStructDefinition(SMLoc TypeLoc, StringRef Name,
33913391
(isPacked && parseToken(lltok::greater, "expected '>' in packed struct")))
33923392
return true;
33933393

3394-
STy->setBody(Body, isPacked);
3394+
if (auto E = STy->setBodyOrError(Body, isPacked))
3395+
return tokError(toString(std::move(E)));
3396+
33953397
ResultTy = STy;
33963398
return false;
33973399
}

llvm/lib/Bitcode/Reader/BitcodeReader.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2659,7 +2659,8 @@ Error BitcodeReader::parseTypeTableBody() {
26592659
}
26602660
if (EltTys.size() != Record.size()-1)
26612661
return error("Invalid named struct record");
2662-
Res->setBody(EltTys, Record[0]);
2662+
if (auto E = Res->setBodyOrError(EltTys, Record[0]))
2663+
return E;
26632664
ContainedIDs.append(Record.begin() + 1, Record.end());
26642665
ResultTy = Res;
26652666
break;

llvm/lib/IR/Type.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "llvm/IR/Type.h"
1414
#include "LLVMContextImpl.h"
1515
#include "llvm/ADT/APInt.h"
16+
#include "llvm/ADT/SetVector.h"
1617
#include "llvm/ADT/SmallString.h"
1718
#include "llvm/ADT/StringMap.h"
1819
#include "llvm/ADT/StringRef.h"
@@ -436,20 +437,37 @@ bool StructType::containsHomogeneousTypes() const {
436437
}
437438

438439
void StructType::setBody(ArrayRef<Type*> Elements, bool isPacked) {
440+
cantFail(setBodyOrError(Elements, isPacked));
441+
}
442+
443+
Error StructType::setBodyOrError(ArrayRef<Type *> Elements, bool isPacked) {
439444
assert(isOpaque() && "Struct body already set!");
440445

446+
if (auto E = checkBody(Elements))
447+
return E;
448+
441449
setSubclassData(getSubclassData() | SCDB_HasBody);
442450
if (isPacked)
443451
setSubclassData(getSubclassData() | SCDB_Packed);
444452

445453
NumContainedTys = Elements.size();
454+
ContainedTys = Elements.empty()
455+
? nullptr
456+
: Elements.copy(getContext().pImpl->Alloc).data();
446457

447-
if (Elements.empty()) {
448-
ContainedTys = nullptr;
449-
return;
450-
}
458+
return Error::success();
459+
}
451460

452-
ContainedTys = Elements.copy(getContext().pImpl->Alloc).data();
461+
Error StructType::checkBody(ArrayRef<Type *> Elements) {
462+
SmallSetVector<Type *, 4> Worklist(Elements.begin(), Elements.end());
463+
for (unsigned I = 0; I < Worklist.size(); ++I) {
464+
Type *Ty = Worklist[I];
465+
if (Ty == this)
466+
return createStringError(Twine("identified structure type '") +
467+
getName() + "' is recursive");
468+
Worklist.insert(Ty->subtype_begin(), Ty->subtype_end());
469+
}
470+
return Error::success();
453471
}
454472

455473
void StructType::setName(StringRef Name) {

llvm/lib/Linker/IRMover.cpp

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@
3434
#include <utility>
3535
using namespace llvm;
3636

37+
/// Most of the errors produced by this module are inconvertible StringErrors.
38+
/// This convenience function lets us return one of those more easily.
39+
static Error stringErr(const Twine &T) {
40+
return make_error<StringError>(T, inconvertibleErrorCode());
41+
}
42+
3743
//===----------------------------------------------------------------------===//
3844
// TypeMap implementation.
3945
//===----------------------------------------------------------------------===//
@@ -69,7 +75,7 @@ class TypeMapTy : public ValueMapTypeRemapper {
6975

7076
/// Produce a body for an opaque type in the dest module from a type
7177
/// definition in the source module.
72-
void linkDefinedTypeBodies();
78+
Error linkDefinedTypeBodies();
7379

7480
/// Return the mapped type to use for the specified input type from the
7581
/// source module.
@@ -207,7 +213,7 @@ bool TypeMapTy::areTypesIsomorphic(Type *DstTy, Type *SrcTy) {
207213
return true;
208214
}
209215

210-
void TypeMapTy::linkDefinedTypeBodies() {
216+
Error TypeMapTy::linkDefinedTypeBodies() {
211217
SmallVector<Type *, 16> Elements;
212218
for (StructType *SrcSTy : SrcDefinitionsToResolve) {
213219
StructType *DstSTy = cast<StructType>(MappedTypes[SrcSTy]);
@@ -218,11 +224,13 @@ void TypeMapTy::linkDefinedTypeBodies() {
218224
for (unsigned I = 0, E = Elements.size(); I != E; ++I)
219225
Elements[I] = get(SrcSTy->getElementType(I));
220226

221-
DstSTy->setBody(Elements, SrcSTy->isPacked());
227+
if (auto E = DstSTy->setBodyOrError(Elements, SrcSTy->isPacked()))
228+
return E;
222229
DstStructTypesSet.switchToNonOpaque(DstSTy);
223230
}
224231
SrcDefinitionsToResolve.clear();
225232
DstResolvedOpaqueTypes.clear();
233+
return Error::success();
226234
}
227235

228236
void TypeMapTy::finishType(StructType *DTy, StructType *STy,
@@ -439,12 +447,6 @@ class IRLinker {
439447
FoundError = std::move(E);
440448
}
441449

442-
/// Most of the errors produced by this module are inconvertible StringErrors.
443-
/// This convenience function lets us return one of those more easily.
444-
Error stringErr(const Twine &T) {
445-
return make_error<StringError>(T, inconvertibleErrorCode());
446-
}
447-
448450
/// Entry point for mapping values and alternate context for mapping aliases.
449451
ValueMapper Mapper;
450452
unsigned IndirectSymbolMCID;
@@ -875,7 +877,7 @@ void IRLinker::computeTypeMapping() {
875877

876878
// Now that we have discovered all of the type equivalences, get a body for
877879
// any 'opaque' types in the dest module that are now resolved.
878-
TypeMap.linkDefinedTypeBodies();
880+
setError(TypeMap.linkDefinedTypeBodies());
879881
}
880882

881883
static void getArrayElements(const Constant *C,
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
; RUN: not llvm-as < %s 2>&1 | FileCheck %s
2+
3+
; CHECK: error: identified structure type 'rt3' is recursive
4+
5+
%rt1 = type { i32, { i8, %rt2, i8 }, i32 }
6+
%rt2 = type { i64, { i6, %rt3 } }
7+
%rt3 = type { %rt1 }
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
; RUN: not llvm-as < %s 2>&1 | FileCheck %s
22

3-
; CHECK: base element of getelementptr must be sized
3+
; CHECK: error: identified structure type 'myTy' is recursive
44

55
%myTy = type { %myTy }
6-
define void @foo(ptr %p){
7-
getelementptr %myTy, ptr %p, i64 1
8-
ret void
9-
}

llvm/test/Linker/pr22807.ll

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
; RUN: llvm-link -S -o - %p/pr22807.ll %p/Inputs/pr22807-1.ll %p/Inputs/pr22807-2.ll | FileCheck %s
1+
; RUN: not llvm-link -S -o - %p/pr22807.ll %p/Inputs/pr22807-1.ll %p/Inputs/pr22807-2.ll 2>&1 | FileCheck %s
22

3-
; CHECK-NOT: type
4-
; CHECK: %struct.B = type { %struct.A }
5-
; CHECK-NEXT: %struct.A = type { %struct.B }
6-
; CHECK-NOT: type
3+
; CHECK: error: identified structure type 'struct.A' is recursive
74

85
%struct.B = type { %struct.A }
96
%struct.A = type opaque

llvm/test/Verifier/recursive-struct-param.ll

Lines changed: 0 additions & 15 deletions
This file was deleted.

llvm/test/Verifier/recursive-type-1.ll

Lines changed: 0 additions & 12 deletions
This file was deleted.

llvm/test/Verifier/recursive-type-2.ll

Lines changed: 0 additions & 14 deletions
This file was deleted.

llvm/test/Verifier/recursive-type-load.ll

Lines changed: 0 additions & 12 deletions
This file was deleted.

llvm/test/Verifier/recursive-type-store.ll

Lines changed: 0 additions & 12 deletions
This file was deleted.

llvm/unittests/Analysis/TargetLibraryInfoTest.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class TargetLibraryInfoTest : public testing::Test {
5959

6060
// Check that we don't accept egregiously incorrect prototypes.
6161
TEST_F(TargetLibraryInfoTest, InvalidProto) {
62-
parseAssembly("%foo = type { %foo }\n");
62+
parseAssembly("%foo = type opaque\n");
6363

6464
auto *StructTy = StructType::getTypeByName(Context, "foo");
6565
auto *InvalidFTy = FunctionType::get(StructTy, /*isVarArg=*/false);

0 commit comments

Comments
 (0)