Index: lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h =================================================================== --- lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -59,6 +59,7 @@ DotResult = UINT64_MAX - 1, ///< .result DotLocal = UINT64_MAX - 2, ///< .local DotEndFunc = UINT64_MAX - 3, ///< .endfunc + DotIndIdx = UINT64_MAX - 4, /// < .indidx }; } // end namespace WebAssembly @@ -123,7 +124,8 @@ case WebAssembly::STORE_I64: case WebAssembly::STORE_F64: return 3; - default: llvm_unreachable("Only loads and stores have p2align values"); + default: + llvm_unreachable("Only loads and stores have p2align values"); } } Index: lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h =================================================================== --- lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h +++ lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h @@ -43,6 +43,8 @@ size_t NumResults) { llvm_unreachable("emitIndirectFunctionType not implemented"); } + /// .indidx + virtual void emitIndIdx(const MCExpr *Value) = 0; }; /// This part is for ascii assembly output @@ -59,6 +61,7 @@ void emitIndirectFunctionType(StringRef name, SmallVectorImpl &SignatureVTs, size_t NumResults) override; + void emitIndIdx(const MCExpr *Value) override; }; /// This part is for ELF object output @@ -70,6 +73,7 @@ void emitResult(ArrayRef Types) override; void emitLocal(ArrayRef Types) override; void emitEndFunc() override; + void emitIndIdx(const MCExpr *Value) override; }; } // end namespace llvm Index: lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp =================================================================== --- lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp +++ lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp @@ -67,13 +67,18 @@ void WebAssemblyTargetAsmStreamer::emitIndirectFunctionType( StringRef name, SmallVectorImpl &SignatureVTs, size_t NumResults) { OS << "\t.functype\t" << name; - if (NumResults == 0) OS << ", void"; + if (NumResults == 0) + OS << ", void"; for (auto Ty : SignatureVTs) { OS << ", " << WebAssembly::TypeToString(Ty); } OS << "\n"; } +void WebAssemblyTargetAsmStreamer::emitIndIdx(const MCExpr *Value) { + OS << "\t.indidx \t" << *Value << '\n'; +} + // FIXME: What follows is not the real binary encoding. static void EncodeTypes(MCStreamer &Streamer, ArrayRef Types) { @@ -100,3 +105,8 @@ void WebAssemblyTargetELFStreamer::emitEndFunc() { Streamer.EmitIntValue(WebAssembly::DotEndFunc, sizeof(uint64_t)); } + +void WebAssemblyTargetELFStreamer::emitIndIdx(const MCExpr *Value) { + Streamer.EmitIntValue(WebAssembly::DotIndIdx, sizeof(uint64_t)); + Streamer.EmitValue(Value, sizeof(uint64_t)); +} Index: lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -14,10 +14,10 @@ /// //===----------------------------------------------------------------------===// -#include "WebAssembly.h" #include "InstPrinter/WebAssemblyInstPrinter.h" #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" #include "MCTargetDesc/WebAssemblyTargetStreamer.h" +#include "WebAssembly.h" #include "WebAssemblyMCInstLower.h" #include "WebAssemblyMachineFunctionInfo.h" #include "WebAssemblyRegisterInfo.h" @@ -183,6 +183,15 @@ SmallVector ResultVTs; const Function &F(*MF->getFunction()); + + // Emit the function index. + if (MDNode *Idx = F.getMetadata("wasm.index")) { + assert(Idx->getNumOperands() == 1); + + getTargetStreamer()->emitIndIdx(AsmPrinter::lowerConstant( + cast(Idx->getOperand(0))->getValue())); + } + ComputeLegalValueVTs(F, TM, F.getReturnType(), ResultVTs); // If the return type needs to be legalized it will get converted into Index: lib/Transforms/IPO/LowerTypeTests.cpp =================================================================== --- lib/Transforms/IPO/LowerTypeTests.cpp +++ lib/Transforms/IPO/LowerTypeTests.cpp @@ -13,7 +13,6 @@ //===----------------------------------------------------------------------===// #include "llvm/Transforms/IPO/LowerTypeTests.h" -#include "llvm/Transforms/IPO.h" #include "llvm/ADT/EquivalenceClasses.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/Triple.h" @@ -30,6 +29,7 @@ #include "llvm/Pass.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/IPO.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" using namespace llvm; @@ -79,8 +79,7 @@ if (!Result) return false; COffset += APOffset.getZExtValue(); - return containsValue(DL, GlobalLayout, GEP->getPointerOperand(), - COffset); + return containsValue(DL, GlobalLayout, GEP->getPointerOperand(), COffset); } if (auto Op = dyn_cast(V)) { @@ -222,6 +221,9 @@ IntegerType *Int64Ty; IntegerType *IntPtrTy; + // Indirect function call index assignment counter for WebAssembly + uint64_t IndirectIndex; + // Mapping from type identifiers to the call sites that test them. DenseMap> TypeTestCallSites; @@ -250,6 +252,10 @@ void verifyTypeMDNode(GlobalObject *GO, MDNode *Type); void buildBitSetsFromFunctions(ArrayRef TypeIds, ArrayRef Functions); + void buildBitSetsFromFunctionsX86(ArrayRef TypeIds, + ArrayRef Functions); + void buildBitSetsFromFunctionsWASM(ArrayRef TypeIds, + ArrayRef Functions); void buildBitSetsFromDisjointSet(ArrayRef TypeIds, ArrayRef Globals); bool lower(); @@ -267,8 +273,7 @@ /// Build a bit set for TypeId using the object layouts in /// GlobalLayout. BitSetInfo LowerTypeTests::buildBitSet( - Metadata *TypeId, - const DenseMap &GlobalLayout) { + Metadata *TypeId, const DenseMap &GlobalLayout) { BitSetBuilder BSB; // Compute the byte offset of each address associated with this type @@ -281,8 +286,9 @@ if (Type->getOperand(1) != TypeId) continue; uint64_t Offset = - cast(cast(Type->getOperand(0)) - ->getValue())->getZExtValue(); + cast( + cast(Type->getOperand(0))->getValue()) + ->getZExtValue(); BSB.addOffset(GlobalAndOffset.second + Offset); } } @@ -311,8 +317,8 @@ // we know the offset and mask to use. auto ByteArrayGlobal = new GlobalVariable( *M, Int8Ty, /*isConstant=*/true, GlobalValue::PrivateLinkage, nullptr); - auto MaskGlobal = new GlobalVariable( - *M, Int8Ty, /*isConstant=*/true, GlobalValue::PrivateLinkage, nullptr); + auto MaskGlobal = new GlobalVariable(*M, Int8Ty, /*isConstant=*/true, + GlobalValue::PrivateLinkage, nullptr); ByteArrayInfos.emplace_back(); ByteArrayInfo *BAI = &ByteArrayInfos.back(); @@ -588,8 +594,7 @@ void LowerTypeTests::verifyTypeMDNode(GlobalObject *GO, MDNode *Type) { if (Type->getNumOperands() != 2) - report_fatal_error( - "All operands of type metadata must have 2 elements"); + report_fatal_error("All operands of type metadata must have 2 elements"); if (GO->isThreadLocal()) report_fatal_error("Bit set element may not be thread-local"); @@ -612,9 +617,6 @@ static const unsigned kX86JumpTableEntrySize = 8; unsigned LowerTypeTests::getJumpTableEntrySize() { - if (Arch != Triple::x86 && Arch != Triple::x86_64) - report_fatal_error("Unsupported architecture for jump tables"); - return kX86JumpTableEntrySize; } @@ -625,9 +627,6 @@ Constant *LowerTypeTests::createJumpTableEntry(GlobalObject *Src, Function *Dest, unsigned Distance) { - if (Arch != Triple::x86 && Arch != Triple::x86_64) - report_fatal_error("Unsupported architecture for jump tables"); - const unsigned kJmpPCRel32Code = 0xe9; const unsigned kInt3Code = 0xcc; @@ -652,18 +651,27 @@ } Type *LowerTypeTests::getJumpTableEntryType() { - if (Arch != Triple::x86 && Arch != Triple::x86_64) - report_fatal_error("Unsupported architecture for jump tables"); - return StructType::get(M->getContext(), {Int8Ty, Int32Ty, Int8Ty, Int8Ty, Int8Ty}, /*Packed=*/true); } -/// Given a disjoint set of type identifiers and functions, build a jump table -/// for the functions, build the bit sets and lower the llvm.type.test calls. +/// Given a disjoint set of type identifiers and functions, build the bit sets +/// and lower the llvm.type.test calls, architecture dependently. void LowerTypeTests::buildBitSetsFromFunctions(ArrayRef TypeIds, ArrayRef Functions) { + if (Arch == Triple::x86 || Arch == Triple::x86_64) + buildBitSetsFromFunctionsX86(TypeIds, Functions); + else if (Arch == Triple::wasm32 || Arch == Triple::wasm64) + buildBitSetsFromFunctionsWASM(TypeIds, Functions); + else + report_fatal_error("Unsupported architecture for jump tables"); +} + +/// Given a disjoint set of type identifiers and functions, build a jump table +/// for the functions, build the bit sets and lower the llvm.type.test calls. +void LowerTypeTests::buildBitSetsFromFunctionsX86( + ArrayRef TypeIds, ArrayRef Functions) { // Unlike the global bitset builder, the function bitset builder cannot // re-arrange functions in a particular order and base its calculations on the // layout of the functions' entry points, as we have no idea how large a @@ -795,6 +803,40 @@ ConstantArray::get(JumpTableType, JumpTableEntries)); } +/// Assign a dummy layout using an incrementing counter, tag each function +/// with its index represented as metadata, and lower each type test to an +/// integer range comparison. During generation of the indirect function call +/// table in the backend, it will assign the given indexes. +/// Note: Dynamic linking is not supported, as the WebAssembly ABI has not yet +/// been finalized. +void LowerTypeTests::buildBitSetsFromFunctionsWASM( + ArrayRef TypeIds, ArrayRef Functions) { + assert(!Functions.empty()); + + // Build consecutive monotonic integer ranges for each call target set + DenseMap GlobalLayout; + for (Function *F : Functions) { + // Skip functions that are not address taken, to avoid bloating the table + if (!F->hasAddressTaken()) + continue; + + // Store metadata with the index for each function + MDNode *MD = MDNode::get(F->getContext(), + ArrayRef(ConstantAsMetadata::get( + ConstantInt::get(Int64Ty, IndirectIndex)))); + F->setMetadata("wasm.index", MD); + + // Assign the counter value + GlobalLayout[F] = IndirectIndex++; + } + + // The indirect function table index space starts at zero, so pass a NULL + // pointer as the subtracted "jump table" offset. + lowerTypeTestCalls(TypeIds, + ConstantPointerNull::get(cast(Int32PtrTy)), + GlobalLayout); +} + void LowerTypeTests::buildBitSetsFromDisjointSet( ArrayRef TypeIds, ArrayRef Globals) { llvm::DenseMap TypeIdIndices; @@ -900,8 +942,7 @@ auto BitSetMDVal = dyn_cast(CI->getArgOperand(1)); if (!BitSetMDVal) - report_fatal_error( - "Second argument of llvm.type.test must be metadata"); + report_fatal_error("Second argument of llvm.type.test must be metadata"); auto BitSet = BitSetMDVal->getMetadata(); // Add the call site to the list of call sites for this type identifier. We @@ -939,7 +980,8 @@ for (GlobalClassesTy::iterator I = GlobalClasses.begin(), E = GlobalClasses.end(); I != E; ++I) { - if (!I->isLeader()) continue; + if (!I->isLeader()) + continue; ++NumTypeIdDisjointSets; unsigned MaxIndex = 0; @@ -1000,6 +1042,7 @@ LTT->Int64Ty = Type::getInt64Ty(M.getContext()); LTT->IntPtrTy = DL.getIntPtrType(M.getContext(), 0); LTT->TypeTestCallSites.clear(); + LTT->IndirectIndex = 0; } bool LowerTypeTests::runOnModule(Module &M) { Index: test/CodeGen/WebAssembly/cfi.ll =================================================================== --- /dev/null +++ test/CodeGen/WebAssembly/cfi.ll @@ -0,0 +1,53 @@ +; RUN: opt -S -lowertypetests < %s | llc -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s + +; Tests that we correctly assign indexes for control flow integrity. + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +@0 = private unnamed_addr constant [2 x void (...)*] [void (...)* bitcast (void ()* @f to void (...)*), void (...)* bitcast (void ()* @g to void (...)*)], align 16 + +; CHECK-LABEL: h: +; CHECK-NOT: .indidx +define void @h() !type !0 { + ret void +} + +; CHECK-LABEL: f: +; CHECK: .indidx 0 +define void @f() !type !0 { + ret void +} + +; CHECK-LABEL: g: +; CHECK: .indidx 1 +define void @g() !type !1 { + ret void +} + +!0 = !{i32 0, !"typeid1"} +!1 = !{i32 0, !"typeid2"} + +declare i1 @llvm.type.test(i8* %ptr, metadata %bitset) nounwind readnone +declare void @llvm.trap() nounwind noreturn + +; CHECK-LABEL: foo: +; CHECK: br_if +; CHECK: br_if +; CHECK: unreachable +define i1 @foo(i8* %p) { + %x = call i1 @llvm.type.test(i8* %p, metadata !"typeid1") + br i1 %x, label %contx, label %trap + +trap: + tail call void @llvm.trap() #1 + unreachable + +contx: + %y = call i1 @llvm.type.test(i8* %p, metadata !"typeid2") + br i1 %y, label %conty, label %trap + +conty: + %z = add i1 %x, %y + ret i1 %z +} Index: test/Transforms/LowerTypeTests/function-disjoint.ll =================================================================== --- /dev/null +++ test/Transforms/LowerTypeTests/function-disjoint.ll @@ -0,0 +1,45 @@ +; RUN: opt -S -lowertypetests -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck --check-prefix=X64 %s +; RUN: opt -S -lowertypetests -mtriple=wasm32-unknown-unknown < %s | FileCheck --check-prefix=WASM32 %s + +; Tests that we correctly handle bitsets with disjoint call target sets. + +target datalayout = "e-p:64:64" + +; X64: @[[JT0:.*]] = private constant [1 x <{ i8, i32, i8, i8, i8 }>] [<{ i8, i32, i8, i8, i8 }> <{ i8 -23, i32 trunc (i64 sub (i64 sub (i64 ptrtoint (void ()* @[[FNAME:.*]] to i64), i64 ptrtoint ([1 x <{ i8, i32, i8, i8, i8 }>]* @[[JT0]] to i64)), i64 5) to i32), i8 -52, i8 -52, i8 -52 }>], section ".text" +; X64: @[[JT1:.*]] = private constant [1 x <{ i8, i32, i8, i8, i8 }>] [<{ i8, i32, i8, i8, i8 }> <{ i8 -23, i32 trunc (i64 sub (i64 sub (i64 ptrtoint (void ()* @[[GNAME:.*]] to i64), i64 ptrtoint ([1 x <{ i8, i32, i8, i8, i8 }>]* @[[JT1]] to i64)), i64 5) to i32), i8 -52, i8 -52, i8 -52 }>], section ".text" +; WASM32: private constant [0 x i8] zeroinitializer +@0 = private unnamed_addr constant [2 x void ()*] [void ()* @f, void ()* @g], align 16 + +; X64: @f = alias void (), bitcast ([1 x <{ i8, i32, i8, i8, i8 }>]* @[[JT0]] to void ()*) +; X64: @g = alias void (), bitcast ([1 x <{ i8, i32, i8, i8, i8 }>]* @[[JT1]] to void ()*) + +; X64: define private void @[[FNAME]]() +; WASM32: define void @f() !type !{{[0-9]+}} !wasm.index ![[I0:[0-9]+]] +define void @f() !type !0 { + ret void +} + +; X64: define private void @[[GNAME]]() +; WASM32: define void @g() !type !{{[0-9]+}} !wasm.index ![[I1:[0-9]+]] +define void @g() !type !1 { + ret void +} + +!0 = !{i32 0, !"typeid1"} +!1 = !{i32 0, !"typeid2"} + +declare i1 @llvm.type.test(i8* %ptr, metadata %bitset) nounwind readnone + +define i1 @foo(i8* %p) { + ; X64: icmp eq i64 {{.*}}, ptrtoint ([1 x <{ i8, i32, i8, i8, i8 }>]* @[[JT0]] to i64) + ; WASM32: icmp eq i64 {{.*}}, 0 + %x = call i1 @llvm.type.test(i8* %p, metadata !"typeid1") + ; X64: icmp eq i64 {{.*}}, ptrtoint ([1 x <{ i8, i32, i8, i8, i8 }>]* @[[JT1]] to i64) + ; WASM32: icmp eq i64 {{.*}}, 1 + %y = call i1 @llvm.type.test(i8* %p, metadata !"typeid2") + %z = add i1 %x, %y + ret i1 %z +} + +; WASM32: ![[I0]] = !{i64 0} +; WASM32: ![[I1]] = !{i64 1} Index: test/Transforms/LowerTypeTests/function-ext.ll =================================================================== --- test/Transforms/LowerTypeTests/function-ext.ll +++ test/Transforms/LowerTypeTests/function-ext.ll @@ -1,16 +1,19 @@ -; RUN: opt -S -lowertypetests < %s | FileCheck %s +; RUN: opt -S -lowertypetests -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck --check-prefix=X64 %s +; RUN: opt -S -lowertypetests -mtriple=wasm32-unknown-unknown < %s | FileCheck --check-prefix=WASM32 %s ; Tests that we correctly handle external references, including the case where ; all functions in a bitset are external references. -target triple = "x86_64-unknown-linux-gnu" +; X64: @[[JT:.*]] = private constant [1 x <{ i8, i32, i8, i8, i8 }>] [<{ i8, i32, i8, i8, i8 }> <{ i8 -23, i32 trunc (i64 sub (i64 sub (i64 ptrtoint (void ()* @foo to i64), i64 ptrtoint ([1 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]] to i64)), i64 5) to i32), i8 -52, i8 -52, i8 -52 }>], section ".text" +; WASM32: private constant [0 x i8] zeroinitializer +; WASM32: declare !type !{{[0-9]+}} void @foo() declare !type !0 void @foo() -; CHECK: @[[JT:.*]] = private constant [1 x <{ i8, i32, i8, i8, i8 }>] [<{ i8, i32, i8, i8, i8 }> <{ i8 -23, i32 trunc (i64 sub (i64 sub (i64 ptrtoint (void ()* @foo to i64), i64 ptrtoint ([1 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]] to i64)), i64 5) to i32), i8 -52, i8 -52, i8 -52 }>], section ".text" - define i1 @bar(i8* %ptr) { - ; CHECK: icmp eq i64 {{.*}}, ptrtoint ([1 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]] to i64) + ; X64: icmp eq i64 {{.*}}, ptrtoint ([1 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]] to i64) + ; WASM32: sub i64 {{.*}}, 0 + ; WASM32: icmp ult i64 {{.*}}, 1 %p = call i1 @llvm.type.test(i8* %ptr, metadata !"void") ret i1 %p } @@ -18,3 +21,4 @@ declare i1 @llvm.type.test(i8* %ptr, metadata %bitset) nounwind readnone !0 = !{i64 0, !"void"} +; WASM-NOT: !{i64 0} Index: test/Transforms/LowerTypeTests/function.ll =================================================================== --- test/Transforms/LowerTypeTests/function.ll +++ test/Transforms/LowerTypeTests/function.ll @@ -1,22 +1,25 @@ -; RUN: opt -S -lowertypetests < %s | FileCheck %s +; RUN: opt -S -lowertypetests -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck --check-prefix=X64 %s +; RUN: opt -S -lowertypetests -mtriple=wasm32-unknown-unknown < %s | FileCheck --check-prefix=WASM32 %s -; Tests that we correctly create a jump table for bitsets containing 2 or more -; functions. +; Tests that we correctly handle bitsets containing 2 or more functions. -target triple = "x86_64-unknown-linux-gnu" target datalayout = "e-p:64:64" -; CHECK: @[[JT:.*]] = private constant [2 x <{ i8, i32, i8, i8, i8 }>] [<{ i8, i32, i8, i8, i8 }> <{ i8 -23, i32 trunc (i64 sub (i64 sub (i64 ptrtoint (void ()* @[[FNAME:.*]] to i64), i64 ptrtoint ([2 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]] to i64)), i64 5) to i32), i8 -52, i8 -52, i8 -52 }>, <{ i8, i32, i8, i8, i8 }> <{ i8 -23, i32 trunc (i64 sub (i64 sub (i64 ptrtoint (void ()* @[[GNAME:.*]] to i64), i64 ptrtoint ([2 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]] to i64)), i64 13) to i32), i8 -52, i8 -52, i8 -52 }>], section ".text" +; X64: @[[JT:.*]] = private constant [2 x <{ i8, i32, i8, i8, i8 }>] [<{ i8, i32, i8, i8, i8 }> <{ i8 -23, i32 trunc (i64 sub (i64 sub (i64 ptrtoint (void ()* @[[FNAME:.*]] to i64), i64 ptrtoint ([2 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]] to i64)), i64 5) to i32), i8 -52, i8 -52, i8 -52 }>, <{ i8, i32, i8, i8, i8 }> <{ i8 -23, i32 trunc (i64 sub (i64 sub (i64 ptrtoint (void ()* @[[GNAME:.*]] to i64), i64 ptrtoint ([2 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]] to i64)), i64 13) to i32), i8 -52, i8 -52, i8 -52 }>], section ".text" +; WASM32: private constant [0 x i8] zeroinitializer +@0 = private unnamed_addr constant [2 x void (...)*] [void (...)* bitcast (void ()* @f to void (...)*), void (...)* bitcast (void ()* @g to void (...)*)], align 16 -; CHECK: @f = alias void (), bitcast ([2 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]] to void ()*) -; CHECK: @g = alias void (), bitcast (<{ i8, i32, i8, i8, i8 }>* getelementptr inbounds ([2 x <{ i8, i32, i8, i8, i8 }>], [2 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]], i64 0, i64 1) to void ()*) +; X64: @f = alias void (), bitcast ([2 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]] to void ()*) +; X64: @g = alias void (), bitcast (<{ i8, i32, i8, i8, i8 }>* getelementptr inbounds ([2 x <{ i8, i32, i8, i8, i8 }>], [2 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]], i64 0, i64 1) to void ()*) -; CHECK: define private void @[[FNAME]]() +; X64: define private void @[[FNAME]]() +; WASM32: define void @f() !type !{{[0-9]+}} !wasm.index ![[I0:[0-9]+]] define void @f() !type !0 { ret void } -; CHECK: define private void @[[GNAME]]() +; X64: define private void @[[GNAME]]() +; WASM32: define void @g() !type !{{[0-9]+}} !wasm.index ![[I1:[0-9]+]] define void @g() !type !0 { ret void } @@ -26,7 +29,12 @@ declare i1 @llvm.type.test(i8* %ptr, metadata %bitset) nounwind readnone define i1 @foo(i8* %p) { - ; CHECK: sub i64 {{.*}}, ptrtoint ([2 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]] to i64) + ; X64: sub i64 {{.*}}, ptrtoint ([2 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]] to i64) + ; WASM32: sub i64 {{.*}}, 0 + ; WASM32: icmp ult i64 {{.*}}, 2 %x = call i1 @llvm.type.test(i8* %p, metadata !"typeid1") ret i1 %x } + +; WASM32: ![[I0]] = !{i64 0} +; WASM32: ![[I1]] = !{i64 1}