https://github.com/hoodmane updated https://github.com/llvm/llvm-project/pull/205817
>From d4223c5748d0c0447e8d755325d305fe04978d4c Mon Sep 17 00:00:00 2001 From: Hood Chatham <[email protected]> Date: Thu, 4 Jun 2026 14:42:11 -0700 Subject: [PATCH 1/2] Add WebAssemblyTableType This adds a new WebAssemblyTableType to the Clang IR intended to represent WebAssembly tables. It does not use it anywhere yet. These correspond to llvm_table_ty in llvm ir and tableidx immediates in llvm ir. Because a tableidx is an immediate, these are not really values at all. All values of this type must be global variables and they cannot be used anywhere other than arguments to wasm table builtins like __builtin_wasm_table_get(). --- clang/include/clang/AST/ASTContext.h | 5 ++ clang/include/clang/AST/RecursiveASTVisitor.h | 6 +++ clang/include/clang/AST/TypeBase.h | 34 +++++++++++++ clang/include/clang/AST/TypeLoc.h | 23 +++++++++ clang/include/clang/AST/TypeProperties.td | 10 ++++ clang/include/clang/Basic/TypeNodes.td | 1 + .../clang/Serialization/TypeBitCodes.def | 1 + clang/lib/AST/ASTContext.cpp | 50 +++++++++++++++++++ clang/lib/AST/ASTImporter.cpp | 9 ++++ clang/lib/AST/ASTStructuralEquivalence.cpp | 6 +++ clang/lib/AST/ExprConstant.cpp | 1 + clang/lib/AST/ItaniumMangle.cpp | 8 +++ clang/lib/AST/MicrosoftMangle.cpp | 14 ++++++ clang/lib/AST/ODRHash.cpp | 5 ++ clang/lib/AST/Type.cpp | 6 +++ clang/lib/AST/TypePrinter.cpp | 14 ++++++ clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 5 ++ clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 6 +++ clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 9 ++++ clang/lib/CodeGen/CGDebugInfo.cpp | 7 +++ clang/lib/CodeGen/CGDebugInfo.h | 1 + clang/lib/CodeGen/CodeGenFunction.cpp | 2 + clang/lib/CodeGen/CodeGenTypes.cpp | 8 +++ clang/lib/CodeGen/ItaniumCXXABI.cpp | 6 +++ clang/lib/CodeGen/QualTypeMapper.cpp | 6 +++ clang/lib/Sema/SemaExpr.cpp | 1 + clang/lib/Sema/SemaLookup.cpp | 1 + clang/lib/Sema/SemaTemplate.cpp | 5 ++ clang/lib/Sema/SemaTemplateDeduction.cpp | 2 + clang/lib/Sema/TreeTransform.h | 33 ++++++++++++ clang/lib/Serialization/ASTReader.cpp | 4 ++ clang/lib/Serialization/ASTWriter.cpp | 3 ++ clang/tools/libclang/CIndex.cpp | 4 ++ .../TypeSystem/Clang/TypeSystemClang.cpp | 6 +++ 34 files changed, 302 insertions(+) diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index a4ed852d36442..e478f47110ce4 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -291,6 +291,7 @@ class ASTContext : public RefCountedBase<ASTContext> { mutable llvm::ContextualFoldingSet<AttributedType, ASTContext &> AttributedTypes; mutable llvm::FoldingSet<PipeType> PipeTypes; + mutable llvm::FoldingSet<WebAssemblyTableType> WebAssemblyTableTypes; mutable llvm::FoldingSet<BitIntType> BitIntTypes; mutable llvm::ContextualFoldingSet<DependentBitIntType, ASTContext &> DependentBitIntTypes; @@ -1671,6 +1672,10 @@ class ASTContext : public RefCountedBase<ASTContext> { /// Return a write_only pipe type for the specified type. QualType getWritePipeType(QualType T) const; + /// Return a WebAssembly table type with the specified element type (a + /// WebAssembly reference type). + QualType getWebAssemblyTableType(QualType T) const; + /// Return a bit-precise integer type with the specified signedness and bit /// count. QualType getBitIntType(bool Unsigned, unsigned NumBits) const; diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index b000a34043696..4ed5ac7040bb4 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1250,6 +1250,9 @@ DEF_TRAVERSE_TYPE(AtomicType, { TRY_TO(TraverseType(T->getValueType())); }) DEF_TRAVERSE_TYPE(PipeType, { TRY_TO(TraverseType(T->getElementType())); }) +DEF_TRAVERSE_TYPE(WebAssemblyTableType, + { TRY_TO(TraverseType(T->getElementType())); }) + DEF_TRAVERSE_TYPE(BitIntType, {}) DEF_TRAVERSE_TYPE(DependentBitIntType, { TRY_TO(TraverseStmt(T->getNumBitsExpr())); }) @@ -1614,6 +1617,9 @@ DEF_TRAVERSE_TYPELOC(AtomicType, { TRY_TO(TraverseTypeLoc(TL.getValueLoc())); }) DEF_TRAVERSE_TYPELOC(PipeType, { TRY_TO(TraverseTypeLoc(TL.getValueLoc())); }) +DEF_TRAVERSE_TYPELOC(WebAssemblyTableType, + { TRY_TO(TraverseTypeLoc(TL.getValueLoc())); }) + DEF_TRAVERSE_TYPELOC(BitIntType, {}) DEF_TRAVERSE_TYPELOC(DependentBitIntType, { TRY_TO(TraverseStmt(TL.getTypePtr()->getNumBitsExpr())); diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h index 3a801e2857b13..493a5242581ef 100644 --- a/clang/include/clang/AST/TypeBase.h +++ b/clang/include/clang/AST/TypeBase.h @@ -8295,6 +8295,40 @@ class PipeType : public Type, public llvm::FoldingSetNode { bool isReadOnly() const { return isRead; } }; +/// WebAssemblyTableType - declared via `static __externref_t table[];`. +/// +/// These correspond to llvm_table_ty in llvm ir and tableidx immediates in llvm +/// ir. Because a tableidx is an immediate, these are not really values at all. +/// All values of this type must be global variables and they cannot be used +/// anywhere other than arguments to wasm table builtins like +/// __builtin_wasm_table_get(). +class WebAssemblyTableType : public Type, public llvm::FoldingSetNode { + friend class ASTContext; // ASTContext creates these. + + QualType ElementType; + + WebAssemblyTableType(QualType elemType, QualType CanonicalPtr) + : Type(WebAssemblyTable, CanonicalPtr, elemType->getDependence()), + ElementType(elemType) {} + +public: + QualType getElementType() const { return ElementType; } + + bool isSugared() const { return false; } + + QualType desugar() const { return QualType(this, 0); } + + void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, getElementType()); } + + static void Profile(llvm::FoldingSetNodeID &ID, QualType T) { + ID.AddPointer(T.getAsOpaquePtr()); + } + + static bool classof(const Type *T) { + return T->getTypeClass() == WebAssemblyTable; + } +}; + /// A fixed int type of a specified bitwidth. class BitIntType final : public Type, public llvm::FoldingSetNode { friend class ASTContext; diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h index 24df18dbaace4..9893ff49ee6a0 100644 --- a/clang/include/clang/AST/TypeLoc.h +++ b/clang/include/clang/AST/TypeLoc.h @@ -2731,6 +2731,29 @@ class PipeTypeLoc : public ConcreteTypeLoc<UnqualTypeLoc, PipeTypeLoc, PipeType, QualType getInnerType() const { return this->getTypePtr()->getElementType(); } }; +struct WebAssemblyTableTypeLocInfo { + SourceLocation KWLoc; +}; + +class WebAssemblyTableTypeLoc + : public ConcreteTypeLoc<UnqualTypeLoc, WebAssemblyTableTypeLoc, + WebAssemblyTableType, + WebAssemblyTableTypeLocInfo> { +public: + TypeLoc getValueLoc() const { return this->getInnerTypeLoc(); } + + SourceRange getLocalSourceRange() const { return SourceRange(getKWLoc()); } + + SourceLocation getKWLoc() const { return this->getLocalData()->KWLoc; } + void setKWLoc(SourceLocation Loc) { this->getLocalData()->KWLoc = Loc; } + + void initializeLocal(ASTContext &Context, SourceLocation Loc) { + setKWLoc(Loc); + } + + QualType getInnerType() const { return this->getTypePtr()->getElementType(); } +}; + template <typename T> inline T TypeLoc::getAsAdjusted() const { TypeLoc Cur = *this; diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td index f16c10da430f9..891a046cb31c3 100644 --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -967,6 +967,16 @@ let Class = PipeType in { }]>; } +let Class = WebAssemblyTableType in { + def : Property<"elementType", QualType> { + let Read = [{ node->getElementType() }]; + } + + def : Creator<[{ + return ctx.getWebAssemblyTableType(elementType); + }]>; +} + let Class = BitIntType in { def : Property<"isUnsigned", Bool> { let Read = [{ node->isUnsigned() }]; diff --git a/clang/include/clang/Basic/TypeNodes.td b/clang/include/clang/Basic/TypeNodes.td index a9965a4a89aa1..b61440f244670 100644 --- a/clang/include/clang/Basic/TypeNodes.td +++ b/clang/include/clang/Basic/TypeNodes.td @@ -106,6 +106,7 @@ def ObjCObjectPointerType : TypeNode<Type>; def BoundsAttributedType : TypeNode<Type, 1>; def CountAttributedType : TypeNode<BoundsAttributedType>, NeverCanonical; def PipeType : TypeNode<Type>; +def WebAssemblyTableType : TypeNode<Type>; def AtomicType : TypeNode<Type>; def BitIntType : TypeNode<Type>; def DependentBitIntType : TypeNode<Type>, AlwaysDependent; diff --git a/clang/include/clang/Serialization/TypeBitCodes.def b/clang/include/clang/Serialization/TypeBitCodes.def index 9f1da65e0f940..8b66cd1e3cadb 100644 --- a/clang/include/clang/Serialization/TypeBitCodes.def +++ b/clang/include/clang/Serialization/TypeBitCodes.def @@ -70,5 +70,6 @@ TYPE_BIT_CODE(HLSLInlineSpirv, HLSL_INLINE_SPIRV, 60) TYPE_BIT_CODE(PredefinedSugar, PREDEFINED_SUGAR, 61) TYPE_BIT_CODE(SubstBuiltinTemplatePack, SUBST_BUILTIN_TEMPLATE_PACK, 62) TYPE_BIT_CODE(OverflowBehavior, OVERFLOWBEHAVIOR, 63) +TYPE_BIT_CODE(WebAssemblyTable, WEBASSEMBLY_TABLE, 64) #undef TYPE_BIT_CODE diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index abf0cd5e18c2b..a6722fde583c4 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -2638,6 +2638,15 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { Width = Target->getPointerWidth(LangAS::opencl_global); Align = Target->getPointerAlign(LangAS::opencl_global); break; + + case Type::WebAssemblyTable: { + // A WebAssembly table is a zero-length array of a reference type; it has + // no storage size and takes the alignment of its element type. + const auto *WTT = cast<WebAssemblyTableType>(T); + Width = 0; + Align = getTypeAlign(WTT->getElementType()); + break; + } } assert(llvm::isPowerOf2_32(Align) && "Alignment must be power of 2"); @@ -3493,6 +3502,7 @@ static void encodeTypeForFunctionPointerAuth(const ASTContext &Ctx, // Don't bother discriminating based on these types. case Type::Pipe: + case Type::WebAssemblyTable: case Type::BitInt: case Type::ConstantMatrix: OS << "?"; @@ -4343,6 +4353,7 @@ QualType ASTContext::getVariableArrayDecayedType(QualType type) const { case Type::HLSLAttributedResource: case Type::HLSLInlineSpirv: case Type::OverflowBehavior: + case Type::WebAssemblyTable: llvm_unreachable("type should never be variably-modified"); // These types can be variably-modified but should never need to @@ -5222,6 +5233,34 @@ QualType ASTContext::getWritePipeType(QualType T) const { return getPipeType(T, false); } +QualType ASTContext::getWebAssemblyTableType(QualType T) const { + llvm::FoldingSetNodeID ID; + WebAssemblyTableType::Profile(ID, T); + + void *InsertPos = nullptr; + if (WebAssemblyTableType *WTT = + WebAssemblyTableTypes.FindNodeOrInsertPos(ID, InsertPos)) + return QualType(WTT, 0); + + // If the element type isn't canonical, this won't be a canonical type either, + // so fill in the canonical type field. + QualType Canonical; + if (!T.isCanonical()) { + Canonical = getWebAssemblyTableType(getCanonicalType(T)); + + // Get the new insert position for the node we care about. + WebAssemblyTableType *NewIP = + WebAssemblyTableTypes.FindNodeOrInsertPos(ID, InsertPos); + assert(!NewIP && "Shouldn't be in the map!"); + (void)NewIP; + } + auto *New = new (*this, alignof(WebAssemblyTableType)) + WebAssemblyTableType(T, Canonical); + Types.push_back(New); + WebAssemblyTableTypes.InsertNode(New, InsertPos); + return QualType(New, 0); +} + QualType ASTContext::getBitIntType(bool IsUnsigned, unsigned NumBits) const { llvm::FoldingSetNodeID ID; BitIntType::Profile(ID, IsUnsigned, NumBits); @@ -9758,6 +9797,7 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string &S, case Type::ArrayParameter: case Type::Pipe: + case Type::WebAssemblyTable: #define ABSTRACT_TYPE(KIND, BASE) #define TYPE(KIND, BASE) #define DEPENDENT_TYPE(KIND, BASE) \ @@ -12208,6 +12248,10 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer, assert(LHS != RHS && "Equivalent pipe types should have already been handled!"); return {}; + case Type::WebAssemblyTable: + assert(LHS != RHS && "Equivalent WebAssembly table types should have " + "already been handled!"); + return {}; case Type::ArrayParameter: assert(LHS != RHS && "Equivalent ArrayParameter types should have already been handled!"); @@ -14662,6 +14706,11 @@ static QualType getCommonNonSugarTypeNode(const ASTContext &Ctx, const Type *X, : &ASTContext::getWritePipeType; return (Ctx.*MP)(getCommonElementType(Ctx, PX, PY)); } + case Type::WebAssemblyTable: { + const auto *TX = cast<WebAssemblyTableType>(X), + *TY = cast<WebAssemblyTableType>(Y); + return Ctx.getWebAssemblyTableType(getCommonElementType(Ctx, TX, TY)); + } case Type::TemplateTypeParm: { const auto *TX = cast<TemplateTypeParmType>(X), *TY = cast<TemplateTypeParmType>(Y); @@ -14717,6 +14766,7 @@ static QualType getCommonSugarTypeNode(const ASTContext &Ctx, const Type *X, CANONICAL_TYPE(RValueReference) CANONICAL_TYPE(VariableArray) CANONICAL_TYPE(Vector) + CANONICAL_TYPE(WebAssemblyTable) #undef CANONICAL_TYPE #undef UNEXPECTED_TYPE diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 567d2d07298a3..f53e9934930e6 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -2172,6 +2172,15 @@ ExpectedType clang::ASTNodeImporter::VisitPipeType(const clang::PipeType *T) { return ToCtx.getWritePipeType(*ToElementTypeOrErr); } +ExpectedType clang::ASTNodeImporter::VisitWebAssemblyTableType( + const clang::WebAssemblyTableType *T) { + ExpectedType ToElementTypeOrErr = import(T->getElementType()); + if (!ToElementTypeOrErr) + return ToElementTypeOrErr.takeError(); + + return Importer.getToContext().getWebAssemblyTableType(*ToElementTypeOrErr); +} + //---------------------------------------------------------------------------- // Import Declarations //---------------------------------------------------------------------------- diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index e0b62591a6a73..6be798a8aa37b 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -1528,6 +1528,12 @@ bool ASTStructuralEquivalence::isEquivalent( cast<PipeType>(T2)->getElementType())) return false; break; + case Type::WebAssemblyTable: + if (!IsStructurallyEquivalent( + Context, cast<WebAssemblyTableType>(T1)->getElementType(), + cast<WebAssemblyTableType>(T2)->getElementType())) + return false; + break; case Type::BitInt: { const auto *Int1 = cast<BitIntType>(T1); const auto *Int2 = cast<BitIntType>(T2); diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 28ac44edd800c..fd018ed266c00 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -15988,6 +15988,7 @@ GCCTypeClass EvaluateBuiltinClassifyType(QualType T, case Type::ObjCInterface: case Type::ObjCObjectPointer: case Type::Pipe: + case Type::WebAssemblyTable: case Type::HLSLAttributedResource: case Type::HLSLInlineSpirv: case Type::OverflowBehavior: diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index e5cdd6f31c507..287075c8f64f7 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -2468,6 +2468,7 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty, case Type::ObjCTypeParam: case Type::Atomic: case Type::Pipe: + case Type::WebAssemblyTable: case Type::MacroQualified: case Type::BitInt: case Type::DependentBitInt: @@ -4622,6 +4623,13 @@ void CXXNameMangler::mangleType(const PipeType *T) { Out << "8ocl_pipe"; } +void CXXNameMangler::mangleType(const WebAssemblyTableType *T) { + // Vendor-extended type mangling for WebAssembly tables. + // <type> ::= u9wasmtable <element-type> + Out << "u9wasmtable"; + mangleType(T->getElementType()); +} + void CXXNameMangler::mangleType(const OverflowBehaviorType *T) { // Vender-extended type mangling for OverflowBehaviorType // <type> ::= U <behavior> <underlying_type> diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index adfe260a0d091..5e4ef6b4ad47c 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -3928,6 +3928,20 @@ void MicrosoftCXXNameMangler::mangleType(const PipeType *T, Qualifiers, mangleArtificialTagType(TagTypeKind::Struct, TemplateMangling, {"__clang"}); } +void MicrosoftCXXNameMangler::mangleType(const WebAssemblyTableType *T, + Qualifiers, SourceRange Range) { + QualType ElementType = T->getElementType(); + + llvm::SmallString<64> TemplateMangling; + llvm::raw_svector_ostream Stream(TemplateMangling); + MicrosoftCXXNameMangler Extra(Context, Stream); + Stream << "?$"; + Extra.mangleSourceName("wasmtable"); + Extra.mangleType(ElementType, Range, QMM_Escape); + + mangleArtificialTagType(TagTypeKind::Struct, TemplateMangling, {"__clang"}); +} + void MicrosoftMangleContextImpl::mangleCXXName(GlobalDecl GD, raw_ostream &Out) { const NamedDecl *D = cast<NamedDecl>(GD.getDecl()); diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index 46a4e256ea3e5..62537c18ccb5e 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -1126,6 +1126,11 @@ class ODRTypeVisitor : public TypeVisitor<ODRTypeVisitor> { VisitType(T); } + void VisitWebAssemblyTableType(const WebAssemblyTableType *T) { + AddQualType(T->getElementType()); + VisitType(T); + } + void VisitPointerType(const PointerType *T) { AddQualType(T->getPointeeType()); VisitType(T); diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index dffb3f1b50207..329abcceb2c98 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -5011,6 +5011,8 @@ static CachedProperties computeCachedProperties(const Type *T) { return Cache::get(cast<AtomicType>(T)->getValueType()); case Type::Pipe: return Cache::get(cast<PipeType>(T)->getElementType()); + case Type::WebAssemblyTable: + return Cache::get(cast<WebAssemblyTableType>(T)->getElementType()); case Type::HLSLAttributedResource: return Cache::get(cast<HLSLAttributedResourceType>(T)->getWrappedType()); case Type::HLSLInlineSpirv: @@ -5112,6 +5114,9 @@ LinkageInfo LinkageComputer::computeTypeLinkageInfo(const Type *T) { return computeTypeLinkageInfo(cast<AtomicType>(T)->getValueType()); case Type::Pipe: return computeTypeLinkageInfo(cast<PipeType>(T)->getElementType()); + case Type::WebAssemblyTable: + return computeTypeLinkageInfo( + cast<WebAssemblyTableType>(T)->getElementType()); case Type::OverflowBehavior: return computeTypeLinkageInfo( cast<OverflowBehaviorType>(T)->getUnderlyingType()); @@ -5304,6 +5309,7 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const { case Type::ObjCInterface: case Type::Atomic: case Type::Pipe: + case Type::WebAssemblyTable: case Type::BitInt: case Type::DependentBitInt: case Type::ArrayParameter: diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index e8fbffb9f954d..e1ae5612286a6 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -240,6 +240,7 @@ bool TypePrinter::canPrefixQualifiers(const Type *T, case Type::ObjCInterface: case Type::Atomic: case Type::Pipe: + case Type::WebAssemblyTable: case Type::BitInt: case Type::DependentBitInt: case Type::BTFTagAttributed: @@ -1520,6 +1521,19 @@ void TypePrinter::printPipeBefore(const PipeType *T, raw_ostream &OS) { void TypePrinter::printPipeAfter(const PipeType *T, raw_ostream &OS) {} +void TypePrinter::printWebAssemblyTableBefore(const WebAssemblyTableType *T, + raw_ostream &OS) { + IncludeStrongLifetimeRAII Strong(Policy); + printBefore(T->getElementType(), OS); +} + +void TypePrinter::printWebAssemblyTableAfter(const WebAssemblyTableType *T, + raw_ostream &OS) { + // Print like the zero-length array a table is declared as. + OS << "[0]"; + printAfter(T->getElementType(), OS); +} + void TypePrinter::printBitIntBefore(const BitIntType *T, raw_ostream &OS) { if (T->isUnsigned()) OS << "unsigned "; diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 6606cf74c7dea..4b957167f31a2 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -86,6 +86,7 @@ cir::TypeEvaluationKind CIRGenFunction::getEvaluationKind(QualType type) { case Type::ObjCObject: case Type::ObjCInterface: case Type::ArrayParameter: + case Type::WebAssemblyTable: return cir::TEK_Aggregate; // We operate on atomic values according to their underlying type. @@ -1770,6 +1771,10 @@ void CIRGenFunction::emitVariablyModifiedType(QualType type) { case Type::Pipe: type = cast<clang::PipeType>(ty)->getElementType(); break; + + case Type::WebAssemblyTable: + type = cast<clang::WebAssemblyTableType>(ty)->getElementType(); + break; } } while (type->isVariablyModifiedType()); } diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index 552d73966e97b..5550dc5437172 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -1029,6 +1029,9 @@ const char *vTableClassNameForType(const CIRGenModule &cgm, const Type *ty) { case Type::Pipe: llvm_unreachable("Pipe types shouldn't get here"); + case Type::WebAssemblyTable: + llvm_unreachable("WebAssembly table types shouldn't get here"); + case Type::ArrayParameter: llvm_unreachable("Array Parameter types should not get here."); @@ -1522,6 +1525,9 @@ mlir::Attribute CIRGenItaniumRTTIBuilder::buildTypeInfo( case Type::Pipe: break; + case Type::WebAssemblyTable: + break; + case Type::BitInt: break; diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp index 3170666304a06..370328954d59b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp @@ -586,6 +586,15 @@ mlir::Type CIRGenTypes::convertType(QualType type) { break; } + case Type::WebAssemblyTable: { + // A WebAssembly table lowers to a zero-length array of its (reference type) + // element, matching the classic codegen lowering. + const auto *wtt = cast<WebAssemblyTableType>(ty); + mlir::Type elemTy = convertTypeForMem(wtt->getElementType()); + resultType = cir::ArrayType::get(elemTy, 0); + break; + } + case Type::ExtVector: case Type::Vector: { const VectorType *vec = cast<VectorType>(ty); diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 7421733efcc24..9de1d3899bb36 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -3974,6 +3974,11 @@ llvm::DIType *CGDebugInfo::CreateType(const PipeType *Ty, llvm::DIFile *U) { return getOrCreateType(Ty->getElementType(), U); } +llvm::DIType *CGDebugInfo::CreateType(const WebAssemblyTableType *Ty, + llvm::DIFile *U) { + return getOrCreateType(Ty->getElementType(), U); +} + llvm::DIType *CGDebugInfo::CreateType(const HLSLAttributedResourceType *Ty, llvm::DIFile *U) { return getOrCreateType(Ty->getWrappedType(), U); @@ -4342,6 +4347,8 @@ llvm::DIType *CGDebugInfo::CreateTypeNode(QualType Ty, llvm::DIFile *Unit) { return CreateType(cast<OverflowBehaviorType>(Ty), Unit); case Type::Pipe: return CreateType(cast<PipeType>(Ty), Unit); + case Type::WebAssemblyTable: + return CreateType(cast<WebAssemblyTableType>(Ty), Unit); case Type::TemplateSpecialization: return CreateType(cast<TemplateSpecializationType>(Ty), Unit); diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h index a818d6471ff3e..5f884139b70e0 100644 --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -245,6 +245,7 @@ class CGDebugInfo { llvm::DIType *CreateType(const MemberPointerType *Ty, llvm::DIFile *F); llvm::DIType *CreateType(const AtomicType *Ty, llvm::DIFile *F); llvm::DIType *CreateType(const PipeType *Ty, llvm::DIFile *F); + llvm::DIType *CreateType(const WebAssemblyTableType *Ty, llvm::DIFile *F); /// Get enumeration type. llvm::DIType *CreateEnumType(const EnumType *Ty); llvm::DIType *CreateTypeDefinition(const EnumType *Ty); diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index b920266b59808..4201f8f536805 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -292,6 +292,7 @@ TypeEvaluationKind CodeGenFunction::getEvaluationKind(QualType type) { case Type::ObjCObject: case Type::ObjCInterface: case Type::ArrayParameter: + case Type::WebAssemblyTable: return TEK_Aggregate; // We operate on atomic values according to their underlying type. @@ -2569,6 +2570,7 @@ void CodeGenFunction::EmitVariablyModifiedType(QualType type) { case Type::BitInt: case Type::HLSLInlineSpirv: case Type::PredefinedSugar: + case Type::WebAssemblyTable: llvm_unreachable("type class is never variably-modified!"); case Type::Adjusted: diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp index 3de3bad6affb5..66f31863b3ffb 100644 --- a/clang/lib/CodeGen/CodeGenTypes.cpp +++ b/clang/lib/CodeGen/CodeGenTypes.cpp @@ -796,6 +796,14 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) { ResultType = CGM.getOpenCLRuntime().getPipeType(cast<PipeType>(Ty)); break; } + case Type::WebAssemblyTable: { + // A WebAssembly table lowers to a zero-length array of its (reference type) + // element, matching the classic array-based lowering. + const auto *WTT = cast<WebAssemblyTableType>(Ty); + llvm::Type *EltTy = ConvertTypeForMem(WTT->getElementType()); + ResultType = llvm::ArrayType::get(EltTy, 0); + break; + } case Type::BitInt: { const auto &EIT = cast<BitIntType>(Ty); ResultType = llvm::Type::getIntNTy(getLLVMContext(), EIT->getNumBits()); diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index b4b3284f752ae..c6d6b675a1b6f 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -3997,6 +3997,9 @@ void ItaniumRTTIBuilder::BuildVTablePointer(const Type *Ty, case Type::Pipe: llvm_unreachable("Pipe types shouldn't get here"); + case Type::WebAssemblyTable: + llvm_unreachable("WebAssembly table types shouldn't get here"); + case Type::ArrayParameter: llvm_unreachable("Array Parameter types should not get here."); @@ -4297,6 +4300,9 @@ llvm::Constant *ItaniumRTTIBuilder::BuildTypeInfo( case Type::Pipe: break; + case Type::WebAssemblyTable: + break; + case Type::BitInt: break; diff --git a/clang/lib/CodeGen/QualTypeMapper.cpp b/clang/lib/CodeGen/QualTypeMapper.cpp index 31d9250a48ec9..d5d38f0ebd8b1 100644 --- a/clang/lib/CodeGen/QualTypeMapper.cpp +++ b/clang/lib/CodeGen/QualTypeMapper.cpp @@ -102,6 +102,12 @@ const llvm::abi::Type *QualTypeMapper::convertTypeImpl(QualType QT) { case Type::BlockPointer: case Type::Pipe: return createPointerTypeForPointee(ASTCtx.VoidPtrTy); + case Type::WebAssemblyTable: { + // A WebAssembly table is a zero-length array of its element type. + const auto *WTT = cast<WebAssemblyTableType>(QT); + return Builder.getArrayType(convertType(WTT->getElementType()), + /*NumElements=*/0, /*Size=*/0); + } case Type::ConstantMatrix: { const auto *MT = cast<ConstantMatrixType>(QT); return Builder.getArrayType(convertType(MT->getElementType()), diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 7c868d176e803..b447eae2cee36 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -4615,6 +4615,7 @@ static void captureVariablyModifiedType(ASTContext &Context, QualType T, case Type::ObjCObjectPointer: case Type::ObjCTypeParam: case Type::Pipe: + case Type::WebAssemblyTable: case Type::BitInt: case Type::HLSLInlineSpirv: llvm_unreachable("type class is never variably-modified!"); diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 65b60964d1192..ef6dc80608d9c 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -3294,6 +3294,7 @@ addAssociatedClassesAndNamespaces(AssociatedLookup &Result, QualType Ty) { // Array parameter types are treated as fundamental types. case Type::ArrayParameter: + case Type::WebAssemblyTable: break; case Type::HLSLAttributedResource: diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 556fa716d61e7..11b02d3e88dfd 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -6464,6 +6464,11 @@ bool UnnamedLocalNoLinkageFinder::VisitPipeType(const PipeType* T) { return false; } +bool UnnamedLocalNoLinkageFinder::VisitWebAssemblyTableType( + const WebAssemblyTableType *T) { + return Visit(T->getElementType()); +} + bool UnnamedLocalNoLinkageFinder::VisitBitIntType(const BitIntType *T) { return false; } diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 26c397afdd6ef..9a18bedcb6e97 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2539,6 +2539,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch( case Type::DeducedTemplateSpecialization: case Type::PackExpansion: case Type::Pipe: + case Type::WebAssemblyTable: case Type::ArrayParameter: case Type::HLSLAttributedResource: case Type::HLSLInlineSpirv: @@ -7066,6 +7067,7 @@ MarkUsedTemplateParameters(ASTContext &Ctx, QualType T, case Type::ObjCObjectPointer: case Type::UnresolvedUsing: case Type::Pipe: + case Type::WebAssemblyTable: case Type::BitInt: case Type::HLSLInlineSpirv: case Type::OverflowBehavior: diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 3b99ff4bb9e23..c90bfde84a9ca 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -1289,6 +1289,10 @@ class TreeTransform { QualType RebuildPipeType(QualType ValueType, SourceLocation KWLoc, bool isReadPipe); + /// Build a new WebAssembly table type given its element type. + QualType RebuildWebAssemblyTableType(QualType ElementType, + SourceLocation KWLoc); + /// Build a bit-precise int given its value type. QualType RebuildBitIntType(bool IsUnsigned, unsigned NumBits, SourceLocation Loc); @@ -7424,6 +7428,28 @@ QualType TreeTransform<Derived>::TransformPipeType(TypeLocBuilder &TLB, return Result; } +template <typename Derived> +QualType TreeTransform<Derived>::TransformWebAssemblyTableType( + TypeLocBuilder &TLB, WebAssemblyTableTypeLoc TL) { + QualType ElementType = getDerived().TransformType(TLB, TL.getValueLoc()); + if (ElementType.isNull()) + return QualType(); + + QualType Result = TL.getType(); + if (getDerived().AlwaysRebuild() || + ElementType != TL.getValueLoc().getType()) { + Result = + getDerived().RebuildWebAssemblyTableType(ElementType, TL.getKWLoc()); + if (Result.isNull()) + return QualType(); + } + + WebAssemblyTableTypeLoc NewTL = TLB.push<WebAssemblyTableTypeLoc>(Result); + NewTL.setKWLoc(TL.getKWLoc()); + + return Result; +} + template <typename Derived> QualType TreeTransform<Derived>::TransformBitIntType(TypeLocBuilder &TLB, BitIntTypeLoc TL) { @@ -17863,6 +17889,13 @@ QualType TreeTransform<Derived>::RebuildPipeType(QualType ValueType, : SemaRef.BuildWritePipeType(ValueType, KWLoc); } +template <typename Derived> +QualType +TreeTransform<Derived>::RebuildWebAssemblyTableType(QualType ElementType, + SourceLocation KWLoc) { + return SemaRef.Context.getWebAssemblyTableType(ElementType); +} + template <typename Derived> QualType TreeTransform<Derived>::RebuildBitIntType(bool IsUnsigned, unsigned NumBits, diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index f8a6a38bb9b5c..9013aa94d76a5 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -7808,6 +7808,10 @@ void TypeLocReader::VisitPipeTypeLoc(PipeTypeLoc TL) { TL.setKWLoc(readSourceLocation()); } +void TypeLocReader::VisitWebAssemblyTableTypeLoc(WebAssemblyTableTypeLoc TL) { + TL.setKWLoc(readSourceLocation()); +} + void TypeLocReader::VisitBitIntTypeLoc(clang::BitIntTypeLoc TL) { TL.setNameLoc(readSourceLocation()); } diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 357a7f7e95fa0..77632d4e65030 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -684,6 +684,9 @@ void TypeLocWriter::VisitAtomicTypeLoc(AtomicTypeLoc TL) { void TypeLocWriter::VisitPipeTypeLoc(PipeTypeLoc TL) { addSourceLocation(TL.getKWLoc()); } +void TypeLocWriter::VisitWebAssemblyTableTypeLoc(WebAssemblyTableTypeLoc TL) { + addSourceLocation(TL.getKWLoc()); +} void TypeLocWriter::VisitBitIntTypeLoc(clang::BitIntTypeLoc TL) { addSourceLocation(TL.getNameLoc()); } diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index ac2fad38a1348..feed6197c68e6 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -1876,6 +1876,10 @@ bool CursorVisitor::VisitPipeTypeLoc(PipeTypeLoc TL) { return Visit(TL.getValueLoc()); } +bool CursorVisitor::VisitWebAssemblyTableTypeLoc(WebAssemblyTableTypeLoc TL) { + return Visit(TL.getValueLoc()); +} + #define DEFAULT_TYPELOC_IMPL(CLASS, PARENT) \ bool CursorVisitor::Visit##CLASS##TypeLoc(CLASS##TypeLoc TL) { \ return Visit##PARENT##Loc(TL); \ diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 82fd9844cf96a..952a2155f23a0 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -4219,6 +4219,8 @@ TypeSystemClang::GetTypeClass(lldb::opaque_compiler_type_t type) { break; case clang::Type::SubstBuiltinTemplatePack: break; + case clang::Type::WebAssemblyTable: + break; } // We don't know hot to display this type... return lldb::eTypeClassOther; @@ -5082,6 +5084,8 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type) { break; case clang::Type::SubstBuiltinTemplatePack: break; + case clang::Type::WebAssemblyTable: + break; } return lldb::eEncodingInvalid; @@ -5252,6 +5256,8 @@ lldb::Format TypeSystemClang::GetFormat(lldb::opaque_compiler_type_t type) { break; case clang::Type::SubstBuiltinTemplatePack: break; + case clang::Type::WebAssemblyTable: + break; } // We don't know hot to display this type... return lldb::eFormatBytes; >From 5f547ad3e0b84c0f5719ef4e906ccb84f999b1ab Mon Sep 17 00:00:00 2001 From: Hood Chatham <[email protected]> Date: Thu, 4 Jun 2026 17:02:32 -0700 Subject: [PATCH 2/2] Use new WebAssemblyTableType in codegen --- clang/include/clang/Sema/SemaInternal.h | 13 +++++ clang/lib/AST/Expr.cpp | 5 ++ clang/lib/AST/Type.cpp | 8 +-- .../CodeGen/TargetBuiltins/WebAssembly.cpp | 26 ++++----- clang/lib/Sema/SemaDecl.cpp | 55 +++++++++++-------- clang/lib/Sema/SemaExpr.cpp | 13 +++-- clang/lib/Sema/SemaExprCXX.cpp | 12 ++-- clang/lib/Sema/SemaInit.cpp | 9 +++ clang/lib/Sema/SemaStmt.cpp | 11 ++-- clang/lib/Sema/SemaType.cpp | 32 +++++++++-- clang/lib/Sema/SemaWasm.cpp | 6 +- clang/test/Sema/wasm-refs-and-tables.c | 6 +- clang/test/SemaCXX/wasm-refs-and-tables.cpp | 2 +- 13 files changed, 129 insertions(+), 69 deletions(-) diff --git a/clang/include/clang/Sema/SemaInternal.h b/clang/include/clang/Sema/SemaInternal.h index 8f6041b5f00e4..f847d98d6c162 100644 --- a/clang/include/clang/Sema/SemaInternal.h +++ b/clang/include/clang/Sema/SemaInternal.h @@ -35,6 +35,19 @@ FTIHasNonVoidParameters(const DeclaratorChunk::FunctionTypeInfo &FTI) { return FTI.NumParams && !FTIHasSingleVoidParameter(FTI); } +/// Determine whether \p T is a WebAssembly table. A valid table is represented +/// by a WebAssemblyTableType; an array of a WebAssembly reference type that is +/// not a valid table (e.g. has a non-zero or unspecified length) remains an +/// ArrayType and is matched here so that it can be diagnosed. +inline bool isWebAssemblyTableOrRefArrayType(const ASTContext &Ctx, + QualType T) { + if (T->isWebAssemblyTableType()) + return true; + if (const auto *ATy = Ctx.getAsArrayType(T)) + return ATy->getElementType().isWebAssemblyReferenceType(); + return false; +} + // Helper function to check whether D's attributes match current CUDA mode. // Decls with mismatched attributes and related diagnostics may have to be // ignored during this CUDA compilation pass. diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 1bafc4708a30b..0b01a24ae79ab 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -3444,6 +3444,11 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef, return true; } + // A WebAssembly table is a zero-length aggregate; an initializer + // list for it must be empty so is always constant. + if (ILE->getType()->isWebAssemblyTableType()) + return true; + if (ILE->getType()->isRecordType()) { unsigned ElementNo = 0; auto *RD = ILE->getType()->castAsRecordDecl(); diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 329abcceb2c98..a0315e1eb5386 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2649,13 +2649,7 @@ bool Type::isWebAssemblyExternrefType() const { } bool Type::isWebAssemblyTableType() const { - if (const auto *ATy = dyn_cast<ArrayType>(this)) - return ATy->getElementType().isWebAssemblyReferenceType(); - - if (const auto *PTy = dyn_cast<PointerType>(this)) - return PTy->getPointeeType().isWebAssemblyReferenceType(); - - return false; + return isa<WebAssemblyTableType>(CanonicalType); } bool Type::isSizelessType() const { return isSizelessBuiltinType(); } diff --git a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp index e7bdb91d49ce8..217a643469773 100644 --- a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp +++ b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp @@ -589,8 +589,8 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID, return Builder.CreateCall(Callee, {Vector, Index, Val}); } case WebAssembly::BI__builtin_wasm_table_get: { - assert(E->getArg(0)->getType()->isArrayType()); - Value *Table = EmitArrayToPointerDecay(E->getArg(0)).emitRawPointer(*this); + assert(E->getArg(0)->getType()->isWebAssemblyTableType()); + Value *Table = EmitLValue(E->getArg(0)).emitRawPointer(*this); Value *Index = EmitScalarExpr(E->getArg(1)); Function *Callee; if (E->getType().isWebAssemblyExternrefType()) @@ -603,8 +603,8 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID, return Builder.CreateCall(Callee, {Table, Index}); } case WebAssembly::BI__builtin_wasm_table_set: { - assert(E->getArg(0)->getType()->isArrayType()); - Value *Table = EmitArrayToPointerDecay(E->getArg(0)).emitRawPointer(*this); + assert(E->getArg(0)->getType()->isWebAssemblyTableType()); + Value *Table = EmitLValue(E->getArg(0)).emitRawPointer(*this); Value *Index = EmitScalarExpr(E->getArg(1)); Value *Val = EmitScalarExpr(E->getArg(2)); Function *Callee; @@ -618,14 +618,14 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID, return Builder.CreateCall(Callee, {Table, Index, Val}); } case WebAssembly::BI__builtin_wasm_table_size: { - assert(E->getArg(0)->getType()->isArrayType()); - Value *Value = EmitArrayToPointerDecay(E->getArg(0)).emitRawPointer(*this); + assert(E->getArg(0)->getType()->isWebAssemblyTableType()); + Value *Value = EmitLValue(E->getArg(0)).emitRawPointer(*this); Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_table_size); return Builder.CreateCall(Callee, Value); } case WebAssembly::BI__builtin_wasm_table_grow: { - assert(E->getArg(0)->getType()->isArrayType()); - Value *Table = EmitArrayToPointerDecay(E->getArg(0)).emitRawPointer(*this); + assert(E->getArg(0)->getType()->isWebAssemblyTableType()); + Value *Table = EmitLValue(E->getArg(0)).emitRawPointer(*this); Value *Val = EmitScalarExpr(E->getArg(1)); Value *NElems = EmitScalarExpr(E->getArg(2)); @@ -641,8 +641,8 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID, return Builder.CreateCall(Callee, {Table, Val, NElems}); } case WebAssembly::BI__builtin_wasm_table_fill: { - assert(E->getArg(0)->getType()->isArrayType()); - Value *Table = EmitArrayToPointerDecay(E->getArg(0)).emitRawPointer(*this); + assert(E->getArg(0)->getType()->isWebAssemblyTableType()); + Value *Table = EmitLValue(E->getArg(0)).emitRawPointer(*this); Value *Index = EmitScalarExpr(E->getArg(1)); Value *Val = EmitScalarExpr(E->getArg(2)); Value *NElems = EmitScalarExpr(E->getArg(3)); @@ -659,9 +659,9 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID, return Builder.CreateCall(Callee, {Table, Index, Val, NElems}); } case WebAssembly::BI__builtin_wasm_table_copy: { - assert(E->getArg(0)->getType()->isArrayType()); - Value *TableX = EmitArrayToPointerDecay(E->getArg(0)).emitRawPointer(*this); - Value *TableY = EmitArrayToPointerDecay(E->getArg(1)).emitRawPointer(*this); + assert(E->getArg(0)->getType()->isWebAssemblyTableType()); + Value *TableX = EmitLValue(E->getArg(0)).emitRawPointer(*this); + Value *TableY = EmitLValue(E->getArg(1)).emitRawPointer(*this); Value *DstIdx = EmitScalarExpr(E->getArg(2)); Value *SrcIdx = EmitScalarExpr(E->getArg(3)); Value *NElems = EmitScalarExpr(E->getArg(4)); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index d45c3eb35094f..8d9642ca8ac20 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -8226,16 +8226,15 @@ NamedDecl *Sema::ActOnVariableDeclarator( } } - // WebAssembly tables are always in address space 1 (wasm_var). Don't apply - // address space if the table has local storage (semantic checks elsewhere - // will produce an error anyway). - if (const auto *ATy = dyn_cast<ArrayType>(NewVD->getType())) { - if (ATy && ATy->getElementType().isWebAssemblyReferenceType() && - !NewVD->hasLocalStorage()) { - QualType Type = Context.getAddrSpaceQualType( - NewVD->getType(), Context.getLangASForBuiltinAddressSpace(1)); - NewVD->setType(Type); - } + // A WebAssemblyTableType is always in address space 1 (wasm_var). Don't apply + // the address space if the declaration has local storage (semantic checks + // elsewhere will produce an error anyway). + QualType VarTy = NewVD->getType(); + bool IsWasmTable = VarTy->isWebAssemblyTableType(); + if (IsWasmTable && !NewVD->hasLocalStorage()) { + QualType Type = Context.getAddrSpaceQualType( + VarTy, Context.getLangASForBuiltinAddressSpace(1)); + NewVD->setType(Type); } LoadExternalExtnameUndeclaredIdentifiers(); @@ -8376,7 +8375,8 @@ NamedDecl *Sema::ActOnVariableDeclarator( // error like WebAssembly tables being declared as arrays with a non-zero // size, but then parsing continues and emits further errors on that line. // To avoid that we check here if it happened and return nullptr. - if (NewVD->getType()->isWebAssemblyTableType() && NewVD->isInvalidDecl()) + if (isWebAssemblyTableOrRefArrayType(Context, NewVD->getType()) && + NewVD->isInvalidDecl()) return nullptr; if (NewTemplate) { @@ -9060,8 +9060,15 @@ void Sema::CheckVariableDeclarationType(VarDecl *NewVD) { } // WebAssembly tables must be static with a zero length and can't be - // declared within functions. - if (T->isWebAssemblyTableType()) { + // declared within functions. A valid table is represented by a + // WebAssemblyTableType; an array of a WebAssembly reference type that is not + // a valid table (e.g. has a non-zero or unspecified length) is detected here + // so that it can be diagnosed. + bool IsWasmTable = T->isWebAssemblyTableType(); + bool IsWasmRefArray = false; + if (const auto *ATy = Context.getAsArrayType(T)) + IsWasmRefArray = ATy->getElementType().isWebAssemblyReferenceType(); + if (IsWasmTable || IsWasmRefArray) { if (getCurScope()->getParent()) { // Parent is null at top-level Diag(NewVD->getLocation(), diag::err_wasm_table_in_function); NewVD->setInvalidDecl(); @@ -9072,8 +9079,7 @@ void Sema::CheckVariableDeclarationType(VarDecl *NewVD) { NewVD->setInvalidDecl(); return; } - const auto *ATy = dyn_cast<ConstantArrayType>(T.getTypePtr()); - if (!ATy || ATy->getZExtSize() != 0) { + if (!IsWasmTable) { Diag(NewVD->getLocation(), diag::err_typecheck_wasm_table_must_have_zero_length); NewVD->setInvalidDecl(); @@ -11230,13 +11236,14 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, } } } - // WebAssembly tables can't be used as function parameters. - if (Context.getTargetInfo().getTriple().isWasm()) { - if (PT->getUnqualifiedDesugaredType()->isWebAssemblyTableType()) { - Diag(Param->getTypeSpecStartLoc(), - diag::err_wasm_table_as_function_parameter); - D.setInvalidType(); - } + // WebAssembly tables can't be used as function parameters. A parameter + // declared as an array of a reference type decays to a pointer, so check + // the pre-decay type as well. + if (Context.getTargetInfo().getTriple().isWasm() && + isWebAssemblyTableOrRefArrayType(Context, Param->getOriginalType())) { + Diag(Param->getTypeSpecStartLoc(), + diag::err_wasm_table_as_function_parameter); + D.setInvalidType(); } } @@ -19401,6 +19408,10 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T, } QualType EltTy = Context.getBaseElementType(T); + // A WebAssembly table cannot be a struct or union member. Diagnose it via its + // (sizeless) reference element type. + if (const auto *WTT = EltTy->getAs<WebAssemblyTableType>()) + EltTy = WTT->getElementType(); if (!EltTy->isDependentType() && !EltTy->containsErrors()) { bool isIncomplete = LangOpts.HLSL // HLSL allows sizeless builtin types diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index b447eae2cee36..dcfa061ef467f 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -8885,6 +8885,14 @@ QualType Sema::CheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, if (!RHSResult.isUsable()) return QualType(); RHS = RHSResult; + // A WebAssembly table cannot be used as the condition of a conditional + // expression. + if (Cond.get()->getType()->isWebAssemblyTableType()) { + Diag(QuestionLoc, diag::err_wasm_table_conditional_expression) + << Cond.get()->getSourceRange(); + return QualType(); + } + // C++ is sufficiently different to merit its own checker. if (getLangOpts().CPlusPlus) return CXXCheckConditionalOperands(Cond, LHS, RHS, VK, OK, QuestionLoc); @@ -13929,10 +13937,7 @@ inline QualType Sema::CheckLogicalOperands(ExprResult &LHS, ExprResult &RHS, // WebAssembly tables can't be used with logical operators. QualType LHSTy = LHS.get()->getType(); QualType RHSTy = RHS.get()->getType(); - const auto *LHSATy = dyn_cast<ArrayType>(LHSTy); - const auto *RHSATy = dyn_cast<ArrayType>(RHSTy); - if ((LHSATy && LHSATy->getElementType().isWebAssemblyReferenceType()) || - (RHSATy && RHSATy->getElementType().isWebAssemblyReferenceType())) { + if (LHSTy->isWebAssemblyTableType() || RHSTy->isWebAssemblyTableType()) { return InvalidOperands(Loc, LHS, RHS); } diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 4e1652462b3ae..f58c95567779c 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -980,15 +980,15 @@ bool Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc, isPointer = true; } - // Cannot throw WebAssembly reference type. - if (Ty.isWebAssemblyReferenceType()) { - Diag(ThrowLoc, diag::err_wasm_reftype_tc) << 0 << E->getSourceRange(); + // Cannot throw a WebAssembly table. + if (ExceptionObjectTy->isWebAssemblyTableType()) { + Diag(ThrowLoc, diag::err_wasm_table_art) << 2 << E->getSourceRange(); return true; } - // Cannot throw WebAssembly table. - if (isPointer && Ty.isWebAssemblyReferenceType()) { - Diag(ThrowLoc, diag::err_wasm_table_art) << 2 << E->getSourceRange(); + // Cannot throw WebAssembly reference type. + if (Ty.isWebAssemblyReferenceType()) { + Diag(ThrowLoc, diag::err_wasm_reftype_tc) << 0 << E->getSourceRange(); return true; } diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 8f685feac4beb..44df438539846 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -1486,6 +1486,15 @@ void InitListChecker::CheckListElementTypes(const InitializedEntity &Entity, // Checks for scalar type are sufficient for these types too. CheckScalarType(Entity, IList, DeclType, Index, StructuredList, StructuredIndex); + } else if (DeclType->isWebAssemblyTableType()) { + // A WebAssembly table is a zero-length aggregate; only an empty initializer + // list (e.g. `= {}`) is valid. + if (IList->getNumInits() != 0) { + if (!VerifyOnly) + SemaRef.Diag(IList->getBeginLoc(), diag::err_illegal_initializer_type) + << DeclType; + hadError = true; + } } else if (DeclType->isDependentType()) { // C++ [over.match.class.deduct]p1.5: // brace elision is not considered for any aggregate element that has a diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 27fee88d20c60..98e2f184b4825 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -35,6 +35,7 @@ #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaCUDA.h" #include "clang/Sema/SemaHLSL.h" +#include "clang/Sema/SemaInternal.h" #include "clang/Sema/SemaObjC.h" #include "clang/Sema/SemaOpenMP.h" #include "llvm/ADT/ArrayRef.h" @@ -4034,12 +4035,10 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp, } else // If we don't have a function/method context, bail. return StmtError(); - if (RetValExp) { - const auto *ATy = dyn_cast<ArrayType>(RetValExp->getType()); - if (ATy && ATy->getElementType().isWebAssemblyReferenceType()) { - Diag(ReturnLoc, diag::err_wasm_table_art) << 1; - return StmtError(); - } + if (RetValExp && + isWebAssemblyTableOrRefArrayType(Context, RetValExp->getType())) { + Diag(ReturnLoc, diag::err_wasm_table_art) << 1; + return StmtError(); } // C++1z: discarded return statements are not considered when deducing a diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index d2bb312feadc1..d9c8f035f95be 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -2135,13 +2135,20 @@ QualType Sema::BuildArrayType(QualType T, ArraySizeModifier ASM, return QualType(); } - // Multi-dimensional arrays of WebAssembly references are not allowed. - if (Context.getTargetInfo().getTriple().isWasm() && T->isArrayType()) { - const auto *ATy = dyn_cast<ArrayType>(T); - if (ATy && ATy->getElementType().isWebAssemblyReferenceType()) { + // Multi-dimensional arrays of WebAssembly references are not allowed. The + // element type is either an array whose element is a reference type, or a + // WebAssembly table (a zero-length reference array already lowered to a + // WebAssemblyTableType). + if (Context.getTargetInfo().getTriple().isWasm()) { + if (T->isWebAssemblyTableType()) { Diag(Loc, diag::err_wasm_reftype_multidimensional_array); return QualType(); } + if (const auto *ATy = dyn_cast<ArrayType>(T)) + if (ATy->getElementType().isWebAssemblyReferenceType()) { + Diag(Loc, diag::err_wasm_reftype_multidimensional_array); + return QualType(); + } } if (T->isSizelessType() && !T.isWebAssemblyReferenceType()) { @@ -2363,6 +2370,18 @@ QualType Sema::BuildArrayType(QualType T, ArraySizeModifier ASM, } } + // On WebAssembly, a zero-length array of a reference type is a table. Give it + // a dedicated WebAssemblyTableType so that it is distinguished from an + // ordinary array. WebAssembly tables are not first-class: a table object may + // only be created by a global declaration and used as an argument to the + // table builtins. Other uses (locals, parameters, return values, struct or + // union members, taking a pointer, sizeof, ...) are rejected by the + // WebAssemblyTableType checks elsewhere. + if (const auto *CAT = dyn_cast<ConstantArrayType>(T)) + if (CAT->getZExtSize() == 0 && + CAT->getElementType().isWebAssemblyReferenceType()) + T = Context.getWebAssemblyTableType(CAT->getElementType()); + return T; } @@ -6301,6 +6320,11 @@ namespace { TL.setRBracketLoc(Chunk.EndLoc); TL.setSizeExpr(static_cast<Expr*>(Chunk.Arr.NumElts)); } + void VisitWebAssemblyTableTypeLoc(WebAssemblyTableTypeLoc TL) { + // A WebAssembly table is formed from a zero-length array declarator. + assert(Chunk.Kind == DeclaratorChunk::Array); + TL.setKWLoc(Chunk.Loc); + } void VisitFunctionTypeLoc(FunctionTypeLoc TL) { assert(Chunk.Kind == DeclaratorChunk::Function); TL.setLocalRangeBegin(Chunk.Loc); diff --git a/clang/lib/Sema/SemaWasm.cpp b/clang/lib/Sema/SemaWasm.cpp index 083460eb6e201..f72e19c13aabb 100644 --- a/clang/lib/Sema/SemaWasm.cpp +++ b/clang/lib/Sema/SemaWasm.cpp @@ -30,13 +30,13 @@ SemaWasm::SemaWasm(Sema &S) : SemaBase(S) {} static bool CheckWasmBuiltinArgIsTable(Sema &S, CallExpr *E, unsigned ArgIndex, QualType &ElTy) { Expr *ArgExpr = E->getArg(ArgIndex); - const auto *ATy = dyn_cast<ArrayType>(ArgExpr->getType()); - if (!ATy || !ATy->getElementType().isWebAssemblyReferenceType()) { + const auto *TTy = ArgExpr->getType()->getAs<WebAssemblyTableType>(); + if (!TTy) { return S.Diag(ArgExpr->getBeginLoc(), diag::err_wasm_builtin_arg_must_be_table_type) << ArgIndex + 1 << ArgExpr->getSourceRange(); } - ElTy = ATy->getElementType(); + ElTy = TTy->getElementType(); return false; } diff --git a/clang/test/Sema/wasm-refs-and-tables.c b/clang/test/Sema/wasm-refs-and-tables.c index dd8536c52cd03..6cfc8a8664535 100644 --- a/clang/test/Sema/wasm-refs-and-tables.c +++ b/clang/test/Sema/wasm-refs-and-tables.c @@ -74,7 +74,7 @@ __externref_t func(__externref_t ref) { _Alignof(ref); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}} _Alignof(__externref_t); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}} _Alignof(__externref_t[]); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}} - _Alignof(__externref_t[0]); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}} + _Alignof(__externref_t[0]); // expected-error {{invalid application of 'alignof' to WebAssembly table}} _Alignof(table); // expected-warning {{'_Alignof' applied to an expression is a GNU extension}} expected-error {{invalid application of 'alignof' to WebAssembly table}} _Alignof(__externref_t[0][0]); // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}} _Alignof(__externref_t *); // expected-error {{pointer to WebAssembly reference type is not allowed}} @@ -92,8 +92,8 @@ __externref_t func(__externref_t ref) { table == 1; // expected-error {{invalid operands to binary expression ('__attribute__((address_space(1))) __externref_t[0]' and 'int')}} 1 >= table; // expected-error {{invalid operands to binary expression ('int' and '__attribute__((address_space(1))) __externref_t[0]')}} table == other_table; // expected-error {{invalid operands to binary expression ('__attribute__((address_space(1))) __externref_t[0]' and '__attribute__((address_space(1))) __externref_t[0]')}} - table !=- table; // expected-error {{invalid argument type '__attribute__((address_space(1))) __externref_t *' to unary expression}} - !table; // expected-error {{invalid argument type '__attribute__((address_space(1))) __externref_t *' to unary expression}} + table !=- table; // expected-error {{invalid argument type '__externref_t[0]' to unary expression}} + !table; // expected-error {{invalid argument type '__externref_t[0]' to unary expression}} 1 && table; // expected-error {{invalid operands to binary expression ('int' and '__attribute__((address_space(1))) __externref_t[0]')}} table || 1; // expected-error {{invalid operands to binary expression ('__attribute__((address_space(1))) __externref_t[0]' and 'int')}} 1 ? table : table; // expected-error {{cannot use a WebAssembly table within a branch of a conditional expression}} diff --git a/clang/test/SemaCXX/wasm-refs-and-tables.cpp b/clang/test/SemaCXX/wasm-refs-and-tables.cpp index 3d6ead07fd530..c8f16b09aa4e8 100644 --- a/clang/test/SemaCXX/wasm-refs-and-tables.cpp +++ b/clang/test/SemaCXX/wasm-refs-and-tables.cpp @@ -19,7 +19,7 @@ __externref_t func(__externref_t ref) throw(__externref_t) { // expected-error #endif void *ret_void_ptr() { - throw table; // expected-error {{cannot throw a WebAssembly reference type}} + throw table; // expected-error {{cannot throw a WebAssembly table}} throw r1; // expected-error {{cannot throw a WebAssembly reference type}} try {} catch (__externref_t T) { // expected-error {{cannot catch a WebAssembly reference type}} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
