Skip to content

Commit

Permalink
[X86] initial -mfunction-return=thunk-extern support
Browse files Browse the repository at this point in the history
Adds support for:
* `-mfunction-return=<value>` command line flag, and
* `__attribute__((function_return("<value>")))` function attribute

Where the supported <value>s are:
* keep (disable)
* thunk-extern (enable)

thunk-extern enables clang to change ret instructions into jmps to an
external symbol named __x86_return_thunk, implemented as a new
MachineFunctionPass named "x86-return-thunks", keyed off the new IR
attribute fn_ret_thunk_extern.

The symbol __x86_return_thunk is expected to be provided by the runtime
the compiled code is linked against and is not defined by the compiler.
Enabling this option alone doesn't provide mitigations without
corresponding definitions of __x86_return_thunk!

This new MachineFunctionPass is very similar to "x86-lvi-ret".

The <value>s "thunk" and "thunk-inline" are currently unsupported. It's
not clear yet that they are necessary: whether the thunk pattern they
would emit is beneficial or used anywhere.

Should the <value>s "thunk" and "thunk-inline" become necessary,
x86-return-thunks could probably be merged into x86-retpoline-thunks
which has pre-existing machinery for emitting thunks (which could be
used to implement the <value> "thunk").

Has been found to build+boot with corresponding Linux
kernel patches. This helps the Linux kernel mitigate RETBLEED.
* CVE-2022-23816
* CVE-2022-28693
* CVE-2022-29901

See also:
* "RETBLEED: Arbitrary Speculative Code Execution with Return
Instructions."
* AMD SECURITY NOTICE AMD-SN-1037: AMD CPU Branch Type Confusion
* TECHNICAL GUIDANCE FOR MITIGATING BRANCH TYPE CONFUSION REVISION 1.0
  2022-07-12
* Return Stack Buffer Underflow / Return Stack Buffer Underflow /
  CVE-2022-29901, CVE-2022-28693 / INTEL-SA-00702

SystemZ may eventually want to support "thunk-extern" and "thunk"; both
options are used by the Linux kernel's CONFIG_EXPOLINE.

This functionality has been available in GCC since the 8.1 release, and
was backported to the 7.3 release.

Many thanks for folks that provided discrete review off list due to the
embargoed nature of this hardware vulnerability. Many Bothans died to
bring us this information.

Link: https://www.youtube.com/watch?v=IF6HbCKQHK8
Link: #54404
Link: https://gcc.gnu.org/legacy-ml/gcc-patches/2018-01/msg01197.html
Link: https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/return-stack-buffer-underflow.html
Link: https://arstechnica.com/information-technology/2022/07/intel-and-amd-cpus-vulnerable-to-a-new-speculative-execution-attack/?comments=1
Link: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ce114c866860aa9eae3f50974efc68241186ba60
Link: https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00702.html
Link: https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00707.html

Reviewed By: aaron.ballman, craig.topper

Differential Revision: https://reviews.llvm.org/D129572
  • Loading branch information
nickdesaulniers committed Jul 12, 2022
1 parent 5791bcf commit 2240d72
Show file tree
Hide file tree
Showing 33 changed files with 572 additions and 1 deletion.
9 changes: 9 additions & 0 deletions clang/docs/ReleaseNotes.rst
Expand Up @@ -404,6 +404,12 @@ Attribute Changes in Clang
format string must correctly format the fixed parameter types of the function.
Using the attribute this way emits a GCC compatibility diagnostic.

- Support was added for ``__attribute__((function_return("thunk-extern")))``
to X86 to replace ``ret`` instructions with ``jmp __x86_return_thunk``. The
corresponding attribute to disable this,
``__attribute__((function_return("keep")))`` was added. This is intended to
be used by the Linux kernel to mitigate RETBLEED.

Windows Support
---------------

Expand Down Expand Up @@ -556,6 +562,9 @@ X86 Support in Clang
this instruction (see rdpruintrin.h).
- Support ``-mstack-protector-guard-symbol=[SymbolName]`` to use the given
symbol for addressing the stack protector guard.
- ``-mfunction-return=thunk-extern`` support was added to clang for x86. This
will be used by Linux kernel mitigations for RETBLEED. The corresponding flag
``-mfunction-return=keep`` may be appended to disable the feature.

DWARF Support in Clang
----------------------
Expand Down
11 changes: 11 additions & 0 deletions clang/include/clang/Basic/Attr.td
Expand Up @@ -4036,3 +4036,14 @@ def NoRandomizeLayout : InheritableAttr {
let LangOpts = [COnly];
}
def : MutualExclusions<[RandomizeLayout, NoRandomizeLayout]>;

def FunctionReturnThunks : InheritableAttr,
TargetSpecificAttr<TargetAnyX86> {
let Spellings = [GCC<"function_return">];
let Args = [EnumArgument<"ThunkType", "Kind",
["keep", "thunk-extern"],
["Keep", "Extern"]
>];
let Subjects = SubjectList<[Function]>;
let Documentation = [FunctionReturnThunksDocs];
}
21 changes: 21 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Expand Up @@ -6606,6 +6606,27 @@ evaluate to NULL.
}
return 0;
}
}];
}

def FunctionReturnThunksDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
The attribute ``function_return`` can replace return instructions with jumps to
target-specific symbols. This attribute supports 2 possible values,
corresponding to the values supported by the ``-mfunction-return=`` command
line flag:
* ``__attribute__((function_return("keep")))`` to disable related transforms.
This is useful for undoing global setting from ``-mfunction-return=`` locally
for individual functions.
* ``__attribute__((function_return("thunk-extern")))`` to replace returns with
jumps, while NOT emitting the thunk.

The values ``thunk`` and ``thunk-inline`` from GCC are not supported.

The symbol used for ``thunk-extern`` is target specific:
* X86: ``__x86_return_thunk``

As such, this function attribute is currently only supported on X86 targets.
}];
}
1 change: 1 addition & 0 deletions clang/include/clang/Basic/CodeGenOptions.def
Expand Up @@ -107,6 +107,7 @@ CODEGENOPT(CFProtectionReturn , 1, 0) ///< if -fcf-protection is
CODEGENOPT(CFProtectionBranch , 1, 0) ///< if -fcf-protection is
///< set to full or branch.
CODEGENOPT(IBTSeal, 1, 0) ///< set to optimize CFProtectionBranch.
CODEGENOPT(FunctionReturnThunks, 1, 0) ///< -mfunction-return={keep|thunk-extern}

CODEGENOPT(XRayInstrumentFunctions , 1, 0) ///< Set when -fxray-instrument is
///< enabled.
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/Driver/Options.td
Expand Up @@ -1998,6 +1998,13 @@ def fcf_protection : Flag<["-"], "fcf-protection">, Group<f_Group>, Flags<[CoreO
HelpText<"Enable cf-protection in 'full' mode">;
def mibt_seal : Flag<["-"], "mibt-seal">, Group<m_Group>, Flags<[CoreOption, CC1Option]>,
HelpText<"Optimize fcf-protection=branch/full (requires LTO).">;
def mfunction_return_EQ : Joined<["-"], "mfunction-return=">,
Group<m_Group>, Flags<[CoreOption, CC1Option]>,
HelpText<"Replace returns with jumps to ``__x86_return_thunk`` (x86 only, error otherwise)">,
Values<"keep,thunk-extern">,
NormalizedValues<["Keep", "Extern"]>,
NormalizedValuesScope<"llvm::FunctionReturnThunksKind">,
MarshallingInfoEnum<CodeGenOpts<"FunctionReturnThunks">, "Keep">;

defm xray_instrument : BoolFOption<"xray-instrument",
LangOpts<"XRayInstrument">, DefaultFalse,
Expand Down
14 changes: 14 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.cpp
Expand Up @@ -898,6 +898,20 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
if (D && D->hasAttr<NoProfileFunctionAttr>())
Fn->addFnAttr(llvm::Attribute::NoProfile);

if (D) {
// Function attributes take precedence over command line flags.
if (auto *A = D->getAttr<FunctionReturnThunksAttr>()) {
switch (A->getThunkType()) {
case FunctionReturnThunksAttr::Kind::Keep:
break;
case FunctionReturnThunksAttr::Kind::Extern:
Fn->addFnAttr(llvm::Attribute::FnRetThunkExtern);
break;
}
} else if (CGM.getCodeGenOpts().FunctionReturnThunks)
Fn->addFnAttr(llvm::Attribute::FnRetThunkExtern);
}

if (FD && (getLangOpts().OpenCL ||
(getLangOpts().HIP && getLangOpts().CUDAIsDevice))) {
// Add metadata for a kernel function.
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Expand Up @@ -6344,6 +6344,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
if (IsUsingLTO)
Args.AddLastArg(CmdArgs, options::OPT_mibt_seal);

if (Arg *A = Args.getLastArg(options::OPT_mfunction_return_EQ))
CmdArgs.push_back(
Args.MakeArgString(Twine("-mfunction-return=") + A->getValue()));

// Forward -f options with positive and negative forms; we translate these by
// hand. Do not propagate PGO options to the GPU-side compilations as the
// profile info is for the host-side compilation only.
Expand Down
24 changes: 24 additions & 0 deletions clang/lib/Frontend/CompilerInvocation.cpp
Expand Up @@ -1485,6 +1485,9 @@ void CompilerInvocation::GenerateCodeGenArgs(
if (Opts.IBTSeal)
GenerateArg(Args, OPT_mibt_seal, SA);

if (Opts.FunctionReturnThunks)
GenerateArg(Args, OPT_mfunction_return_EQ, "thunk-extern", SA);

for (const auto &F : Opts.LinkBitcodeFiles) {
bool Builtint = F.LinkFlags == llvm::Linker::Flags::LinkOnlyNeeded &&
F.PropagateAttrs && F.Internalize;
Expand Down Expand Up @@ -1825,6 +1828,27 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name;
}

if (const Arg *A = Args.getLastArg(OPT_mfunction_return_EQ)) {
auto Val = llvm::StringSwitch<llvm::FunctionReturnThunksKind>(A->getValue())
.Case("keep", llvm::FunctionReturnThunksKind::Keep)
.Case("thunk-extern", llvm::FunctionReturnThunksKind::Extern)
.Default(llvm::FunctionReturnThunksKind::Invalid);
// SystemZ might want to add support for "expolines."
if (!T.isX86())
Diags.Report(diag::err_drv_argument_not_allowed_with)
<< A->getSpelling() << T.getTriple();
else if (Val == llvm::FunctionReturnThunksKind::Invalid)
Diags.Report(diag::err_drv_invalid_value)
<< A->getAsString(Args) << A->getValue();
else if (Val == llvm::FunctionReturnThunksKind::Extern &&
Args.getLastArgValue(OPT_mcmodel_EQ).equals("large"))
Diags.Report(diag::err_drv_argument_not_allowed_with)
<< A->getAsString(Args)
<< Args.getLastArg(OPT_mcmodel_EQ)->getAsString(Args);
else
Opts.FunctionReturnThunks = static_cast<unsigned>(Val);
}

if (Opts.PrepareForLTO && Args.hasArg(OPT_mibt_seal))
Opts.IBTSeal = 1;

Expand Down
23 changes: 23 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Expand Up @@ -8000,6 +8000,26 @@ static void handleZeroCallUsedRegsAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
D->addAttr(ZeroCallUsedRegsAttr::Create(S.Context, Kind, AL));
}

static void handleFunctionReturnThunksAttr(Sema &S, Decl *D,
const ParsedAttr &AL) {
StringRef KindStr;
SourceLocation LiteralLoc;
if (!S.checkStringLiteralArgumentAttr(AL, 0, KindStr, &LiteralLoc))
return;

FunctionReturnThunksAttr::Kind Kind;
if (!FunctionReturnThunksAttr::ConvertStrToKind(KindStr, Kind)) {
S.Diag(LiteralLoc, diag::warn_attribute_type_not_supported)
<< AL << KindStr;
return;
}
// FIXME: it would be good to better handle attribute merging rather than
// silently replacing the existing attribute, so long as it does not break
// the expected codegen tests.
D->dropAttr<FunctionReturnThunksAttr>();
D->addAttr(FunctionReturnThunksAttr::Create(S.Context, Kind, AL));
}

static void handleSYCLKernelAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
// The 'sycl_kernel' attribute applies only to function templates.
const auto *FD = cast<FunctionDecl>(D);
Expand Down Expand Up @@ -8866,6 +8886,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_ZeroCallUsedRegs:
handleZeroCallUsedRegsAttr(S, D, AL);
break;
case ParsedAttr::AT_FunctionReturnThunks:
handleFunctionReturnThunksAttr(S, D, AL);
break;

// Microsoft attributes:
case ParsedAttr::AT_LayoutVersion:
Expand Down
96 changes: 96 additions & 0 deletions clang/test/CodeGen/attr-function-return.c
@@ -0,0 +1,96 @@
// RUN: %clang_cc1 -std=gnu2x -triple x86_64-linux-gnu %s -emit-llvm -o - \
// RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-NOM
// RUN: %clang_cc1 -std=gnu2x -triple x86_64-linux-gnu %s -emit-llvm -o - \
// RUN: -mfunction-return=keep | FileCheck %s \
// RUN: --check-prefixes=CHECK,CHECK-KEEP
// RUN: %clang_cc1 -std=gnu2x -triple x86_64-linux-gnu %s -emit-llvm -o - \
// RUN: -mfunction-return=thunk-extern | FileCheck %s \
// RUN: --check-prefixes=CHECK,CHECK-EXTERN

#if !__has_attribute(function_return)
#error "missing attribute support for function_return"
#endif

// CHECK: @keep() [[KEEP:#[0-9]+]]
__attribute__((function_return("keep"))) void keep(void) {}

// CHECK: @keep2() [[KEEP:#[0-9]+]]
[[gnu::function_return("keep")]] void keep2(void) {}

// CHECK: @thunk_extern() [[EXTERN:#[0-9]+]]
__attribute__((function_return("thunk-extern"))) void thunk_extern(void) {}

// CHECK: @thunk_extern2() [[EXTERN:#[0-9]+]]
[[gnu::function_return("thunk-extern")]] void thunk_extern2(void) {}

// CHECK: @double_thunk_keep() [[KEEP]]
// clang-format off
__attribute__((function_return("thunk-extern")))
__attribute__((function_return("keep")))
void double_thunk_keep(void) {}

// CHECK: @double_thunk_keep2() [[KEEP]]
[[gnu::function_return("thunk-extern")]][[gnu::function_return("keep")]]
void double_thunk_keep2(void) {}

// CHECK: @double_keep_thunk() [[EXTERN]]
__attribute__((function_return("keep")))
__attribute__((function_return("thunk-extern")))
void double_keep_thunk(void) {}

// CHECK: @double_keep_thunk2() [[EXTERN]]
[[gnu::function_return("thunk-keep")]][[gnu::function_return("thunk-extern")]]
void double_keep_thunk2(void) {}

// CHECK: @thunk_keep() [[KEEP]]
__attribute__((function_return("thunk-extern"), function_return("keep")))
void thunk_keep(void) {}

// CHECK: @thunk_keep2() [[KEEP]]
[[gnu::function_return("thunk-extern"), gnu::function_return("keep")]]
void thunk_keep2(void) {}

// CHECK: @keep_thunk() [[EXTERN]]
__attribute__((function_return("keep"), function_return("thunk-extern")))
void keep_thunk(void) {}

// CHECK: @keep_thunk2() [[EXTERN]]
[[gnu::function_return("keep"), gnu::function_return("thunk-extern")]]
void keep_thunk2(void) {}
// clang-format on

void undef(void);
// CHECK: @undef() [[KEEP]]
__attribute__((function_return("keep"))) void undef(void) {}

void undef2(void);
// CHECK: @undef2() [[EXTERN]]
__attribute__((function_return("thunk-extern"))) void undef2(void) {}

__attribute__((function_return("thunk-extern"))) void change_def(void);
// CHECK: @change_def() [[KEEP]]
__attribute__((function_return("keep"))) void change_def(void) {}

__attribute__((function_return("keep"))) void change_def2(void);
// CHECK: @change_def2() [[EXTERN]]
__attribute__((function_return("thunk-extern"))) void change_def2(void) {}

__attribute__((function_return("thunk-extern"))) void change_def3(void);
// CHECK: @change_def3() [[KEEP]]
[[gnu::function_return("keep")]] void change_def3(void) {}

[[gnu::function_return("keep")]] void change_def4(void);
// CHECK: @change_def4() [[EXTERN]]
__attribute__((function_return("thunk-extern"))) void change_def4(void) {}

// When there is no -mfunction-return= flag set (NOM) or it's set to keep,
// we don't emit anything into the IR for unattributed functions.

// CHECK-NOM: @no_attrs() [[NOATTR:#[0-9]+]]
// CHECK-KEEP: @no_attrs() [[NOATTR:#[0-9]+]]
// CHECK-EXTERN: @no_attrs() [[EXTERN]]
void no_attrs(void) {}

// CHECK-NOM-NOT: [[NOATTR]] = {{.*}}fn_ret_thunk_extern
// CHECK-KEEP-NOT: [[NOATTR]] = {{.*}}fn_ret_thunk_extern
// CHECK: [[EXTERN]] = {{.*}}fn_ret_thunk_extern
52 changes: 52 additions & 0 deletions clang/test/CodeGen/attr-function-return.cpp
@@ -0,0 +1,52 @@
// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -emit-llvm -o - \
// RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-NOM
// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -emit-llvm -o - \
// RUN: -mfunction-return=keep | FileCheck %s \
// RUN: --check-prefixes=CHECK,CHECK-KEEP
// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -emit-llvm -o - \
// RUN: -mfunction-return=thunk-extern | FileCheck %s \
// RUN: --check-prefixes=CHECK,CHECK-EXTERN

int foo(void) {
// CHECK: @"_ZZ3foovENK3$_0clEv"({{.*}}) [[NOATTR:#[0-9]+]]
return []() {
return 42;
}();
}
int bar(void) {
// CHECK: @"_ZZ3barvENK3$_1clEv"({{.*}}) [[EXTERN:#[0-9]+]]
return []() __attribute__((function_return("thunk-extern"))) {
return 42;
}
();
}
int baz(void) {
// CHECK: @"_ZZ3bazvENK3$_2clEv"({{.*}}) [[KEEP:#[0-9]+]]
return []() __attribute__((function_return("keep"))) {
return 42;
}
();
}

class Foo {
public:
// CHECK: @_ZN3Foo3fooEv({{.*}}) [[EXTERN]]
__attribute__((function_return("thunk-extern"))) int foo() { return 42; }
};

int quux() {
Foo my_foo;
return my_foo.foo();
}

// CHECK: @extern_c() [[EXTERN]]
extern "C" __attribute__((function_return("thunk-extern"))) void extern_c() {}
extern "C" {
// CHECK: @extern_c2() [[EXTERN]]
__attribute__((function_return("thunk-extern"))) void extern_c2() {}
}

// CHECK-NOM-NOT: [[NOATTR]] = {{.*}}fn_ret_thunk_extern
// CHECK-KEEP-NOT: [[NOATTR]] = {{.*}}fn_ret_thunk_extern
// CHECK-KEEP-NOT: [[KEEP]] = {{.*}}fn_ret_thunk_extern
// CHECK-EXTERN: [[EXTERN]] = {{.*}}fn_ret_thunk_extern
22 changes: 22 additions & 0 deletions clang/test/Driver/mfunction-return.c
@@ -0,0 +1,22 @@
// RUN: %clang -mfunction-return= -### %s 2>&1 \
// RUN: | FileCheck --check-prefix=CHECK-VALID %s
// RUN: not %clang -mfunction-return -### %s 2>&1 \
// RUN: | FileCheck --check-prefix=CHECK-INVALID %s

// RUN: %clang -mfunction-return=keep -### %s 2>&1 \
// RUN: | FileCheck --check-prefix=CHECK-KEEP %s
// RUN: %clang -mfunction-return=thunk-extern -### %s 2>&1 \
// RUN: | FileCheck --check-prefix=CHECK-EXTERN %s

// RUN: %clang -mfunction-return=keep -mfunction-return=thunk-extern -### %s 2>&1 \
// RUN: | FileCheck --check-prefix=CHECK-EXTERN %s
// RUN: %clang -mfunction-return=thunk-extern -mfunction-return=keep -### %s 2>&1 \
// RUN: | FileCheck --check-prefix=CHECK-KEEP %s

// CHECK-VALID: "-mfunction-return="
// CHECK-INVALID: error: unknown argument: '-mfunction-return'

// CHECK-KEEP: "-mfunction-return=keep"
// CHECK-KEEP-NOT: "-mfunction-return=thunk-extern"
// CHECK-EXTERN: "-mfunction-return=thunk-extern"
// CHECK-EXTERN-NOT: "-mfunction-return=keep"
20 changes: 20 additions & 0 deletions clang/test/Frontend/mfunction-return.c
@@ -0,0 +1,20 @@
// RUN: %clang_cc1 -mfunction-return=keep -triple x86_64-linux-gnu %s
// RUN: %clang_cc1 -mfunction-return=thunk-extern -triple x86_64-linux-gnu %s

// RUN: not %clang_cc1 -mfunction-return=thunk -triple x86_64-linux-gnu %s 2>&1 \
// RUN: | FileCheck --check-prefix=CHECK-THUNK %s
// RUN: not %clang_cc1 -mfunction-return=thunk-inline -triple x86_64-linux-gnu %s 2>&1 \
// RUN: | FileCheck --check-prefix=CHECK-INLINE %s
// RUN: not %clang_cc1 -mfunction-return=invalid -triple x86_64-linux-gnu %s 2>&1 \
// RUN: | FileCheck --check-prefix=CHECK-INVALID %s
// RUN: not %clang_cc1 -mfunction-return=thunk-extern -triple s390x-linux-gnu %s 2>&1 \
// RUN: | FileCheck --check-prefix=CHECK-TARGET %s
// RUN: not %clang_cc1 -mfunction-return=thunk-extern -mcmodel=large \
// RUN: -triple x86_64-linux-gnu %s 2>&1 \
// RUN: | FileCheck --check-prefix=CHECK-LARGE %s

// CHECK-THUNK: error: invalid value 'thunk' in '-mfunction-return=thunk'
// CHECK-INLINE: error: invalid value 'thunk-inline' in '-mfunction-return=thunk-inline'
// CHECK-INVALID: error: invalid value 'invalid' in '-mfunction-return=invalid'
// CHECK-TARGET: error: invalid argument '-mfunction-return=' not allowed with 's390x-unknown-linux-gnu'
// CHECK-LARGE: error: invalid argument '-mfunction-return=thunk-extern' not allowed with '-mcmodel=large'
Expand Up @@ -69,6 +69,7 @@
// CHECK-NEXT: ExternalSourceSymbol ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_implementation, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable))
// CHECK-NEXT: FlagEnum (SubjectMatchRule_enum)
// CHECK-NEXT: Flatten (SubjectMatchRule_function)
// CHECK-NEXT: FunctionReturnThunks (SubjectMatchRule_function)
// CHECK-NEXT: GNUInline (SubjectMatchRule_function)
// CHECK-NEXT: HIPManaged (SubjectMatchRule_variable)
// CHECK-NEXT: Hot (SubjectMatchRule_function)
Expand Down

0 comments on commit 2240d72

Please sign in to comment.