diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -704,7 +704,9 @@ :ref:`Thread Local Storage Model `. :ref:`Scalable vectors ` cannot be global variables or members of -structs or arrays because their size is unknown at compile time. +arrays because their size is unknown at compile time. They are allowed in +structs to facilitate intrinsics returning multiple values. Structs containing +scalable vectors cannot be used in loads, stores, allocas, or GEPs. Syntax:: diff --git a/llvm/include/llvm/IR/DerivedTypes.h b/llvm/include/llvm/IR/DerivedTypes.h --- a/llvm/include/llvm/IR/DerivedTypes.h +++ b/llvm/include/llvm/IR/DerivedTypes.h @@ -284,6 +284,9 @@ /// isSized - Return true if this is a sized type. bool isSized(SmallPtrSetImpl *Visited = nullptr) const; + /// Returns true if this struct contains a scalable vector. + bool containsScalableVectorType() const; + /// Return true if this is a named struct that has a non-empty name. bool hasName() const { return SymbolTableEntry != nullptr; } diff --git a/llvm/lib/IR/DataLayout.cpp b/llvm/lib/IR/DataLayout.cpp --- a/llvm/lib/IR/DataLayout.cpp +++ b/llvm/lib/IR/DataLayout.cpp @@ -65,7 +65,10 @@ StructAlignment = std::max(TyAlign, StructAlignment); MemberOffsets[i] = StructSize; - StructSize += DL.getTypeAllocSize(Ty); // Consume space for this data item + TypeSize TySize = DL.getTypeAllocSize(Ty); + assert(!TySize.isScalable() && "StructLayout is not supported for structs " + "containing scalable vectors"); + StructSize += TySize; // Consume space for this data item } // Add padding to the end of the struct so that it could be put in an array diff --git a/llvm/lib/IR/Type.cpp b/llvm/lib/IR/Type.cpp --- a/llvm/lib/IR/Type.cpp +++ b/llvm/lib/IR/Type.cpp @@ -390,6 +390,18 @@ return ST; } +bool StructType::containsScalableVectorType() const { + for (Type *Ty : elements()) { + if (isa(Ty)) + return true; + if (auto *STy = dyn_cast(Ty)) + if (STy->containsScalableVectorType()) + return true; + } + + return false; +} + void StructType::setBody(ArrayRef Elements, bool isPacked) { assert(isOpaque() && "Struct body already set!"); @@ -509,9 +521,14 @@ // Okay, our struct is sized if all of the elements are, but if one of the // elements is opaque, the struct isn't sized *yet*, but may become sized in // the future, so just bail out without caching. - for (element_iterator I = element_begin(), E = element_end(); I != E; ++I) - if (!(*I)->isSized(Visited)) + for (Type *Ty : elements()) { + // If the struct contains a scalable vector type, don't consider it sized. + // This prevents it from being used in loads/stores/allocas/GEPs. + if (isa(Ty)) + return false; + if (!Ty->isSized(Visited)) return false; + } // Here we cheat a bit and cast away const-ness. The goal is to memoize when // we find a sized type, as types can only move from opaque to sized, not the @@ -531,7 +548,7 @@ bool StructType::isValidElementType(Type *ElemTy) { return !ElemTy->isVoidTy() && !ElemTy->isLabelTy() && !ElemTy->isMetadataTy() && !ElemTy->isFunctionTy() && - !ElemTy->isTokenTy() && !isa(ElemTy); + !ElemTy->isTokenTy(); } bool StructType::isLayoutIdentical(StructType *Other) const { diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -702,12 +702,16 @@ } // Scalable vectors cannot be global variables, since we don't know - // the runtime size. If the global is a struct or an array containing - // scalable vectors, that will be caught by the isValidElementType methods - // in StructType or ArrayType instead. + // the runtime size. If the global is an array containing scalable vectors, + // that will be caught by the isValidElementType methods in StructType or + // ArrayType instead. Assert(!isa(GV.getValueType()), "Globals cannot contain scalable vectors", &GV); + if (auto *STy = dyn_cast(GV.getValueType())) + Assert(!STy->containsScalableVectorType(), + "Globals cannot contain scalable vectors", &GV); + if (!GV.hasInitializer()) { visitGlobalValue(GV); return; diff --git a/llvm/test/Other/scalable-vector-struct-intrinsic.ll b/llvm/test/Other/scalable-vector-struct-intrinsic.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Other/scalable-vector-struct-intrinsic.ll @@ -0,0 +1,18 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -verify < %s 2>&1 | FileCheck %s + +; Make sure we allow scalable vectors in structs for returning multiple +; values from intrinsics. + +declare { , } @llvm.sadd.with.overflow.nxv2i32(, ) + +define @foo( %x, %y) { +; CHECK-LABEL: @foo( +; CHECK-NEXT: [[A:%.*]] = call { , } @llvm.sadd.with.overflow.nxv2i32( [[X:%.*]], [[Y:%.*]]) +; CHECK-NEXT: [[B:%.*]] = extractvalue { , } [[A]], 0 +; CHECK-NEXT: ret [[B]] +; + %a = call { , } @llvm.sadd.with.overflow.nxv2i32( %x, %y) + %b = extractvalue { , } %a, 0 + ret %b +} diff --git a/llvm/test/Other/scalable-vector-struct.ll b/llvm/test/Other/scalable-vector-struct.ll deleted file mode 100644 --- a/llvm/test/Other/scalable-vector-struct.ll +++ /dev/null @@ -1,8 +0,0 @@ -; RUN: not opt -S -verify < %s 2>&1 | FileCheck %s - -;; Structs cannot contain scalable vectors; make sure we detect them even -;; when nested inside other aggregates. - -%ty = type [2 x { i32, }] -; CHECK: error: invalid element type for struct -; CHECK: %ty = type [2 x { i32, }] diff --git a/llvm/test/Verifier/scalable-global-vars.ll b/llvm/test/Verifier/scalable-global-vars.ll --- a/llvm/test/Verifier/scalable-global-vars.ll +++ b/llvm/test/Verifier/scalable-global-vars.ll @@ -7,6 +7,10 @@ ; CHECK-NEXT: * @ScalableVecGlobal @ScalableVecGlobal = global zeroinitializer +; CHECK-NEXT: Globals cannot contain scalable vectors +; CHECK-NEXT: { i32, }* @ScalableVecStructGlobal +@ScalableVecStructGlobal = global { i32, } zeroinitializer + ;; Global _pointers_ to scalable vectors are fine ; CHECK-NOT: Globals cannot contain scalable vectors -@ScalableVecPtr = global * zeroinitializer \ No newline at end of file +@ScalableVecPtr = global * zeroinitializer diff --git a/llvm/test/Verifier/scalable-vector-struct-alloca.ll b/llvm/test/Verifier/scalable-vector-struct-alloca.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/scalable-vector-struct-alloca.ll @@ -0,0 +1,7 @@ +; RUN: not opt -S -verify < %s 2>&1 | FileCheck %s + +define void @alloca() { +; CHECK: error: Cannot allocate unsized type + %a = alloca { i32, } + ret void +} diff --git a/llvm/test/Verifier/scalable-vector-struct-load.ll b/llvm/test/Verifier/scalable-vector-struct-load.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/scalable-vector-struct-load.ll @@ -0,0 +1,8 @@ +; RUN: not opt -S -verify < %s 2>&1 | FileCheck %s + +define @load({ i32, }* %x) { +; CHECK: error: loading unsized types is not allowed + %a = load { i32, }, { i32, }* %x + %b = extractvalue { i32, } %a, 1 + ret %b +} diff --git a/llvm/test/Verifier/scalable-vector-struct-store.ll b/llvm/test/Verifier/scalable-vector-struct-store.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/scalable-vector-struct-store.ll @@ -0,0 +1,9 @@ +; RUN: not opt -S -verify < %s 2>&1 | FileCheck %s + +define void @store({ i32, }* %x, i32 %y, %z) { +; CHECK: error: storing unsized types is not allowed + %a = insertvalue { i32, } undef, i32 %y, 0 + %b = insertvalue { i32, } %a, %z, 1 + store { i32, } %b, { i32, }* %x + ret void +}