Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/BuiltinsWebAssembly.def
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,12 @@ TARGET_BUILTIN(__builtin_wasm_ref_is_null_extern, "ii", "nct", "reference-types"
// return type.
TARGET_BUILTIN(__builtin_wasm_ref_null_func, "i", "nct", "reference-types")

// Check if the runtime type of a function pointer matches its static type. Used
// to avoid "function signature mismatch" traps. Takes a function pointer, uses
// table.get to look up the pointer in __indirect_function_table and then
// ref.test to test the type.
TARGET_BUILTIN(__builtin_wasm_test_function_pointer_signature, "i.", "nct", "gc")

// Table builtins
TARGET_BUILTIN(__builtin_wasm_table_set, "viii", "t", "reference-types")
TARGET_BUILTIN(__builtin_wasm_table_get, "iii", "t", "reference-types")
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -7575,6 +7575,8 @@ def err_typecheck_illegal_increment_decrement : Error<
"cannot %select{decrement|increment}1 value of type %0">;
def err_typecheck_expect_int : Error<
"used type %0 where integer is required">;
def err_typecheck_expect_function_pointer
: Error<"used type %0 where function pointer is required">;
def err_typecheck_expect_hlsl_resource : Error<
"used type %0 where __hlsl_resource_t is required">;
def err_typecheck_arithmetic_incomplete_or_sizeless_type : Error<
Expand Down Expand Up @@ -13202,6 +13204,10 @@ def err_wasm_builtin_arg_must_match_table_element_type : Error <
"%ordinal0 argument must match the element type of the WebAssembly table in the %ordinal1 argument">;
def err_wasm_builtin_arg_must_be_integer_type : Error <
"%ordinal0 argument must be an integer">;
def err_wasm_builtin_test_fp_sig_cannot_include_reference_type
: Error<"not supported for "
"function pointers with a reference type %select{return "
"value|parameter}0">;

// OpenACC diagnostics.
def warn_acc_routine_unimplemented
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Sema/SemaWasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class SemaWasm : public SemaBase {
bool BuiltinWasmTableGrow(CallExpr *TheCall);
bool BuiltinWasmTableFill(CallExpr *TheCall);
bool BuiltinWasmTableCopy(CallExpr *TheCall);
bool BuiltinWasmTestFunctionPointerSignature(CallExpr *TheCall);

WebAssemblyImportNameAttr *
mergeImportNameAttr(Decl *D, const WebAssemblyImportNameAttr &AL);
Expand Down
16 changes: 16 additions & 0 deletions clang/lib/Basic/Targets/WebAssembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ bool WebAssemblyTargetInfo::hasFeature(StringRef Feature) const {
.Case("mutable-globals", HasMutableGlobals)
.Case("nontrapping-fptoint", HasNontrappingFPToInt)
.Case("reference-types", HasReferenceTypes)
.Case("gc", HasGC)
.Case("relaxed-simd", SIMDLevel >= RelaxedSIMD)
.Case("sign-ext", HasSignExt)
.Case("simd128", SIMDLevel >= SIMD128)
Expand Down Expand Up @@ -106,6 +107,8 @@ void WebAssemblyTargetInfo::getTargetDefines(const LangOptions &Opts,
Builder.defineMacro("__wasm_nontrapping_fptoint__");
if (HasReferenceTypes)
Builder.defineMacro("__wasm_reference_types__");
if (HasGC)
Builder.defineMacro("__wasm_gc__");
if (SIMDLevel >= RelaxedSIMD)
Builder.defineMacro("__wasm_relaxed_simd__");
if (HasSignExt)
Expand Down Expand Up @@ -307,6 +310,14 @@ bool WebAssemblyTargetInfo::handleTargetFeatures(
HasReferenceTypes = false;
continue;
}
if (Feature == "+gc") {
HasGC = true;
continue;
}
if (Feature == "-gc") {
HasGC = false;
continue;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we add new feature like this don't we normally add them to WebAssemblyTargetInfo::initFeatureMap in addBleedingEdgeFeatures.. I would also expect the test for bleeding edge CPU to be updated, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the test for llvm/test/CodeGen/WebAssembly/target-features-cpus.ll below does seem to include gc in bleeding-edge. But you're right, that we've been also adding things with addBleedingEdgeFeatures so it's weird that that test changes. We should look into what's going on there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #151107

if (Feature == "+relaxed-simd") {
SIMDLevel = std::max(SIMDLevel, RelaxedSIMD);
continue;
Expand Down Expand Up @@ -353,6 +364,11 @@ bool WebAssemblyTargetInfo::handleTargetFeatures(
return false;
}

// gc implies reference-types
if (HasGC) {
HasReferenceTypes = true;
}

// bulk-memory-opt is a subset of bulk-memory.
if (HasBulkMemory) {
HasBulkMemoryOpt = true;
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Basic/Targets/WebAssembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo {
bool HasMutableGlobals = false;
bool HasNontrappingFPToInt = false;
bool HasReferenceTypes = false;
bool HasGC = false;
bool HasSignExt = false;
bool HasTailCall = false;
bool HasWideArithmetic = false;
Expand Down
61 changes: 61 additions & 0 deletions clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@

#include "CGBuiltin.h"
#include "clang/Basic/TargetBuiltins.h"
#include "llvm/ADT/APInt.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/IntrinsicsWebAssembly.h"
#include "llvm/Support/ErrorHandling.h"

using namespace clang;
using namespace CodeGen;
Expand Down Expand Up @@ -218,6 +221,64 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID,
Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_ref_null_func);
return Builder.CreateCall(Callee);
}
case WebAssembly::BI__builtin_wasm_test_function_pointer_signature: {
Value *FuncRef = EmitScalarExpr(E->getArg(0));

// Get the function type from the argument's static type
QualType ArgType = E->getArg(0)->getType();
const PointerType *PtrTy = ArgType->getAs<PointerType>();
assert(PtrTy && "Sema should have ensured this is a function pointer");

const FunctionType *FuncTy = PtrTy->getPointeeType()->getAs<FunctionType>();
assert(FuncTy && "Sema should have ensured this is a function pointer");

// In the llvm IR, we won't have access any more to the type of the function
// pointer so we need to insert this type information somehow. The
// @llvm.wasm.ref.test.func takes varargs arguments whose values are unused
// to indicate the type of the function to test for. See the test here:
// llvm/test/CodeGen/WebAssembly/ref-test-func.ll
//
// The format is: first we include the return types (since this is a C
// function pointer, there will be 0 or one of these) then a token type to
// indicate the boundary between return types and param types, then the
// param types.

llvm::FunctionType *LLVMFuncTy =
cast<llvm::FunctionType>(ConvertType(QualType(FuncTy, 0)));

unsigned NParams = LLVMFuncTy->getNumParams();
std::vector<Value *> Args;
Args.reserve(NParams + 3);
// The only real argument is the FuncRef
Args.push_back(FuncRef);

// Add the type information
auto addType = [this, &Args](llvm::Type *T) {
if (T->isVoidTy()) {
// Do nothing
} else if (T->isFloatingPointTy()) {
Args.push_back(ConstantFP::get(T, 0));
} else if (T->isIntegerTy()) {
Args.push_back(ConstantInt::get(T, 0));
} else if (T->isPointerTy()) {
Args.push_back(ConstantPointerNull::get(llvm::PointerType::get(
getLLVMContext(), T->getPointerAddressSpace())));
} else {
// TODO: Handle reference types. For now, we reject them in Sema.
llvm_unreachable("Unhandled type");
}
};

addType(LLVMFuncTy->getReturnType());
// The token type indicates the boundary between return types and param
// types.
Args.push_back(PoisonValue::get(llvm::Type::getTokenTy(getLLVMContext())));
for (unsigned i = 0; i < NParams; i++) {
addType(LLVMFuncTy->getParamType(i));
}
Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_ref_test_func);
return Builder.CreateCall(Callee, Args);
}
case WebAssembly::BI__builtin_wasm_swizzle_i8x16: {
Value *Src = EmitScalarExpr(E->getArg(0));
Value *Indices = EmitScalarExpr(E->getArg(1));
Expand Down
49 changes: 49 additions & 0 deletions clang/lib/Sema/SemaWasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,53 @@ bool SemaWasm::BuiltinWasmTableCopy(CallExpr *TheCall) {
return false;
}

bool SemaWasm::BuiltinWasmTestFunctionPointerSignature(CallExpr *TheCall) {
if (SemaRef.checkArgCount(TheCall, 1))
return true;

Expr *FuncPtrArg = TheCall->getArg(0);
QualType ArgType = FuncPtrArg->getType();

// Check that the argument is a function pointer
const PointerType *PtrTy = ArgType->getAs<PointerType>();
if (!PtrTy) {
return Diag(FuncPtrArg->getBeginLoc(),
diag::err_typecheck_expect_function_pointer)
<< ArgType << FuncPtrArg->getSourceRange();
}

const FunctionProtoType *FuncTy =
PtrTy->getPointeeType()->getAs<FunctionProtoType>();
if (!FuncTy) {
return Diag(FuncPtrArg->getBeginLoc(),
diag::err_typecheck_expect_function_pointer)
<< ArgType << FuncPtrArg->getSourceRange();
}

// Check that the function pointer doesn't use reference types
if (FuncTy->getReturnType().isWebAssemblyReferenceType()) {
return Diag(
FuncPtrArg->getBeginLoc(),
diag::err_wasm_builtin_test_fp_sig_cannot_include_reference_type)
<< 0 << FuncTy->getReturnType() << FuncPtrArg->getSourceRange();
}
auto NParams = FuncTy->getNumParams();
for (unsigned I = 0; I < NParams; I++) {
if (FuncTy->getParamType(I).isWebAssemblyReferenceType()) {
return Diag(
FuncPtrArg->getBeginLoc(),
diag::
err_wasm_builtin_test_fp_sig_cannot_include_reference_type)
<< 1 << FuncPtrArg->getSourceRange();
}
}

// Set return type to int (the result of the test)
TheCall->setType(getASTContext().IntTy);

return false;
}

bool SemaWasm::CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI,
unsigned BuiltinID,
CallExpr *TheCall) {
Expand All @@ -249,6 +296,8 @@ bool SemaWasm::CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI,
return BuiltinWasmTableFill(TheCall);
case WebAssembly::BI__builtin_wasm_table_copy:
return BuiltinWasmTableCopy(TheCall);
case WebAssembly::BI__builtin_wasm_test_function_pointer_signature:
return BuiltinWasmTestFunctionPointerSignature(TheCall);
}

return false;
Expand Down
27 changes: 24 additions & 3 deletions clang/test/CodeGen/builtins-wasm.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -triple wasm32-unknown-unknown -target-feature +reference-types -target-feature +simd128 -target-feature +relaxed-simd -target-feature +nontrapping-fptoint -target-feature +exception-handling -target-feature +bulk-memory -target-feature +atomics -target-feature +fp16 -flax-vector-conversions=none -O3 -emit-llvm -o - %s | FileCheck %s -check-prefixes WEBASSEMBLY,WEBASSEMBLY32
// RUN: %clang_cc1 -triple wasm64-unknown-unknown -target-feature +reference-types -target-feature +simd128 -target-feature +relaxed-simd -target-feature +nontrapping-fptoint -target-feature +exception-handling -target-feature +bulk-memory -target-feature +atomics -target-feature +fp16 -flax-vector-conversions=none -O3 -emit-llvm -o - %s | FileCheck %s -check-prefixes WEBASSEMBLY,WEBASSEMBLY64
// RUN: not %clang_cc1 -triple wasm64-unknown-unknown -target-feature +reference-types -target-feature +nontrapping-fptoint -target-feature +exception-handling -target-feature +bulk-memory -target-feature +atomics -flax-vector-conversions=none -O3 -emit-llvm -o - %s 2>&1 | FileCheck %s -check-prefixes MISSING-SIMD
// RUN: %clang_cc1 -triple wasm32-unknown-unknown -target-feature +reference-types -target-feature +simd128 -target-feature +relaxed-simd -target-feature +nontrapping-fptoint -target-feature +exception-handling -target-feature +bulk-memory -target-feature +atomics -target-feature +gc -target-feature +fp16 -flax-vector-conversions=none -O3 -emit-llvm -o - %s | FileCheck %s -check-prefixes WEBASSEMBLY,WEBASSEMBLY32
// RUN: %clang_cc1 -triple wasm64-unknown-unknown -target-feature +reference-types -target-feature +simd128 -target-feature +relaxed-simd -target-feature +nontrapping-fptoint -target-feature +exception-handling -target-feature +bulk-memory -target-feature +atomics -target-feature +gc -target-feature +fp16 -flax-vector-conversions=none -O3 -emit-llvm -o - %s | FileCheck %s -check-prefixes WEBASSEMBLY,WEBASSEMBLY64
// RUN: not %clang_cc1 -triple wasm64-unknown-unknown -target-feature +reference-types -target-feature +nontrapping-fptoint -target-feature +exception-handling -target-feature +bulk-memory -target-feature +atomics -target-feature +gc -flax-vector-conversions=none -O3 -emit-llvm -o - %s 2>&1 | FileCheck %s -check-prefixes MISSING-SIMD

// SIMD convenience types
typedef signed char i8x16 __attribute((vector_size(16)));
Expand Down Expand Up @@ -751,3 +751,24 @@ void *tp (void) {
return __builtin_thread_pointer ();
// WEBASSEMBLY: call {{.*}} @llvm.thread.pointer.p0()
}

typedef void (*Fvoid)(void);
typedef float (*Ffloats)(float, double, int);
typedef void (*Fpointers)(Fvoid, Ffloats, void*, int*, int***, char[5]);

void use(int);

void test_function_pointer_signature_void(Fvoid func) {
// WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison)
use(__builtin_wasm_test_function_pointer_signature(func));
}

void test_function_pointer_signature_floats(Ffloats func) {
// WEBASSEMBLY: tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, float 0.000000e+00, token poison, float 0.000000e+00, double 0.000000e+00, i32 0)
use(__builtin_wasm_test_function_pointer_signature(func));
}

void test_function_pointer_signature_pointers(Fpointers func) {
// WEBASSEMBLY: %0 = tail call i32 (ptr, ...) @llvm.wasm.ref.test.func(ptr %func, token poison, ptr null, ptr null, ptr null, ptr null, ptr null, ptr null)
use(__builtin_wasm_test_function_pointer_signature(func));
}
24 changes: 24 additions & 0 deletions clang/test/Sema/builtins-wasm.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,27 @@ void test_table_copy(int dst_idx, int src_idx, int nelem) {
__builtin_wasm_table_copy(table, table, dst_idx, src_idx, table); // expected-error {{5th argument must be an integer}}
__builtin_wasm_table_copy(table, table, dst_idx, src_idx, nelem);
}

typedef void (*F1)(void);
typedef int (*F2)(int);
typedef int (*F3)(__externref_t);
typedef __externref_t (*F4)(int);

void test_function_pointer_signature() {
// Test argument count validation
(void)__builtin_wasm_test_function_pointer_signature(); // expected-error {{too few arguments to function call, expected 1, have 0}}
(void)__builtin_wasm_test_function_pointer_signature((F1)0, (F2)0); // expected-error {{too many arguments to function call, expected 1, have 2}}

// // Test argument type validation - should require function pointer
(void)__builtin_wasm_test_function_pointer_signature((void*)0); // expected-error {{used type 'void *' where function pointer is required}}
(void)__builtin_wasm_test_function_pointer_signature((int)0); // expected-error {{used type 'int' where function pointer is required}}
(void)__builtin_wasm_test_function_pointer_signature((F3)0); // expected-error {{not supported for function pointers with a reference type parameter}}
(void)__builtin_wasm_test_function_pointer_signature((F4)0); // expected-error {{not supported for function pointers with a reference type return value}}

// // Test valid usage
int res = __builtin_wasm_test_function_pointer_signature((F1)0);
res = __builtin_wasm_test_function_pointer_signature((F2)0);

// Test return type
_Static_assert(EXPR_HAS_TYPE(__builtin_wasm_test_function_pointer_signature((F1)0), int), "");
}
15 changes: 8 additions & 7 deletions llvm/lib/Target/WebAssembly/WebAssembly.td
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ def FeatureReferenceTypes :
SubtargetFeature<"reference-types", "HasReferenceTypes", "true",
"Enable reference types">;

def FeatureGC : SubtargetFeature<"gc", "HasGC", "true", "Enable wasm gc">;
def FeatureRelaxedSIMD :
SubtargetFeature<"relaxed-simd", "SIMDLevel", "RelaxedSIMD",
"Enable relaxed-simd instructions">;
Expand Down Expand Up @@ -136,13 +137,13 @@ def : ProcessorModel<"lime1", NoSchedModel,

// Latest and greatest experimental version of WebAssembly. Bugs included!
def : ProcessorModel<"bleeding-edge", NoSchedModel,
[FeatureAtomics, FeatureBulkMemory, FeatureBulkMemoryOpt,
FeatureCallIndirectOverlong, FeatureExceptionHandling,
FeatureExtendedConst, FeatureFP16, FeatureMultiMemory,
FeatureMultivalue, FeatureMutableGlobals,
FeatureNontrappingFPToInt, FeatureRelaxedSIMD,
FeatureReferenceTypes, FeatureSIMD128, FeatureSignExt,
FeatureTailCall]>;
[FeatureAtomics, FeatureBulkMemory, FeatureBulkMemoryOpt,
FeatureCallIndirectOverlong, FeatureExceptionHandling,
FeatureExtendedConst, FeatureFP16, FeatureMultiMemory,
FeatureMultivalue, FeatureMutableGlobals,
FeatureNontrappingFPToInt, FeatureRelaxedSIMD,
FeatureReferenceTypes, FeatureGC, FeatureSIMD128,
FeatureSignExt, FeatureTailCall]>;

//===----------------------------------------------------------------------===//
// Target Declaration
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ def HasReferenceTypes :
Predicate<"Subtarget->hasReferenceTypes()">,
AssemblerPredicate<(all_of FeatureReferenceTypes), "reference-types">;

def HasGC : Predicate<"Subtarget->hasGC()">,
AssemblerPredicate<(all_of FeatureGC), "gc">;

def HasRelaxedSIMD :
Predicate<"Subtarget->hasRelaxedSIMD()">,
AssemblerPredicate<(all_of FeatureRelaxedSIMD), "relaxed-simd">;
Expand Down
11 changes: 4 additions & 7 deletions llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,10 @@ multiclass REF_I<WebAssemblyRegClass rc, ValueType vt, string ht> {
Requires<[HasReferenceTypes]>;
}

defm REF_TEST_FUNCREF :
I<(outs I32: $res),
(ins TypeIndex:$type, FUNCREF: $ref),
(outs),
(ins TypeIndex:$type),
[],
"ref.test\t$type, $ref", "ref.test $type", 0xfb14>;
defm REF_TEST_FUNCREF : I<(outs I32:$res), (ins TypeIndex:$type, FUNCREF:$ref),
(outs), (ins TypeIndex:$type), [],
"ref.test\t$type, $ref", "ref.test $type", 0xfb14>,
Requires<[HasGC]>;

defm "" : REF_I<FUNCREF, funcref, "func">;
defm "" : REF_I<EXTERNREF, externref, "extern">;
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ WebAssemblySubtarget::initializeSubtargetDependencies(StringRef CPU,
Bits.set(WebAssembly::FeatureBulkMemoryOpt);
}

// gc implies reference-types
if (HasGC) {
HasReferenceTypes = true;
}

// reference-types implies call-indirect-overlong
if (HasReferenceTypes) {
HasCallIndirectOverlong = true;
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
bool HasMutableGlobals = false;
bool HasNontrappingFPToInt = false;
bool HasReferenceTypes = false;
bool HasGC = false;
bool HasSignExt = false;
bool HasTailCall = false;
bool HasWideArithmetic = false;
Expand Down Expand Up @@ -107,6 +108,7 @@ class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
bool hasMutableGlobals() const { return HasMutableGlobals; }
bool hasNontrappingFPToInt() const { return HasNontrappingFPToInt; }
bool hasReferenceTypes() const { return HasReferenceTypes; }
bool hasGC() const { return HasGC; }
bool hasRelaxedSIMD() const { return SIMDLevel >= RelaxedSIMD; }
bool hasSignExt() const { return HasSignExt; }
bool hasSIMD128() const { return SIMDLevel >= SIMD128; }
Expand Down
4 changes: 2 additions & 2 deletions llvm/test/CodeGen/WebAssembly/ref-test-func.ll
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
; RUN: llc < %s --mtriple=wasm32-unknown-unknown -mcpu=mvp -mattr=+reference-types -verify-machineinstrs | FileCheck --check-prefixes CHECK,CHK32 %s
; RUN: llc < %s --mtriple=wasm64-unknown-unknown -mcpu=mvp -mattr=+reference-types -verify-machineinstrs | FileCheck --check-prefixes CHECK,CHK64 %s
; RUN: llc < %s --mtriple=wasm32-unknown-unknown -mcpu=mvp -mattr=+reference-types -mattr=+gc -verify-machineinstrs | FileCheck --check-prefixes CHECK,CHK32 %s
; RUN: llc < %s --mtriple=wasm64-unknown-unknown -mcpu=mvp -mattr=+reference-types -mattr=+gc -verify-machineinstrs | FileCheck --check-prefixes CHECK,CHK64 %s

define void @test_fpsig_void_void(ptr noundef %func) local_unnamed_addr #0 {
; CHECK-LABEL: test_fpsig_void_void:
Expand Down
Loading