1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 19:12:56 +02:00

[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

llvm-svn: 277398
This commit is contained in:
Derek Schuff 2016-08-01 22:25:02 +00:00
parent d1c4284e63
commit b35f7499a1
9 changed files with 221 additions and 43 deletions

View File

@ -59,6 +59,7 @@ enum Directive {
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 @@ inline unsigned GetDefaultP2Align(unsigned Opcode) {
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");
}
}

View File

@ -67,13 +67,18 @@ void WebAssemblyTargetAsmStreamer::emitEndFunc() { OS << "\t.endfunc\n"; }
void WebAssemblyTargetAsmStreamer::emitIndirectFunctionType(
StringRef name, SmallVectorImpl<MVT> &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<MVT> Types) {
@ -100,3 +105,8 @@ void WebAssemblyTargetELFStreamer::emitLocal(ArrayRef<MVT> Types) {
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));
}

View File

@ -43,6 +43,8 @@ public:
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 @@ public:
void emitIndirectFunctionType(StringRef name,
SmallVectorImpl<MVT> &SignatureVTs,
size_t NumResults) override;
void emitIndIdx(const MCExpr *Value) override;
};
/// This part is for ELF object output
@ -70,6 +73,7 @@ public:
void emitResult(ArrayRef<MVT> Types) override;
void emitLocal(ArrayRef<MVT> Types) override;
void emitEndFunc() override;
void emitIndIdx(const MCExpr *Value) override;
};
} // end namespace llvm

View File

@ -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 @@ void WebAssemblyAsmPrinter::EmitFunctionBodyStart() {
SmallVector<MVT, 4> 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<ConstantAsMetadata>(Idx->getOperand(0))->getValue()));
}
ComputeLegalValueVTs(F, TM, F.getReturnType(), ResultVTs);
// If the return type needs to be legalized it will get converted into

View File

@ -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 @@ bool BitSetInfo::containsValue(
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<Operator>(V)) {
@ -222,6 +221,9 @@ struct LowerTypeTests : public ModulePass {
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<Metadata *, std::vector<CallInst *>> TypeTestCallSites;
@ -250,6 +252,10 @@ struct LowerTypeTests : public ModulePass {
void verifyTypeMDNode(GlobalObject *GO, MDNode *Type);
void buildBitSetsFromFunctions(ArrayRef<Metadata *> TypeIds,
ArrayRef<Function *> Functions);
void buildBitSetsFromFunctionsX86(ArrayRef<Metadata *> TypeIds,
ArrayRef<Function *> Functions);
void buildBitSetsFromFunctionsWASM(ArrayRef<Metadata *> TypeIds,
ArrayRef<Function *> Functions);
void buildBitSetsFromDisjointSet(ArrayRef<Metadata *> TypeIds,
ArrayRef<GlobalObject *> Globals);
bool lower();
@ -267,8 +273,7 @@ ModulePass *llvm::createLowerTypeTestsPass() { return new LowerTypeTests; }
/// Build a bit set for TypeId using the object layouts in
/// GlobalLayout.
BitSetInfo LowerTypeTests::buildBitSet(
Metadata *TypeId,
const DenseMap<GlobalObject *, uint64_t> &GlobalLayout) {
Metadata *TypeId, const DenseMap<GlobalObject *, uint64_t> &GlobalLayout) {
BitSetBuilder BSB;
// Compute the byte offset of each address associated with this type
@ -281,8 +286,9 @@ BitSetInfo LowerTypeTests::buildBitSet(
if (Type->getOperand(1) != TypeId)
continue;
uint64_t Offset =
cast<ConstantInt>(cast<ConstantAsMetadata>(Type->getOperand(0))
->getValue())->getZExtValue();
cast<ConstantInt>(
cast<ConstantAsMetadata>(Type->getOperand(0))->getValue())
->getZExtValue();
BSB.addOffset(GlobalAndOffset.second + Offset);
}
}
@ -311,8 +317,8 @@ ByteArrayInfo *LowerTypeTests::createByteArray(BitSetInfo &BSI) {
// 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::lowerTypeTestCalls(
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 @@ void LowerTypeTests::verifyTypeMDNode(GlobalObject *GO, MDNode *Type) {
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 @@ unsigned LowerTypeTests::getJumpTableEntrySize() {
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 @@ Constant *LowerTypeTests::createJumpTableEntry(GlobalObject *Src,
}
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<Metadata *> TypeIds,
ArrayRef<Function *> 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<Metadata *> TypeIds, ArrayRef<Function *> 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 @@ void LowerTypeTests::buildBitSetsFromFunctions(ArrayRef<Metadata *> TypeIds,
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<Metadata *> TypeIds, ArrayRef<Function *> Functions) {
assert(!Functions.empty());
// Build consecutive monotonic integer ranges for each call target set
DenseMap<GlobalObject *, uint64_t> 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<Metadata *>(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<PointerType>(Int32PtrTy)),
GlobalLayout);
}
void LowerTypeTests::buildBitSetsFromDisjointSet(
ArrayRef<Metadata *> TypeIds, ArrayRef<GlobalObject *> Globals) {
llvm::DenseMap<Metadata *, uint64_t> TypeIdIndices;
@ -900,8 +942,7 @@ bool LowerTypeTests::lower() {
auto BitSetMDVal = dyn_cast<MetadataAsValue>(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 @@ bool LowerTypeTests::lower() {
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 @@ static void init(LowerTypeTests *LTT, Module &M) {
LTT->Int64Ty = Type::getInt64Ty(M.getContext());
LTT->IntPtrTy = DL.getIntPtrType(M.getContext(), 0);
LTT->TypeTestCallSites.clear();
LTT->IndirectIndex = 0;
}
bool LowerTypeTests::runOnModule(Module &M) {

View File

@ -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
}

View File

@ -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}

View File

@ -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 @@ define i1 @bar(i8* %ptr) {
declare i1 @llvm.type.test(i8* %ptr, metadata %bitset) nounwind readnone
!0 = !{i64 0, !"void"}
; WASM-NOT: !{i64 0}

View File

@ -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 @@ define void @g() !type !0 {
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}