llvm.org GIT mirror llvm / 23429a5
[WebAssembly] Support CFI for WebAssembly target Summary: This patch implements CFI for WebAssembly. It modifies the LowerTypeTest pass to pre-assign table indexes to functions that are called indirectly, and lowers type checks to test against the appropriate table indexes. It also modifies the WebAssembly backend to support a special ".indidx" assembly directive that propagates the table index assignments out to the linker. Patch by Dominic Chen Differential Revision: https://reviews.llvm.org/D21768 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@277398 91177308-0d34-0410-b5e6-96231b3b80d8 Derek Schuff 4 years ago
9 changed file(s) with 221 addition(s) and 43 deletion(s). Raw diff Collapse all Expand all
5858 DotResult = UINT64_MAX - 1, ///< .result
5959 DotLocal = UINT64_MAX - 2, ///< .local
6060 DotEndFunc = UINT64_MAX - 3, ///< .endfunc
61 DotIndIdx = UINT64_MAX - 4, /// < .indidx
6162 };
6263
6364 } // end namespace WebAssembly
122123 case WebAssembly::STORE_I64:
123124 case WebAssembly::STORE_F64:
124125 return 3;
125 default: llvm_unreachable("Only loads and stores have p2align values");
126 default:
127 llvm_unreachable("Only loads and stores have p2align values");
126128 }
127129 }
128130
6666 void WebAssemblyTargetAsmStreamer::emitIndirectFunctionType(
6767 StringRef name, SmallVectorImpl &SignatureVTs, size_t NumResults) {
6868 OS << "\t.functype\t" << name;
69 if (NumResults == 0) OS << ", void";
69 if (NumResults == 0)
70 OS << ", void";
7071 for (auto Ty : SignatureVTs) {
7172 OS << ", " << WebAssembly::TypeToString(Ty);
7273 }
7374 OS << "\n";
75 }
76
77 void WebAssemblyTargetAsmStreamer::emitIndIdx(const MCExpr *Value) {
78 OS << "\t.indidx \t" << *Value << '\n';
7479 }
7580
7681 // FIXME: What follows is not the real binary encoding.
99104 void WebAssemblyTargetELFStreamer::emitEndFunc() {
100105 Streamer.EmitIntValue(WebAssembly::DotEndFunc, sizeof(uint64_t));
101106 }
107
108 void WebAssemblyTargetELFStreamer::emitIndIdx(const MCExpr *Value) {
109 Streamer.EmitIntValue(WebAssembly::DotIndIdx, sizeof(uint64_t));
110 Streamer.EmitValue(Value, sizeof(uint64_t));
111 }
4242 size_t NumResults) {
4343 llvm_unreachable("emitIndirectFunctionType not implemented");
4444 }
45 /// .indidx
46 virtual void emitIndIdx(const MCExpr *Value) = 0;
4547 };
4648
4749 /// This part is for ascii assembly output
5860 void emitIndirectFunctionType(StringRef name,
5961 SmallVectorImpl &SignatureVTs,
6062 size_t NumResults) override;
63 void emitIndIdx(const MCExpr *Value) override;
6164 };
6265
6366 /// This part is for ELF object output
6972 void emitResult(ArrayRef Types) override;
7073 void emitLocal(ArrayRef Types) override;
7174 void emitEndFunc() override;
75 void emitIndIdx(const MCExpr *Value) override;
7276 };
7377
7478 } // end namespace llvm
1313 ///
1414 //===----------------------------------------------------------------------===//
1515
16 #include "WebAssembly.h"
1716 #include "InstPrinter/WebAssemblyInstPrinter.h"
1817 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
1918 #include "MCTargetDesc/WebAssemblyTargetStreamer.h"
19 #include "WebAssembly.h"
2020 #include "WebAssemblyMCInstLower.h"
2121 #include "WebAssemblyMachineFunctionInfo.h"
2222 #include "WebAssemblyRegisterInfo.h"
182182
183183 SmallVector ResultVTs;
184184 const Function &F(*MF->getFunction());
185
186 // Emit the function index.
187 if (MDNode *Idx = F.getMetadata("wasm.index")) {
188 assert(Idx->getNumOperands() == 1);
189
190 getTargetStreamer()->emitIndIdx(AsmPrinter::lowerConstant(
191 cast(Idx->getOperand(0))->getValue()));
192 }
193
185194 ComputeLegalValueVTs(F, TM, F.getReturnType(), ResultVTs);
186195
187196 // If the return type needs to be legalized it will get converted into
1212 //===----------------------------------------------------------------------===//
1313
1414 #include "llvm/Transforms/IPO/LowerTypeTests.h"
15 #include "llvm/Transforms/IPO.h"
1615 #include "llvm/ADT/EquivalenceClasses.h"
1716 #include "llvm/ADT/Statistic.h"
1817 #include "llvm/ADT/Triple.h"
2928 #include "llvm/Pass.h"
3029 #include "llvm/Support/Debug.h"
3130 #include "llvm/Support/raw_ostream.h"
31 #include "llvm/Transforms/IPO.h"
3232 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
3333
3434 using namespace llvm;
7878 if (!Result)
7979 return false;
8080 COffset += APOffset.getZExtValue();
81 return containsValue(DL, GlobalLayout, GEP->getPointerOperand(),
82 COffset);
81 return containsValue(DL, GlobalLayout, GEP->getPointerOperand(), COffset);
8382 }
8483
8584 if (auto Op = dyn_cast(V)) {
220219 Type *Int32PtrTy;
221220 IntegerType *Int64Ty;
222221 IntegerType *IntPtrTy;
222
223 // Indirect function call index assignment counter for WebAssembly
224 uint64_t IndirectIndex;
223225
224226 // Mapping from type identifiers to the call sites that test them.
225227 DenseMap> TypeTestCallSites;
249251 void verifyTypeMDNode(GlobalObject *GO, MDNode *Type);
250252 void buildBitSetsFromFunctions(ArrayRef TypeIds,
251253 ArrayRef Functions);
254 void buildBitSetsFromFunctionsX86(ArrayRef TypeIds,
255 ArrayRef Functions);
256 void buildBitSetsFromFunctionsWASM(ArrayRef TypeIds,
257 ArrayRef Functions);
252258 void buildBitSetsFromDisjointSet(ArrayRef TypeIds,
253259 ArrayRef Globals);
254260 bool lower();
266272 /// Build a bit set for TypeId using the object layouts in
267273 /// GlobalLayout.
268274 BitSetInfo LowerTypeTests::buildBitSet(
269 Metadata *TypeId,
270 const DenseMap &GlobalLayout) {
275 Metadata *TypeId, const DenseMap &GlobalLayout) {
271276 BitSetBuilder BSB;
272277
273278 // Compute the byte offset of each address associated with this type
280285 if (Type->getOperand(1) != TypeId)
281286 continue;
282287 uint64_t Offset =
283 cast(cast(Type->getOperand(0))
284 ->getValue())->getZExtValue();
288 cast(
289 cast(Type->getOperand(0))->getValue())
290 ->getZExtValue();
285291 BSB.addOffset(GlobalAndOffset.second + Offset);
286292 }
287293 }
310316 // we know the offset and mask to use.
311317 auto ByteArrayGlobal = new GlobalVariable(
312318 *M, Int8Ty, /*isConstant=*/true, GlobalValue::PrivateLinkage, nullptr);
313 auto MaskGlobal = new GlobalVariable(
314 *M, Int8Ty, /*isConstant=*/true, GlobalValue::PrivateLinkage, nullptr);
319 auto MaskGlobal = new GlobalVariable(*M, Int8Ty, /*isConstant=*/true,
320 GlobalValue::PrivateLinkage, nullptr);
315321
316322 ByteArrayInfos.emplace_back();
317323 ByteArrayInfo *BAI = &ByteArrayInfos.back();
587593
588594 void LowerTypeTests::verifyTypeMDNode(GlobalObject *GO, MDNode *Type) {
589595 if (Type->getNumOperands() != 2)
590 report_fatal_error(
591 "All operands of type metadata must have 2 elements");
596 report_fatal_error("All operands of type metadata must have 2 elements");
592597
593598 if (GO->isThreadLocal())
594599 report_fatal_error("Bit set element may not be thread-local");
611616 static const unsigned kX86JumpTableEntrySize = 8;
612617
613618 unsigned LowerTypeTests::getJumpTableEntrySize() {
614 if (Arch != Triple::x86 && Arch != Triple::x86_64)
615 report_fatal_error("Unsupported architecture for jump tables");
616
617619 return kX86JumpTableEntrySize;
618620 }
619621
624626 Constant *LowerTypeTests::createJumpTableEntry(GlobalObject *Src,
625627 Function *Dest,
626628 unsigned Distance) {
627 if (Arch != Triple::x86 && Arch != Triple::x86_64)
628 report_fatal_error("Unsupported architecture for jump tables");
629
630629 const unsigned kJmpPCRel32Code = 0xe9;
631630 const unsigned kInt3Code = 0xcc;
632631
651650 }
652651
653652 Type *LowerTypeTests::getJumpTableEntryType() {
654 if (Arch != Triple::x86 && Arch != Triple::x86_64)
655 report_fatal_error("Unsupported architecture for jump tables");
656
657653 return StructType::get(M->getContext(),
658654 {Int8Ty, Int32Ty, Int8Ty, Int8Ty, Int8Ty},
659655 /*Packed=*/true);
660656 }
661657
658 /// Given a disjoint set of type identifiers and functions, build the bit sets
659 /// and lower the llvm.type.test calls, architecture dependently.
660 void LowerTypeTests::buildBitSetsFromFunctions(ArrayRef TypeIds,
661 ArrayRef Functions) {
662 if (Arch == Triple::x86 || Arch == Triple::x86_64)
663 buildBitSetsFromFunctionsX86(TypeIds, Functions);
664 else if (Arch == Triple::wasm32 || Arch == Triple::wasm64)
665 buildBitSetsFromFunctionsWASM(TypeIds, Functions);
666 else
667 report_fatal_error("Unsupported architecture for jump tables");
668 }
669
662670 /// Given a disjoint set of type identifiers and functions, build a jump table
663671 /// for the functions, build the bit sets and lower the llvm.type.test calls.
664 void LowerTypeTests::buildBitSetsFromFunctions(ArrayRef TypeIds,
665 ArrayRef Functions) {
672 void LowerTypeTests::buildBitSetsFromFunctionsX86(
673 ArrayRef TypeIds, ArrayRef Functions) {
666674 // Unlike the global bitset builder, the function bitset builder cannot
667675 // re-arrange functions in a particular order and base its calculations on the
668676 // layout of the functions' entry points, as we have no idea how large a
794802 ConstantArray::get(JumpTableType, JumpTableEntries));
795803 }
796804
805 /// Assign a dummy layout using an incrementing counter, tag each function
806 /// with its index represented as metadata, and lower each type test to an
807 /// integer range comparison. During generation of the indirect function call
808 /// table in the backend, it will assign the given indexes.
809 /// Note: Dynamic linking is not supported, as the WebAssembly ABI has not yet
810 /// been finalized.
811 void LowerTypeTests::buildBitSetsFromFunctionsWASM(
812 ArrayRef TypeIds, ArrayRef Functions) {
813 assert(!Functions.empty());
814
815 // Build consecutive monotonic integer ranges for each call target set
816 DenseMap GlobalLayout;
817 for (Function *F : Functions) {
818 // Skip functions that are not address taken, to avoid bloating the table
819 if (!F->hasAddressTaken())
820 continue;
821
822 // Store metadata with the index for each function
823 MDNode *MD = MDNode::get(F->getContext(),
824 ArrayRef(ConstantAsMetadata::get(
825 ConstantInt::get(Int64Ty, IndirectIndex))));
826 F->setMetadata("wasm.index", MD);
827
828 // Assign the counter value
829 GlobalLayout[F] = IndirectIndex++;
830 }
831
832 // The indirect function table index space starts at zero, so pass a NULL
833 // pointer as the subtracted "jump table" offset.
834 lowerTypeTestCalls(TypeIds,
835 ConstantPointerNull::get(cast(Int32PtrTy)),
836 GlobalLayout);
837 }
838
797839 void LowerTypeTests::buildBitSetsFromDisjointSet(
798840 ArrayRef TypeIds, ArrayRef Globals) {
799841 llvm::DenseMap TypeIdIndices;
899941
900942 auto BitSetMDVal = dyn_cast(CI->getArgOperand(1));
901943 if (!BitSetMDVal)
902 report_fatal_error(
903 "Second argument of llvm.type.test must be metadata");
944 report_fatal_error("Second argument of llvm.type.test must be metadata");
904945 auto BitSet = BitSetMDVal->getMetadata();
905946
906947 // Add the call site to the list of call sites for this type identifier. We
938979 for (GlobalClassesTy::iterator I = GlobalClasses.begin(),
939980 E = GlobalClasses.end();
940981 I != E; ++I) {
941 if (!I->isLeader()) continue;
982 if (!I->isLeader())
983 continue;
942984 ++NumTypeIdDisjointSets;
943985
944986 unsigned MaxIndex = 0;
9991041 LTT->Int64Ty = Type::getInt64Ty(M.getContext());
10001042 LTT->IntPtrTy = DL.getIntPtrType(M.getContext(), 0);
10011043 LTT->TypeTestCallSites.clear();
1044 LTT->IndirectIndex = 0;
10021045 }
10031046
10041047 bool LowerTypeTests::runOnModule(Module &M) {
0 ; RUN: opt -S -lowertypetests < %s | llc -asm-verbose=false -disable-wasm-fallthrough-return-opt | FileCheck %s
1
2 ; Tests that we correctly assign indexes for control flow integrity.
3
4 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
5 target triple = "wasm32-unknown-unknown"
6
7 @0 = private unnamed_addr constant [2 x void (...)*] [void (...)* bitcast (void ()* @f to void (...)*), void (...)* bitcast (void ()* @g to void (...)*)], align 16
8
9 ; CHECK-LABEL: h:
10 ; CHECK-NOT: .indidx
11 define void @h() !type !0 {
12 ret void
13 }
14
15 ; CHECK-LABEL: f:
16 ; CHECK: .indidx 0
17 define void @f() !type !0 {
18 ret void
19 }
20
21 ; CHECK-LABEL: g:
22 ; CHECK: .indidx 1
23 define void @g() !type !1 {
24 ret void
25 }
26
27 !0 = !{i32 0, !"typeid1"}
28 !1 = !{i32 0, !"typeid2"}
29
30 declare i1 @llvm.type.test(i8* %ptr, metadata %bitset) nounwind readnone
31 declare void @llvm.trap() nounwind noreturn
32
33 ; CHECK-LABEL: foo:
34 ; CHECK: br_if
35 ; CHECK: br_if
36 ; CHECK: unreachable
37 define i1 @foo(i8* %p) {
38 %x = call i1 @llvm.type.test(i8* %p, metadata !"typeid1")
39 br i1 %x, label %contx, label %trap
40
41 trap:
42 tail call void @llvm.trap() #1
43 unreachable
44
45 contx:
46 %y = call i1 @llvm.type.test(i8* %p, metadata !"typeid2")
47 br i1 %y, label %conty, label %trap
48
49 conty:
50 %z = add i1 %x, %y
51 ret i1 %z
52 }
0 ; RUN: opt -S -lowertypetests -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck --check-prefix=X64 %s
1 ; RUN: opt -S -lowertypetests -mtriple=wasm32-unknown-unknown < %s | FileCheck --check-prefix=WASM32 %s
2
3 ; Tests that we correctly handle bitsets with disjoint call target sets.
4
5 target datalayout = "e-p:64:64"
6
7 ; 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"
8 ; 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"
9 ; WASM32: private constant [0 x i8] zeroinitializer
10 @0 = private unnamed_addr constant [2 x void ()*] [void ()* @f, void ()* @g], align 16
11
12 ; X64: @f = alias void (), bitcast ([1 x <{ i8, i32, i8, i8, i8 }>]* @[[JT0]] to void ()*)
13 ; X64: @g = alias void (), bitcast ([1 x <{ i8, i32, i8, i8, i8 }>]* @[[JT1]] to void ()*)
14
15 ; X64: define private void @[[FNAME]]()
16 ; WASM32: define void @f() !type !{{[0-9]+}} !wasm.index ![[I0:[0-9]+]]
17 define void @f() !type !0 {
18 ret void
19 }
20
21 ; X64: define private void @[[GNAME]]()
22 ; WASM32: define void @g() !type !{{[0-9]+}} !wasm.index ![[I1:[0-9]+]]
23 define void @g() !type !1 {
24 ret void
25 }
26
27 !0 = !{i32 0, !"typeid1"}
28 !1 = !{i32 0, !"typeid2"}
29
30 declare i1 @llvm.type.test(i8* %ptr, metadata %bitset) nounwind readnone
31
32 define i1 @foo(i8* %p) {
33 ; X64: icmp eq i64 {{.*}}, ptrtoint ([1 x <{ i8, i32, i8, i8, i8 }>]* @[[JT0]] to i64)
34 ; WASM32: icmp eq i64 {{.*}}, 0
35 %x = call i1 @llvm.type.test(i8* %p, metadata !"typeid1")
36 ; X64: icmp eq i64 {{.*}}, ptrtoint ([1 x <{ i8, i32, i8, i8, i8 }>]* @[[JT1]] to i64)
37 ; WASM32: icmp eq i64 {{.*}}, 1
38 %y = call i1 @llvm.type.test(i8* %p, metadata !"typeid2")
39 %z = add i1 %x, %y
40 ret i1 %z
41 }
42
43 ; WASM32: ![[I0]] = !{i64 0}
44 ; WASM32: ![[I1]] = !{i64 1}
None ; RUN: opt -S -lowertypetests < %s | FileCheck %s
0 ; RUN: opt -S -lowertypetests -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck --check-prefix=X64 %s
1 ; RUN: opt -S -lowertypetests -mtriple=wasm32-unknown-unknown < %s | FileCheck --check-prefix=WASM32 %s
12
23 ; Tests that we correctly handle external references, including the case where
34 ; all functions in a bitset are external references.
45
5 target triple = "x86_64-unknown-linux-gnu"
6 ; 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"
7 ; WASM32: private constant [0 x i8] zeroinitializer
68
9 ; WASM32: declare !type !{{[0-9]+}} void @foo()
710 declare !type !0 void @foo()
811
9 ; 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"
10
1112 define i1 @bar(i8* %ptr) {
12 ; CHECK: icmp eq i64 {{.*}}, ptrtoint ([1 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]] to i64)
13 ; X64: icmp eq i64 {{.*}}, ptrtoint ([1 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]] to i64)
14 ; WASM32: sub i64 {{.*}}, 0
15 ; WASM32: icmp ult i64 {{.*}}, 1
1316 %p = call i1 @llvm.type.test(i8* %ptr, metadata !"void")
1417 ret i1 %p
1518 }
1720 declare i1 @llvm.type.test(i8* %ptr, metadata %bitset) nounwind readnone
1821
1922 !0 = !{i64 0, !"void"}
23 ; WASM-NOT: !{i64 0}
None ; RUN: opt -S -lowertypetests < %s | FileCheck %s
0 ; RUN: opt -S -lowertypetests -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck --check-prefix=X64 %s
1 ; RUN: opt -S -lowertypetests -mtriple=wasm32-unknown-unknown < %s | FileCheck --check-prefix=WASM32 %s
12
2 ; Tests that we correctly create a jump table for bitsets containing 2 or more
3 ; functions.
3 ; Tests that we correctly handle bitsets containing 2 or more functions.
44
5 target triple = "x86_64-unknown-linux-gnu"
65 target datalayout = "e-p:64:64"
76
8 ; 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"
7 ; 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"
8 ; WASM32: private constant [0 x i8] zeroinitializer
9 @0 = private unnamed_addr constant [2 x void (...)*] [void (...)* bitcast (void ()* @f to void (...)*), void (...)* bitcast (void ()* @g to void (...)*)], align 16
910
10 ; CHECK: @f = alias void (), bitcast ([2 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]] to void ()*)
11 ; 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 ()*)
11 ; X64: @f = alias void (), bitcast ([2 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]] to void ()*)
12 ; 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 ()*)
1213
13 ; CHECK: define private void @[[FNAME]]()
14 ; X64: define private void @[[FNAME]]()
15 ; WASM32: define void @f() !type !{{[0-9]+}} !wasm.index ![[I0:[0-9]+]]
1416 define void @f() !type !0 {
1517 ret void
1618 }
1719
18 ; CHECK: define private void @[[GNAME]]()
20 ; X64: define private void @[[GNAME]]()
21 ; WASM32: define void @g() !type !{{[0-9]+}} !wasm.index ![[I1:[0-9]+]]
1922 define void @g() !type !0 {
2023 ret void
2124 }
2528 declare i1 @llvm.type.test(i8* %ptr, metadata %bitset) nounwind readnone
2629
2730 define i1 @foo(i8* %p) {
28 ; CHECK: sub i64 {{.*}}, ptrtoint ([2 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]] to i64)
31 ; X64: sub i64 {{.*}}, ptrtoint ([2 x <{ i8, i32, i8, i8, i8 }>]* @[[JT]] to i64)
32 ; WASM32: sub i64 {{.*}}, 0
33 ; WASM32: icmp ult i64 {{.*}}, 2
2934 %x = call i1 @llvm.type.test(i8* %p, metadata !"typeid1")
3035 ret i1 %x
3136 }
37
38 ; WASM32: ![[I0]] = !{i64 0}
39 ; WASM32: ![[I1]] = !{i64 1}